[BUG] MCP tool schema mangled for locally-configured stdio servers (Optional[str] params)

Status Open
Reported on v2.1.207
Maintainer reply None cached
Activity 0 comments · opened Aug 27, 2026

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 a local (stdio) MCP server exposes a tool parameter typed as Optional[str] (e.g. param: str | None = None), the server correctly advertises its JSON Schema as anyOf: [{"type":"string"},{"type":"null"}] (verified directly on the server). But when that tool definition reaches the model through Claude, the parameter's schema arrives as an empty object {}, with no type information at all.

As a result, when I pass a digit-only string for that parameter (e.g. a numeric account ID like "4608023173"), it gets serialized as a JSON number instead of a JSON string in the tools/call request. The server's Pydantic validation then rejects it, and there is no way to pass the value correctly — any non-numeric-looking string (with dashes, spaces, etc.) survives as a string but is then rejected by the downstream API for a different reason (it expects a pure-digit string).

What Should Happen?

The full JSON Schema for Optional/Union-typed parameters, as correctly advertised by the MCP server (e.g. anyOf: [{"type":"string"},{"type":"null"}]), should be preserved when relayed to the model, so arguments can be serialized with the correct JSON type instead of guessing the type from the value's content.

Error Messages/Logs

1 validation error for call[execute_gaql]
login_customer_id
  Input should be a valid string [type=string_type, input_value=1234567890, input_type=int]
    For further information visit https://errors.pydantic.dev/2.13/v/string_type

---

Regression evidence from ~/Library/Logs/Claude/mcp-server-<name>.log:

The same tool/server had several successful execute_gaql calls (string parameter handled correctly, no
errors) both two days earlier and earlier the same day I hit the bug, from what appears to be a regular
Claude Desktop chat session. A Claude Code session started shortly after, and every execute_gaql call from
that Claude Code session has failed with the type error above since then.

The failing calls don't even appear in the server's own log (no matching tools/call entry) — they never
reach the local server process, meaning the request is rejected earlier, client-side, specifically within
the Claude Code call path.

Steps to Reproduce

  1. Configure a local stdio MCP server (Python, fastmcp) exposing a tool with a parameter typed

param: str | None = None (Optional[str], no other type in the union).

  1. Confirm server-side that fastmcp generates the correct schema for it: anyOf: [{"type":"string"},{"type":"null"}].
  2. From a Claude Code session, ask Claude to call the tool passing a digit-only string for that parameter,
  1. The call fails with a Pydantic error showing the value was sent as a JSON integer, not a string:

Input should be a valid string [type=string_type, input_value=*number*, input_type=int].

  1. For comparison, calling the exact same tool with the exact same parameter from a regular Claude Desktop

chat conversation (not Claude Code) works correctly — confirmed on the same server, same day.

This appears specific to the Claude Code execution path, not a general Claude Desktop regression.

Claude Model

Sonnet (default)

Is this a regression?

Yes, this worked in a previous version

Last Working Version

Unknown exact app version — last confirmed working 2026-05-28, per local MCP server logs. Failure onset correlates with a new "Era probe verdict: legacy (exec lane pinned...)" log line first appearing 2026-08-18.

Claude Code Version

2.1.207 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

This affects any locally-configured stdio MCP server with an Optional/Union-typed tool parameter, not specific
to one server implementation — the schema loss happens on the Claude Desktop side when relaying local server
tool definitions to the model.

View original on GitHub ↗