[BUG] Cowork/Desktop: MCP tool inputSchema with property-level anyOf [X, null] (Optional[X]) is silently stripped before reaching the model
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When an MCP tool's inputSchema contains a parameter with anyOf [X, null] shape — the standard output for Python Optional[X] generated by FastMCP — Claude desktop strips the anyOf block, leaving only default and description. The LLM sees no type info and serializes arguments inconsistently. In particular, Optional[list[str]] parameters end up serialized as stringified JSON ('["a","b"]'), which servers then reject with "expected list, got str".
Affected: Claude desktop, both Code tab and Cowork tab (verified 2026-05-05).
Not affected: Claude Code CLI (preserves full schema).
What Should Happen?
Claude desktop should pass the full inputSchema to the LLM, including any anyOf blocks. The current stripping breaks any MCP tool that uses Optional[X] Python parameters — i.e. virtually every FastMCP-generated tool with optional list/dict/object inputs.
Error Messages/Logs
Steps to Reproduce
MCP server: FastMCP 3.2.4 + Pydantic 2.x, streamable-HTTP transport.
Server-side raw tools/list response (verified via curl):
"keywords": {
"anyOf": [
{"items": {"type": "string"}, "type": "array"},
{"type": "null"}
],
"default": null,
"description": "Seed keywords..."
}
In Claude desktop Code tab and Cowork tab, asking Claude to dump the raw schema for the same parameter returns:
{
"default": null,
"description": "Seed keywords..."
}
anyOf, type, items are gone. The same prompt in Claude Code CLI returns the full anyOf block intact.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.128 (Claude Code) / Claude 1.5354.0 (9a9e3d)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Symptom in Cowork tab
Calling the tool with keywords=["judo gi", "bjj gi", "karate gi"] causes the LLM to serialize the parameter as one stringified JSON value ('["judo gi", "bjj gi", "karate gi"]'), and the server-side pydantic validator rejects with:
Invalid arguments for tool 'generate_keyword_ideas':
keywords: Input should be a valid list
The same call in Claude Code CLI works (LLM sees type: array, sends array). In Claude desktop Code tab, the LLM happens to infer "array" from the description's example, but that is non-deterministic — different model runs or different examples in description will trip this.
Counter-evidence — narrowing the bug location
I ran the same tools/list request through three layers and inspected the schema at each step:
- Server (FastMCP): raw tools/list response over HTTP contains the full anyOf (verified via curl).
- mcp-remote proxy: the stdio adapter that Claude desktop uses to bridge HTTP MCP servers preserves the full anyOf intact (verified by piping JSON-RPC to npx mcp-remote and inspecting its stdout — schema passes through unchanged).
- Claude Code CLI: connects directly to the HTTP server (no proxy). LLM sees the full anyOf; tool calls succeed.
- Claude desktop (Code tab and Cowork tab): uses mcp-remote as a stdio bridge. LLM sees only {default, description}. anyOf is gone.
Therefore the stripping happens inside the desktop client, after mcp-remote hands the schema over via stdio and before the schema reaches the model context. mcp-remote and the MCP server are not at fault.
Environment
Claude desktop on macOS, build Claude 1.5354.0 (9a9e3d), 2026-04-29T01:14:34.000Z
Reproduced: 2026-05-05
MCP transport: streamable-HTTP
Server: FastMCP 3.2.4 + Pydantic 2.x
Same server tested from Claude Code CLI: full schema preserved (control group)
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗