Summary
Three MCP tools return a FastMCP runtime error to the client even though the underlying memory operation succeeds and persists correctly:
mcp__cortex__remember
mcp__cortex__recall
mcp__cortex__get_telemetry
Error message:
structured_content must be a dict or None. Got str: '{...}'. Tools should wrap non-dict values based on their output_schema.
The data inside the error payload is the correct JSON; it's just being returned as a stringified JSON instead of a dict, so FastMCP rejects it before delivery.
Reproducer
From a strict MCP client (e.g. Claude Code's tool runtime), call:
remember({content: \"any non-trivial content\", tags: [\"test\"]})
The DB write happens (verified by memory_stats count incrementing and direct psql on the memories table), but the client sees the schema-wrap error.
The same shape of error occurs on recall and get_telemetry. Tools that work cleanly include memory_stats, forget, query_methodology, list_domains, wiki_list, wiki_read, detect_gaps, seed_project, consolidate, rebuild_profiles, backfill_memories.
Likely cause
The handlers' return values are being JSON-encoded to a string before FastMCP sees them. FastMCP expects a dict (or None) when an output_schema is declared. Either:
- The handlers wrap the dict in
json.dumps() somewhere in the response path, or
- The
output_schema decorator is missing on these specific handlers and FastMCP's auto-wrap can't infer the structure.
Comparing the affected vs. clean handlers should localize quickly — the cleanly-working handlers presumably return dicts directly without the JSON string round-trip.
Impact
- Every call from a strict MCP client surfaces as an exception even when the op succeeded — confusing UX, and unsafe for any caller that bails on tool exceptions.
- Workaround: ignore the error and verify via
memory_stats / direct recall from a different code path. Not a production-acceptable workaround.
Environment
- Cortex 3.14.5
- Python 3.12 inside Docker container (
cortex-mcp:latest built from upstream source via local Dockerfile)
- FastMCP version: whatever is pinned in
pyproject.toml (fastmcp>=2.0.0)
- Client: Claude Code MCP runtime
Summary
Three MCP tools return a FastMCP runtime error to the client even though the underlying memory operation succeeds and persists correctly:
mcp__cortex__remembermcp__cortex__recallmcp__cortex__get_telemetryError message:
The data inside the error payload is the correct JSON; it's just being returned as a stringified JSON instead of a dict, so FastMCP rejects it before delivery.
Reproducer
From a strict MCP client (e.g. Claude Code's tool runtime), call:
The DB write happens (verified by
memory_statscount incrementing and directpsqlon thememoriestable), but the client sees the schema-wrap error.The same shape of error occurs on
recallandget_telemetry. Tools that work cleanly includememory_stats,forget,query_methodology,list_domains,wiki_list,wiki_read,detect_gaps,seed_project,consolidate,rebuild_profiles,backfill_memories.Likely cause
The handlers' return values are being JSON-encoded to a string before FastMCP sees them. FastMCP expects a dict (or None) when an
output_schemais declared. Either:json.dumps()somewhere in the response path, oroutput_schemadecorator is missing on these specific handlers and FastMCP's auto-wrap can't infer the structure.Comparing the affected vs. clean handlers should localize quickly — the cleanly-working handlers presumably return dicts directly without the JSON string round-trip.
Impact
memory_stats/ directrecallfrom a different code path. Not a production-acceptable workaround.Environment
cortex-mcp:latestbuilt from upstream source via local Dockerfile)pyproject.toml(fastmcp>=2.0.0)