[BUG] MCP dict/object tool arguments serialized as JSON strings (regression of #3084) — 422 on object-typed params in 2.1.214

Status Closed — not planned
Reported on v2.1.214
Maintainer reply None cached
Activity 3 comments · opened Jul 23, 2026 · closed Aug 24, 2026

Summary

Claude Code serializes a dict/object-typed MCP tool argument as a JSON string before sending it to the server, so servers that type the parameter as an object (anyOf: [object, null]) reject it with 422 Input should be a valid dictionary.

This is the same defect as #3084 ("FastMCP Pydantic model parameters serialized as JSON strings"), which was closed as COMPLETED on 2025-09-08, and #2747 (closed as a duplicate of #3084). Both are locked, and the tracker's auto-message asks to file a new issue referencing them if it recurs. It recurs on a current build.

Environment

  • Claude Code 2.1.214 (2026-07-23)
  • Platform: macOS
  • MCP server: a FastMCP 3.4.2 HTTP server (streamable-http), tool add_memory, whose metadata parameter is typed anyOf: [object, null] (generated by FastMCP.from_openapi from the backend's OpenAPI). Same server class as #3084.

Reproduction

Call an MCP tool with a dict-typed argument:

add_memory(
  messages=[{"role": "user", "content": "..."}],
  user_id="u1",
  metadata={"category": "debug"}       # dict, matches the tool's object schema
)

Result:

Error: MCP error: HTTP error 422: Unprocessable Entity -
{'detail': [{'type': 'dict_type', 'loc': ['body', 'metadata'],
'msg': 'Input should be a valid dictionary',
'input': '{"category":"debug"}'}]}    # <-- note: input arrived as a STRING

The input field in the error is the smoking gun: the server received '{"category":"debug"}' (a string), not {"category":"debug"} (an object).

Isolation (the server is not at fault)

Verified by bypassing Claude Code entirely and calling the same server over raw HTTP:

| Client / payload | Result |
|---|---|
| Raw HTTP, metadata as a real dict | 200 OK |
| Raw HTTP, metadata as a JSON string | 422 (correct rejection) |
| Claude Code, metadata as a dict | 422 — arrives at server as a string |

The MCP tool's own inputSchema (from tools/list) correctly declares metadata: anyOf[object, null]. The backend OpenAPI is identical. Every server layer is consistent and correct. The dict is stringified between the tool call and the wire, inside Claude Code's MCP client.

Impact

Any MCP server that strictly validates an object-typed parameter (FastMCP + Pydantic, and anything generated from an OpenAPI type: object) is unusable for that parameter from Claude Code. Workarounds are ugly: fold the structured value into a string field, or make the server tolerate a stringified dict (I did the latter for our server, but that should not be required of every server).

Relationship to #3084

#3084 was marked COMPLETED 2025-09-08, and its final comments already noted the fix did not fully take ("I just checked and it seems the issue still persists"). This report is a current reproduction on 2.1.214, ~10 months later, on the exact server class the original described. Either the fix regressed or it never covered the anyOf[object,null] / OpenAPI-generated path.

Happy to provide the raw tools/list schema dump or a minimal FastMCP server that reproduces it.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗