Summary
Two related bugs affect the MCP query path:
-
Guard gap: handle_search_graph, handle_get_graph_schema, handle_trace_call_path, handle_get_code_snippet, and handle_query_graph did not check whether the requested project was actually indexed before querying. They silently returned empty results, misleading callers into thinking the project had no symbols rather than that it had not been indexed.
-
Ghost .db file creation: resolve_store called cbm_store_open → store_open_internal, which always passes SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE to sqlite3_open_v2. This means any query for an unknown or misspelled project name creates an empty .db file in ~/.cache/codebase-memory-mcp/, which then appears in list_projects output — polluting the project registry.
Root Cause
store_open_internal in src/store/store.c unconditionally includes SQLITE_OPEN_CREATE:
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX;
sqlite3_open_v2(db_path, &s->db, flags, NULL);
This is correct for the indexing path (pipeline.c, pipeline_incremental.c), which must create new stores. But resolve_store in mcp.c is on the query path — it should fail gracefully when the .db file doesn't exist, not silently create it.
Affected Handlers
handle_search_graph
handle_get_graph_schema
handle_trace_call_path
handle_get_code_snippet
handle_query_graph
(handle_get_architecture already had an inline guard, but was also updated to use the shared helper.)
Ghost File Pollution
Any typo'd or unindexed project name passed to a query tool creates a zero-byte-data .db file. The file persists in the cache directory and appears in list_projects. Users can end up with a polluted cache of dozens of empty stubs.
Note: The fix prevents new ghost files from being created. Existing ghost files in user caches are not removed automatically.
Proposed Fix
Two changes:
1. cbm_store_open_path_query() — query-safe store opener
Add a new function to src/store/store.c that opens with SQLITE_OPEN_READWRITE only (no SQLITE_OPEN_CREATE). When the .db file is absent, SQLite returns SQLITE_CANTOPEN and the function returns NULL:
cbm_store_t *cbm_store_open_path_query(const char *db_path) {
// opens with SQLITE_OPEN_READWRITE only
// returns NULL on SQLITE_CANTOPEN
}
Declare it in src/store/store.h.
2. verify_project_indexed() helper + guard in all query handlers
Add a static helper in src/mcp/mcp.c that verifies the project has at least one node in the store (differentiating an indexed-but-empty project from a ghost file):
static char *verify_project_indexed(cbm_store_t *store, const char *project);
Call this helper after REQUIRE_STORE in every query handler.
resolve_store is updated to call cbm_store_open_path_query instead of cbm_store_open_path.
Note on Orthogonality to #115
Issue #115 (closed 2026-03-22) described a CLI connection lifecycle bug where the MCP server would not properly handle the client's connection lifecycle. This issue is entirely orthogonal to #115. The guard gap and ghost-file creation affect the MCP server's internal query dispatch — not the CLI connection or stdio transport layer. There is no overlap between the two bugs or their fixes.
Smoke Test
A smoke test has been added at tests/smoke_guard.sh that:
- Builds the project.
- Invokes
search_graph with project="nonexistent_smoke_test_xyz".
- Asserts the response contains a guard error (
"no project loaded" or "not indexed").
- Asserts no
.db file is created at ~/.cache/codebase-memory-mcp/nonexistent_smoke_test_xyz.db.
Summary
Two related bugs affect the MCP query path:
Guard gap:
handle_search_graph,handle_get_graph_schema,handle_trace_call_path,handle_get_code_snippet, andhandle_query_graphdid not check whether the requested project was actually indexed before querying. They silently returned empty results, misleading callers into thinking the project had no symbols rather than that it had not been indexed.Ghost
.dbfile creation:resolve_storecalledcbm_store_open→store_open_internal, which always passesSQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATEtosqlite3_open_v2. This means any query for an unknown or misspelled project name creates an empty.dbfile in~/.cache/codebase-memory-mcp/, which then appears inlist_projectsoutput — polluting the project registry.Root Cause
store_open_internalinsrc/store/store.cunconditionally includesSQLITE_OPEN_CREATE:This is correct for the indexing path (
pipeline.c,pipeline_incremental.c), which must create new stores. Butresolve_storeinmcp.cis on the query path — it should fail gracefully when the.dbfile doesn't exist, not silently create it.Affected Handlers
handle_search_graphhandle_get_graph_schemahandle_trace_call_pathhandle_get_code_snippethandle_query_graph(
handle_get_architecturealready had an inline guard, but was also updated to use the shared helper.)Ghost File Pollution
Any typo'd or unindexed project name passed to a query tool creates a zero-byte-data
.dbfile. The file persists in the cache directory and appears inlist_projects. Users can end up with a polluted cache of dozens of empty stubs.Note: The fix prevents new ghost files from being created. Existing ghost files in user caches are not removed automatically.
Proposed Fix
Two changes:
1.
cbm_store_open_path_query()— query-safe store openerAdd a new function to
src/store/store.cthat opens withSQLITE_OPEN_READWRITEonly (noSQLITE_OPEN_CREATE). When the.dbfile is absent, SQLite returnsSQLITE_CANTOPENand the function returnsNULL:Declare it in
src/store/store.h.2.
verify_project_indexed()helper + guard in all query handlersAdd a static helper in
src/mcp/mcp.cthat verifies the project has at least one node in the store (differentiating an indexed-but-empty project from a ghost file):Call this helper after
REQUIRE_STOREin every query handler.resolve_storeis updated to callcbm_store_open_path_queryinstead ofcbm_store_open_path.Note on Orthogonality to #115
Issue #115 (closed 2026-03-22) described a CLI connection lifecycle bug where the MCP server would not properly handle the client's connection lifecycle. This issue is entirely orthogonal to #115. The guard gap and ghost-file creation affect the MCP server's internal query dispatch — not the CLI connection or stdio transport layer. There is no overlap between the two bugs or their fixes.
Smoke Test
A smoke test has been added at
tests/smoke_guard.shthat:search_graphwithproject="nonexistent_smoke_test_xyz"."no project loaded"or"not indexed")..dbfile is created at~/.cache/codebase-memory-mcp/nonexistent_smoke_test_xyz.db.