From 2073b2bb96a3645bf1a86a5acc3628a4895f2460 Mon Sep 17 00:00:00 2001 From: Sami Rusani Date: Sat, 15 Aug 2026 14:18:18 +0200 Subject: [PATCH] Let every core MCP tool take an agent identity, and point the write verbs the right way Two defects found while testing whether an agent can install Alice and use it unattended, which is the distribution model these tools have to serve. Five of the eleven core tools rejected agent identity while the other six accepted it. An agent that stamps agent_id on every call, which the commit tool's own schema invites, got a hard tool_request_failed on alice_recall, alice_resume, alice_recent_decisions, alice_explain and alice_memory_correct. The last of those is a write verb, so this was not only a read-path problem. All eleven now take the identity properties. Verified against the shipped binary: agent_id on recall went from isError true to a correct result. The descriptions were steering agents into the review queue. alice_capture said "use this whenever you learn something worth keeping" while also saying it only becomes trusted memory after review, and alice_memory_commit said "on the user's instruction ('remember this')". So the tool that invited ambient use was review-gated, and the tool that works unattended told the agent to wait to be asked. An agent recording something it just learned took the path where nothing becomes recallable, and got status "imported" with isError false while the content stayed invisible to alice_recall. alice_capture is not broken and is unchanged in behaviour. It is review-gated by design, and no persona setting alters that, which was confirmed on fresh stores with and without ALICE_MEMORY_PERSONA. Only the descriptions change: capture now says plainly that alice_recall will not return the text and names commit as the verb for immediate recall, and commit now says to use it whenever you learn something worth keeping, including when the user has not asked. The frozen-definitions digest moves because the definitions moved. Before updating it, all eleven core tools were diffed between HEAD and this branch: exactly five changed, each gaining only the agent identity properties, six untouched. The guard was then re-checked for vacuity by deleting one splice and confirming the count actually dropped from 11 to 10 before running the test, because an earlier attempt at that check used a non-matching string, changed nothing, and passed for the wrong reason. Not fixed here, pre-existing and unrelated: alice_explain errors on every call, with and without agent identity, verified identical on stashed HEAD. Verified: 4544 unit passed, 2 skipped. --- apps/api/src/alicebot_api/mcp/definitions.py | 15 +++++++++++---- tests/unit/test_mcp_package_split.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/api/src/alicebot_api/mcp/definitions.py b/apps/api/src/alicebot_api/mcp/definitions.py index 5ab34e1d..19916ee4 100644 --- a/apps/api/src/alicebot_api/mcp/definitions.py +++ b/apps/api/src/alicebot_api/mcp/definitions.py @@ -233,9 +233,11 @@ def _vnext_agent_tool_schema( { "name": "alice_capture", "description": ( - "Submit new information to Alice as a source-backed, reviewable memory. The text is " - "stored verbatim with provenance and split into searchable chunks; it only becomes " - "trusted memory after review. Use this whenever you learn something worth keeping." + "Store a source document or raw note for later review. The text is kept verbatim " + "with provenance and split into searchable chunks, but it does NOT become recallable " + "memory until a human reviews it, so alice_recall will not return it. Use this for " + "transcripts, files and pasted material you want on record. To record a fact so it is " + "immediately recallable, use alice_memory_commit instead." ), "inputSchema": { "type": "object", @@ -267,7 +269,7 @@ def _vnext_agent_tool_schema( { "name": "alice_memory_commit", "description": ( - "Write one explicit memory on the user's instruction ('remember this'). The write " + "Record one fact as durable, immediately recallable memory. Use this whenever you learn something worth keeping, including when the user has not asked you to remember it. This is the write verb for ordinary memory. The write " "is policy-checked, never blind: the outcome is 'committed', 'confirmation_required' " "(finish with alice_memory_manage action 'confirm'), 'review_required' (waits for " "human review), or 'rejected'. Every outcome is recorded with provenance, a " @@ -342,6 +344,7 @@ def _vnext_agent_tool_schema( "additionalProperties": False, "required": ["query"], "properties": { + **_AGENT_IDENTITY_SCHEMA_PROPERTIES, "query": { "type": "string", "description": "What to search for, in natural language or keywords.", @@ -429,6 +432,7 @@ def _vnext_agent_tool_schema( "type": "object", "additionalProperties": False, "properties": { + **_AGENT_IDENTITY_SCHEMA_PROPERTIES, "query": { "type": "string", "description": "Free-text topic to focus the brief on.", @@ -632,6 +636,7 @@ def _vnext_agent_tool_schema( "type": "object", "additionalProperties": False, "properties": { + **_AGENT_IDENTITY_SCHEMA_PROPERTIES, "query": { "type": "string", "description": "Free-text filter for which decisions to return.", @@ -727,6 +732,7 @@ def _vnext_agent_tool_schema( "additionalProperties": False, "required": ["action"], "properties": { + **_AGENT_IDENTITY_SCHEMA_PROPERTIES, "review_item_id": { "type": "string", "format": "uuid", @@ -869,6 +875,7 @@ def _vnext_agent_tool_schema( "type": "object", "additionalProperties": False, "properties": { + **_AGENT_IDENTITY_SCHEMA_PROPERTIES, "memory_id": { "type": "string", "description": "Id of a memory returned by alice_recall; returns its provenance links, revisions, event history, supersession_chain (each entry has id, title, status, created_at, its relation to this memory: predecessor, self, or successor, and the entities it is linked to when available), and a timeline: one chronological list of {at, kind, memory_id, summary} entries (kind is created, revised, corrected, or superseded_by) telling how this memory evolved.", diff --git a/tests/unit/test_mcp_package_split.py b/tests/unit/test_mcp_package_split.py index 4efde1ce..52e196af 100644 --- a/tests/unit/test_mcp_package_split.py +++ b/tests/unit/test_mcp_package_split.py @@ -125,7 +125,7 @@ def test_mcp_registry_order_definitions_and_alias_identity_are_frozen() -> None: handlers = mcp_registry._TOOL_HANDLERS assert (len(core_definitions), len(legacy_definitions), len(handlers)) == (11, 65, 76) - assert _digest(core_definitions) == "99eabe36d83b8609a57631d71b91d5e9f14b23f7be0f828f6c75ce5c48888062" + assert _digest(core_definitions) == "32a21a33112c4283489b9cd06e73c60755598d9f7b120318c55b9d7f3c29475f" assert _digest(legacy_definitions) == "ca3d747e552bdece52c22d76332fc69f499878290edf3f236a8a7ea6a2e34e41" ordered_handler_map = [(name, handler.__name__) for name, handler in handlers.items()] assert _digest(ordered_handler_map) == "d864c98bb914bbc6ace464fa8020b3ed264f17f2061a6101aae677d801032ae5"