Summary
The per-session workspace project index is built once and never refreshed on lookup miss. A cloud project created out-of-band (via CLI, another client, or — the worst case — a teammate in a shared team workspace) is invisible to every already-running MCP session:
list_memory_projects omits it
project_id routing hard-errors: Project '<uuid>' was not found in indexed cloud workspaces — asserting nonexistence of a project that demonstrably exists
read_note's fuzzy-fallback breaks even when the caller's project name resolves fine, because the fallback internally re-resolves via project_id
Reproduction (one session, minutes apart)
- MCP session running (local stdio, OAuth). Create a project out-of-band:
bm project add manual --cloud --workspace <team-slug> --visibility shared
- In the running session:
search_notes(project="manual", query="x") → works (name resolves via local config per-project routing)
search_notes(project_id="0e6a327b-...", query="x") → error: Project '0e6a327b-...' was not found in indexed cloud workspaces. Available projects: <stale list omitting manual>
read_note("fuzzy text", project="manual") → error (same), because the fallback at mcp/tools/read_note.py:319 passes project_id=active_project.external_id to search_notes, and project_id takes precedence — so the already-successfully-resolved project gets re-resolved through the stale index and dies
list_memory_projects() → stale list, no manual
The same asymmetry means a teammate adding a project to a shared workspace is invisible (and unroutable by project_id) to everyone's long-running sessions — e.g. Claude Desktop sessions that stay up for days.
Root cause
src/basic_memory/mcp/project_context.py:
- The index is cached in FastMCP context state under
_WORKSPACE_PROJECT_INDEX_STATE_KEY (~line 728/787) with no TTL
resolve_workspace_project_identifier() (~line 801) looks up the cached index and on miss raises ValueError (~line 881) with no attempt to rebuild
invalidate_workspace_project_index() exists but is only called after in-session create_memory_project — out-of-band creation has no invalidation path
Expected
Cache-miss refresh: when an external_id / qualified-name lookup misses the cached index, rebuild the index once and retry before raising. (Optionally also a short TTL, but miss-refresh alone fixes the correctness issue — a miss is exactly the signal that the cache may be stale.)
Also worth fixing alongside: read_note's fallback should not re-resolve a project it already holds — it has active_project in hand and could pass routing through directly.
Severity
High for Teams: shared-workspace project creation is a core flow, and the failure mode is a confident wrong answer ("project does not exist") rather than a miss.
Context
Found during live verification for #952. Related: #954 (create_memory_project workspace routing) — same discovery session.
Summary
The per-session workspace project index is built once and never refreshed on lookup miss. A cloud project created out-of-band (via CLI, another client, or — the worst case — a teammate in a shared team workspace) is invisible to every already-running MCP session:
list_memory_projectsomits itproject_idrouting hard-errors:Project '<uuid>' was not found in indexed cloud workspaces— asserting nonexistence of a project that demonstrably existsread_note's fuzzy-fallback breaks even when the caller's project name resolves fine, because the fallback internally re-resolves viaproject_idReproduction (one session, minutes apart)
bm project add manual --cloud --workspace <team-slug> --visibility sharedsearch_notes(project="manual", query="x")→ works (name resolves via local config per-project routing)search_notes(project_id="0e6a327b-...", query="x")→ error:Project '0e6a327b-...' was not found in indexed cloud workspaces. Available projects: <stale list omitting manual>read_note("fuzzy text", project="manual")→ error (same), because the fallback atmcp/tools/read_note.py:319passesproject_id=active_project.external_idtosearch_notes, andproject_idtakes precedence — so the already-successfully-resolved project gets re-resolved through the stale index and dieslist_memory_projects()→ stale list, nomanualThe same asymmetry means a teammate adding a project to a shared workspace is invisible (and unroutable by
project_id) to everyone's long-running sessions — e.g. Claude Desktop sessions that stay up for days.Root cause
src/basic_memory/mcp/project_context.py:_WORKSPACE_PROJECT_INDEX_STATE_KEY(~line 728/787) with no TTLresolve_workspace_project_identifier()(~line 801) looks up the cached index and on miss raisesValueError(~line 881) with no attempt to rebuildinvalidate_workspace_project_index()exists but is only called after in-sessioncreate_memory_project— out-of-band creation has no invalidation pathExpected
Cache-miss refresh: when an external_id / qualified-name lookup misses the cached index, rebuild the index once and retry before raising. (Optionally also a short TTL, but miss-refresh alone fixes the correctness issue — a miss is exactly the signal that the cache may be stale.)
Also worth fixing alongside:
read_note's fallback should not re-resolve a project it already holds — it hasactive_projectin hand and could pass routing through directly.Severity
High for Teams: shared-workspace project creation is a core flow, and the failure mode is a confident wrong answer ("project does not exist") rather than a miss.
Context
Found during live verification for #952. Related: #954 (create_memory_project workspace routing) — same discovery session.