MCP: unrecognized tool-call args are silently dropped instead of rejected when all params are optional

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 29, 2026

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": [...]}
  1. Call the tool with {"wrong_id": "abc123", "days": 1} (note: wrong_id is not a real parameter — the correct name is target_id).
  2. Expected: a validation error naming wrong_id as unrecognized.
  3. Actual: the call succeeds, wrong_id is silently dropped, target_id defaults to None, 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.

View original on GitHub ↗