fix(iot): guard CouchDB construction and add first-run DX scaffolding#322
fix(iot): guard CouchDB construction and add first-run DX scaffolding#322iksnerd wants to merge 1 commit into
Conversation
End-to-end verification with CouchDB runningAfter opening this PR I noticed the test plan only covered the cold-start (no env) path. Re-ran with
The iot connected path is unchanged by the guard — the existing Side observation while testingThe bundled |
8308f92 to
a6c7f1e
Compare
The iot MCP server crashed on startup when COUCHDB_URL was unset: `couchdb3.Database(None, url=None, ...)` raised inside __init__, and the partially-constructed object's __del__ then double-faulted with `AttributeError: 'Database' object has no attribute 'session'`. Guard the construction so the server stays startable for tool discovery even when CouchDB env vars are missing — tool calls that need the DB return a clean error instead. Also bundle three small first-run DX improvements that surfaced while investigating: - Add `VIBRATION_DBNAME=vibration` to `.env.public` (already documented in `docs/mcp-servers.md` but missing from the example env file). - Ship `.mcp.json.example` at repo root so Claude Code / MCP Inspector users have a copy-paste config for the six stdio servers. Gitignore `.mcp.json` so user-local copies stay out of git. - Document an "Inspecting a server directly" recipe in `docs/mcp-servers.md` (Inspector UI, `.mcp.json` template, raw stdio JSON-RPC) and note that `wo` returns `*_not_available` until the CouchDB container's seeding step has run. Signed-off-by: iksnerd <bdrensk@me.com>
a6c7f1e to
34e25aa
Compare
Description
The
iotMCP server crashed on startup whenCOUCHDB_URLwas unset:couchdb3.Database(None, url=None, ...)raised inside__init__, leaving a partially-constructed instance whose__del__then double-faulted withAttributeError: 'Database' object has no attribute 'session'.This PR guards the construction so the server stays startable for tool discovery even when CouchDB env vars are missing — tool calls that need the DB now return a clean error instead. It also bundles three small first-run DX improvements that surfaced while investigating.
Fix Details
src/servers/iot/main.py— initializedb = Nonebefore the construction attempt and guard onCOUCHDB_URL and COUCHDB_DBNAMEbeing set. Skipping construction avoids the partially-constructed-Database trap entirely; aWARNINGis logged so the failure mode is visible. Also removed an unusedfrom functools import lru_cacheflagged by ruff..env.public— addedVIBRATION_DBNAME=vibration. Documented indocs/mcp-servers.md:93but missing from the example env file, causing thevibrationserver to start without its DB name..mcp.json.example— new file at repo root with stdio entries for the six*-mcp-serverconsole scripts. Any MCP client (e.g. MCP Inspector) cancp .mcp.json.example .mcp.jsonand have all servers wired up..mcp.jsonis gitignored so user-local copies stay out of git.docs/mcp-servers.md— added an "Inspecting a server directly" section covering the MCP Inspector UI, the.mcp.json.exampletemplate, and a raw stdio JSON-RPC handshake recipe. Also extended thewo"Data init" line to note that an*_not_availableerror means the CouchDB container's seeding step didn't run.Impact on Benchmarking
WARNINGinstead of anERRORfollowed by anAttributeErrortraceback.Related Issues
Verification Steps
uv run pytest src/ -k "not integration"→311 passed, 3 skipped, 50 deselected.ERROR:iot-mcp-server:Failed to connect to CouchDB: 'NoneType' object has no attribute 'startswith'plusAttributeError: 'Database' object has no attribute 'session'on shutdown.WARNING:iot-mcp-server:CouchDB env vars not set (COUCHDB_URL, IOT_DBNAME); tool calls that need CouchDB will return an error.No traceback.tools/listreturns the same four tools.uvx ruff format --check src/servers/iot/main.py→ clean;uvx ruff check src/servers/iot/main.py→All checks passed!Pre-existing test failures observed (not caused by this PR)
While running the full unit suite,
src/observability/tests/test_file_exporter.py::test_file_exporter_writes_jsonland::test_file_exporter_appendsfail withModuleNotFoundError: No module named 'google.protobuf'. The base install does not include protobuf — it's only pulled in viauv sync --group otel. These tests likely want askipifguard (similar to the integration suites in CouchDB / WATSONX). Happy to file a separate issue if useful.Checklist
test_db_disconnectedcases insrc/servers/iot/tests/test_tools.pyexercise thedb is Nonepath; the new guard makes that path the only outcome when env vars are unset rather than a side-effect of an exception.)