fix(mcp): remember tool output validation — schema vs handler mismatches#45
Merged
Conversation
The remember tool failed MCP output validation on every code path because
the declared schema did not match what the handler actually returns:
(1) memory_id / merged_with declared "type": "string" (UUID-style) but the
handler returns int (PG bigint). Aligned with anchor.py / forget.py /
get_causal_chain.py which already declare integer.
(2) Schema invariant required:["stored","action"] was violated by both
no_content early-exit paths in remember.py and by
write_gate.build_rejection_response(): they returned {"stored": false,
...} with no "action" field. Added "action": "rejected" to each.
(3) Schema enum was ["stored","merged","rejected"] (past-tense outcomes)
but the handler forwarded the internal curation vocab "create"/"link"
untouched, so every successful create/link failed with:
Output validation error: 'create' is not one of [...]
Normalized at the response boundary in build_response:
create/link → "stored", merge → "merged".
After these fixes the schema invariant and the enum constraint are both
honoured on every code path (no_content, gate-rejected, created, linked,
merged).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
cf1bf1e to
7514413
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
rememberMCP tool failed output validation on every code path due to schema/handler mismatches. Three independent fixes in one commit:memory_id/merged_withtype — schema declaredstring(UUID-style) but the handler returnsint(PG bigint). Nowinteger, aligning withanchor.py/forget.py/get_causal_chain.pywhich already declare integer.action— schema invariantrequired: ["stored","action"]was violated by bothno_contentearly-exits inremember.pyand bywrite_gate.build_rejection_response(). Added"action": "rejected"to each.["stored","merged","rejected"]but the handler forwarded internal curation vocab"create"/"link"untouched, so every successful create/link failed with'create' is not one of [...]. Normalized at the response boundary inbuild_response:create/link→stored,merge→merged.Repro (before)
After this PR, the schema invariant
required:["stored","action"]and the enum constraint are honoured on every code path (no_content, gate-rejected, created, linked, merged).Test plan
remember({content: "test memory"})— expect{stored: true, memory_id: <int>, action: "stored", ...}remember({content: ""})— expect{stored: false, action: "rejected", reason: "no_content"}remember({content: "<duplicate of existing>"})— expect{stored: false, action: "rejected", reason: "<gate reason>", ...}from write gateremember({content: "<very similar to existing>", force: false})— expect{stored: true, action: "merged", merged_with: <int>, ...}Output validation error)Files
mcp_server/handlers/remember.py— schema types + early-exitactionmcp_server/handlers/remember_response.py—build_responsenormalizationmcp_server/core/write_gate.py—build_rejection_responseaction field🤖 Generated with Claude Code