Sub-issue of #16654. Discovered while fixing #16701 (the same disclosure via the REST route).
Problem
`mcp/autobot_server.py`'s stdio/JSON-RPC MCP server (`AutoBotMCPServer`) authenticates callers entirely by scope token (`"kb"`/`"memory"`/`"agents"` prefixes, via `_check_scope`) -- there is no per-user identity anywhere in its request handling. `_authenticate`/`_validate_token`/`_validate_redis_token` all resolve to a list of granted scopes, never a user id.
The `memory.verbatim_search` tool (`_memory_verbatim_search`, line ~833) calls `VerbatimStore.search(args.query, session_filter=args.session_filter)` with no user scoping at all. Any caller holding a `"memory"`-scoped token -- a broad, service-level grant shared with every other memory tool -- gets every user's verbatim conversation chunks matching the query.
This is the same shape as `_kb_search`/`_kb_get_document` in the same file, already tracked as `TRACKED_GAP #16666` in `repo_tests/kb_read_visibility_allowlist.py`. Memory/verbatim has no equivalent allowlist mechanism to track it in, hence this issue.
#16701 makes `VerbatimStore.search`'s `user_id` a required argument (no longer optional/omittable) specifically so this gap can't spread silently -- the MCP call site now must pass an explicit `UNSCOPED_ALL_USERS` sentinel and say why, rather than a caller simply forgetting to pass `user_id`. That sentinel is what still needs closing here.
Acceptance criteria
Refs #16654, #16666 (same shape, different subsystem).
Sub-issue of #16654. Discovered while fixing #16701 (the same disclosure via the REST route).
Problem
`mcp/autobot_server.py`'s stdio/JSON-RPC MCP server (`AutoBotMCPServer`) authenticates callers entirely by scope token (`"kb"`/`"memory"`/`"agents"` prefixes, via `_check_scope`) -- there is no per-user identity anywhere in its request handling. `_authenticate`/`_validate_token`/`_validate_redis_token` all resolve to a list of granted scopes, never a user id.
The `memory.verbatim_search` tool (`_memory_verbatim_search`, line ~833) calls `VerbatimStore.search(args.query, session_filter=args.session_filter)` with no user scoping at all. Any caller holding a `"memory"`-scoped token -- a broad, service-level grant shared with every other memory tool -- gets every user's verbatim conversation chunks matching the query.
This is the same shape as `_kb_search`/`_kb_get_document` in the same file, already tracked as `TRACKED_GAP #16666` in `repo_tests/kb_read_visibility_allowlist.py`. Memory/verbatim has no equivalent allowlist mechanism to track it in, hence this issue.
#16701 makes `VerbatimStore.search`'s `user_id` a required argument (no longer optional/omittable) specifically so this gap can't spread silently -- the MCP call site now must pass an explicit `UNSCOPED_ALL_USERS` sentinel and say why, rather than a caller simply forgetting to pass `user_id`. That sentinel is what still needs closing here.
Acceptance criteria
Refs #16654, #16666 (same shape, different subsystem).