MCP: unrecognized tool-call args are silently dropped instead of rejected when all params are optional
Summary
When a tool call includes a parameter name that isn't in the tool's declared JSON schema, and every declared parameter is optional (has a default), the unrecognized parameter is silently dropped instead of the call being rejected. The tool then executes with default values for everything, producing a "successful" result that silently ignores the caller's intended argument.
This is a client-side (Claude Code tool-calling layer) behavior — it happens before the request reaches the MCP server. It happens even when the MCP server's own advertised schema for that tool includes "additionalProperties": false.
Expected behavior
A tool call with a key not present in the tool's inputSchema.properties should be rejected with a clear validation error (as MCP-spec-compliant strict JSON Schema validation would do for a schema with "additionalProperties": false), regardless of whether the resulting (filtered) argument set happens to satisfy the schema's required list.
Actual behavior
- If the tool has at least one required parameter with no default, and stripping the unrecognized key leaves that required parameter unfilled, the call does fail — but with a message about the required field being missing/undefined, not about the actually-wrong key name.
- If every parameter is optional, stripping the unrecognized key still leaves a schema-valid (all-defaults) argument set, so the call proceeds silently. The caller gets a normal, successful-looking result — just not filtered/scoped the way they intended, with no indication anything went wrong.
Minimal repro
A FastMCP (Python) tool like:
@mcp.tool()
def search_records(target_id: str | None = None, days: int = 1, limit: int = 50) -> dict:
"""Query records, optionally filtered by target_id."""
...
return {"filter": {"target_id": target_id, "days": days}, "results": [...]}
- Call the tool with
{"wrong_id": "abc123", "days": 1}(note:wrong_idis not a real parameter — the correct name istarget_id). - Expected: a validation error naming
wrong_idas unrecognized. - Actual: the call succeeds,
wrong_idis silently dropped,target_iddefaults toNone, and the response comes back as{"filter": {"target_id": null, "days": 1}, "results": [... everything, unfiltered ...]}with no error or warning at all.
For comparison: calling the same MCP server directly over stdio with the low-level mcp Python client (bypassing Claude Code's tool-calling layer entirely) correctly rejects the identical bad call with a pydantic ValidationError: Unexpected keyword argument. So the MCP server and its declared schema (which does include "additionalProperties": false, confirmed via TypeAdapter(fn).json_schema()) are behaving correctly — the gap is specifically in how Claude Code validates/constructs the call before sending it.
Why this matters
For any tool where a caller-supplied identifier/filter parameter is optional (a very common and reasonable API shape — e.g., "search everything, or narrow by X if given"), a typo'd or half-remembered parameter name doesn't error, it silently widens the query to everything instead of narrowing it. In a security/audit-log-querying context this is a real hazard: an analyst intending to look at one user's activity can silently get a "successful," plausible-looking, but actually unfiltered/tenant-wide result set with zero indication their filter didn't apply.
Suggested fix
Honor "additionalProperties": false (or equivalent strict-schema semantics) in the client-side tool-call argument validation/construction step, rejecting unrecognized keys outright rather than silently dropping them, independent of whether the remaining fields happen to satisfy required.