fix(memory): use empty content: [] with structuredContent to avoid double-serialization (#2689) - #4583
Open
knoal wants to merge 1 commit into
Open
Conversation
…uble-serialization (modelcontextprotocol#2689) The memory server's tool handlers returned both: content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], structuredContent: { entities: result } This double-serializes the same data. The MCP SDK emits both fields in the response payload, producing: { "content": [{"text": "<JSON>"}], "structuredContent": {...} } Some clients (specifically Gemini CLI per the bug report) try to parse both halves as a single JSON document and fail with: MCP error -32603: Unexpected non-whitespace character after JSON at position 102 The fix: when structuredContent is set, content must be an empty array. Per the MCP spec, when a tool has an outputSchema, the structuredContent is the canonical output; text content is optional. This change updates all 6 affected tool handlers: - create_entities - create_relations - add_observations - delete_entities - delete_observations - delete_relations It also covers read_graph, search_nodes, search_graph (the 3 JSON.stringify variants). ## Tests - 52 tests pass (50 existing + 2 new).\n- Tests verify: 1. Every tool return with structuredContent has content: [] (not duplicated). 2. Hardcoded success messages appear only in structuredContent, not content. - Both tests FAIL on pre-fix (verified by git stash). ## Files - src/memory/index.ts: 9 tool return blocks updated to content: [] + structuredContent. - src/memory/__tests__/no-dual-content-structured.test.ts: 2 regression tests. Fixes modelcontextprotocol#2689. "
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
Fixes #2689: Memory server double-serializes its results.
The memory server's tool handlers returned both
content(text array) andstructuredContent(object) simultaneously:The MCP SDK serializes both fields into the response payload, producing:
Some clients (specifically Gemini CLI per the bug report) try to parse both halves as a single JSON document and fail with:
The fix: when
structuredContentis set,contentmust be an empty array. Per the MCP spec, when a tool has anoutputSchema, thestructuredContentis the canonical output; text content is optional.This change updates all 6 affected tool handlers:
It also covers read_graph, search_nodes, search_graph (the 3
JSON.stringifyvariants).Tests
structuredContenthascontent: [](not duplicated).structuredContent, notcontent.git stash).Files
src/memory/index.ts: 9 tool return blocks updated tocontent: [] + structuredContent.src/memory/__tests__/no-dual-content-structured.test.ts: 2 regression tests.Notes
The issue title mentions Gemini CLI specifically, but the same pattern would affect any client that:
The fix is the safest of the three options I considered:
content: []— satisfies the SDK type, eliminates double-serialization.contententirely — fails TypeScript strict-typing (the SDK requires it).content: [...]with a JSON-parseable string — text clients see the string, structured clients see the object. Same risk as the original code.Fixes #2689.
— knoal (via the operator, Alex Knotts)