[BUG] MCP client drops a trailing Optional[str] tool argument from tools/call (present in schema, lost on the wire)
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,
mcp1.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
mcp1.26.0) includesrelated_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)receiveslabelandbodycorrectly 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗