fix: resolve working memory key via search index when user_id/namespace omitted#239
fix: resolve working memory key via search index when user_id/namespace omitted#239tylerhutcherson merged 3 commits intomainfrom
Conversation
🛡️ Jit Security Scan Results✅ No security findings were detected in this PR
Security scan by Jit
|
There was a problem hiding this comment.
Pull request overview
Fixes issue #235 where GET/DELETE /v1/working-memory/{session_id} could 404 after a successful scoped PUT because user_id/namespace were provided in the PUT body but omitted from GET/DELETE query params. The PR adds an index-based fallback to resolve the correct Redis key by session_id.
Changes:
- Add
_resolve_working_memory_key_via_index()and wire it intoget_working_memory()anddelete_working_memory()as a fallback when the direct key lookup misses. - Return stored
namespace/user_idfrom the persisted JSON document (instead of echoing caller-supplied params). - Extend the MCP
get_working_memorytool to accept optionaluser_id/namespace, and add regression tests for the bug and related scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
agent_memory_server/working_memory.py |
Adds index-based key resolution fallback for GET/DELETE when scoping params are omitted. |
agent_memory_server/mcp.py |
Adds optional user_id and namespace parameters to the MCP get_working_memory tool. |
tests/test_issue_235.py |
Introduces a dedicated regression test suite for key resolution behavior and API round-trips. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ce omitted (#235) PUT /v1/working-memory/{session_id} stores user_id and namespace from the request body into the Redis key, but GET/DELETE construct the key from query params. When callers omit those params, a different key is produced and the session appears missing (404). Add an index-based fallback: when a direct key lookup returns nothing, query the working memory search index by session_id to resolve the actual Redis key. This preserves multi-tenant isolation (namespace + user_id remain in keys) while making GET/DELETE work without requiring callers to repeat scoping parameters. Changes: - Add _resolve_working_memory_key_via_index() helper in working_memory.py - Use fallback in get_working_memory() and delete_working_memory() - Return stored namespace/user_id from JSON data instead of caller params - Add user_id/namespace params to MCP get_working_memory tool - Add comprehensive tests covering the fix and multi-tenant isolation
Co-authored-by: Vishal Bala <vishal-bala@users.noreply.github.com>
| f"(original key: {key})" | ||
| ) | ||
| key = resolved_key | ||
| working_memory_data = await redis_client.json().get(key) |
There was a problem hiding this comment.
Index fallback enables cross-tenant session access without scoping
Medium Severity
The index fallback in both get_working_memory and delete_working_memory allows any caller who knows a session_id to retrieve or delete another tenant's session without providing the correct namespace/user_id. When only one session matches a given session_id across all tenants, the ambiguity guard doesn't trigger, and the session is returned or deleted. This contradicts the PR's claim that multi-tenant isolation is preserved.
Additional Locations (1)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| async def get_working_memory( | ||
| session_id: str, | ||
| user_id: str | None = None, | ||
| namespace: str | None = None, |
There was a problem hiding this comment.
MCP get defaults inconsistent with set defaults
Low Severity
The MCP set_working_memory tool defaults user_id and namespace to settings.default_mcp_user_id and settings.default_mcp_namespace, but the newly added params on get_working_memory default to None. When those settings are configured to non-None values, every MCP GET will build a mismatched key, miss the direct lookup, and always fall through to the slower index-based fallback. Using settings.default_mcp_user_id / settings.default_mcp_namespace as defaults here would keep the tools consistent and avoid unnecessary fallback lookups.


Closes #235
Summary
PUT /v1/working-memory/{session_id}acceptsuser_idandnamespacein the request body, embedding them into the Redis key (e.g.working_memory:demo:alice:session-1)GETandDELETEtake those values as query params, which default toNonewhen omitted, producing a different key (working_memory:session-1) — resulting in a 404session_idto resolve the actual Redis keyChanges
working_memory.py: Add_resolve_working_memory_key_via_index()helper; wire fallback intoget_working_memory()anddelete_working_memory(); return storednamespace/user_idfrom the JSON document instead of caller-supplied paramsmcp.py: Add missinguser_idandnamespaceparameters to the MCPget_working_memorytooltests/test_issue_235.py: 9 tests covering key resolution, API round-trips, delete without params, multi-tenant isolation, and non-existent session handlingTest plan
test_get_resolves_via_index_when_params_omitted— PUT with scoping params, GET without → 200test_get_with_partial_params_resolves— GET with namespace only still resolvestest_get_with_correct_params_still_uses_fast_path— direct lookup still works when params matchtest_delete_resolves_via_index_when_params_omitted— DELETE without params removes the correct sessiontest_api_put_body_params_get_without_query_params— full HTTP round-triptest_api_delete_without_query_params— full HTTP DELETE round-triptest_multi_tenant_isolation_preserved— same session_id in different namespaces stays separatetest_nonexistent_session_still_returns_none— no false positivesNote
Medium Risk
Moderate risk because it changes working-memory GET/DELETE lookup behavior and introduces index-based key resolution that could affect multi-tenant correctness if the search index is stale or filtering is insufficient (though ambiguity is explicitly rejected).
Overview
Fixes working-memory retrieval/deletion when a session was stored with scoped Redis keys (
namespace/user_id) but later fetched/deleted without those parameters by adding an index-based fallback that searches the working-memory Redis Search index bysession_idto resolve the actual key.The fallback refuses to return data when the
session_idis ambiguous across tenants (warns and returnsNone), andget_working_memorynow returns storednamespace/user_idfrom the JSON document when the caller omitted them. The MCPget_working_memorytool is updated to accept/passuser_idandnamespace, and a newtests/test_issue_235.pycovers GET/DELETE/API round-trips plus multi-tenant disambiguation behavior.Written by Cursor Bugbot for commit faea9d3. This will update automatically on new commits. Configure here.