[BUG] MCP client drops a trailing Optional[str] tool argument from tools/call (present in schema, lost on the wire)

Resolved 💬 3 comments Opened Jun 6, 2026 by atdash Closed Jun 15, 2026

Summary

When Claude Code calls an MCP server tool whose signature has an Optional[str] = None parameter positioned after a parameter that has a non-None default, the value supplied for that trailing optional parameter does not reach the server. The tool receives the parameter as its default (None), even though a value was provided in the tool call and the parameter is present in the tool's advertised input schema. Sibling tools whose optional parameters follow only required parameters deliver their values correctly.

Environment

  • Claude Code: 2.1.126
  • OS: macOS (Darwin 25.5.0)
  • MCP server: Python, mcp 1.26.0 (FastMCP), stdio transport

Reproduction

Minimal server:

from typing import Optional
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("repro")

@mcp.tool()
def echo_args(summary: str, observation_type: str = "general",
              related_item_ids: Optional[str] = None) -> str:
    return (f"summary={summary!r} "
            f"observation_type={observation_type!r} "
            f"related_item_ids={related_item_ids!r}")

if __name__ == "__main__":
    mcp.run()

.mcp.json:

{ "mcpServers": { "repro": { "command": "python", "args": ["/abs/path/repro_server.py"] } } }

Then have Claude Code call echo_args with all three arguments, e.g. summary=\"x\", observation_type=\"y\", related_item_ids=\"a,b\".

Expected

related_item_ids='a,b'

Actual

related_item_ids=None — the supplied value is absent and the tool runs with its default.

What rules out the server

  • The tool's advertised input schema (generated by mcp 1.26.0) includes related_item_ids.
  • Calling the underlying function directly (bypassing the MCP transport) with the value works as expected.
  • A sibling tool with signature (item_id: str, label: Optional[str] = None, body: Optional[str] = None) receives label and body correctly over the same connection.
  • The failing tool differs only by having a defaulted non-Optional parameter (observation_type: str = \"general\") between the required parameter and the trailing optional one.

Suspected trigger (unconfirmed)

An Optional parameter that follows a parameter with a non-None default may be omitted from the outgoing tools/call arguments object. This is inferred by eliminating the server; I did not capture the raw JSON-RPC tools/call payload. Confirmation via claude --debug (inspecting the outgoing tools/call arguments) would verify whether the field is absent on the wire.

Impact

Tools relying on such a parameter silently receive the default, so any behavior gated on it never runs and no error is surfaced.

View original on GitHub ↗

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