You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
mcp.server.fastmcp.utilities.func_metadata.ArgModelBase does not set extra in its model_config, so it inherits Pydantic v2's default, extra="ignore":
classArgModelBase(BaseModel):
"""A model representing the arguments to a function."""
...
model_config=ConfigDict(
arbitrary_types_allowed=True,
)
Every per-tool *Arguments model that FastMCP generates via create_model(..., __base__=ArgModelBase, ...)
inherits this. As a result, when a client calls a tool with an argument name that doesn't
exist on the function (a typo, or a hallucinated parameter name from an LLM), Pydantic
silently drops it instead of raising a validation error. The tool then runs with that
parameter at its default value, and returns a "successful" but semantically wrong result —
with no signal anywhere that anything was off.
Reproduction
frommcp.server.fastmcpimportFastMCPmcp=FastMCP("repro")
@mcp.tool()defread_doc(topic: str="") ->str:
returnf"topic={topic!r}"# Simulate what happens when a client calls with the wrong argument name:tool=mcp._tool_manager.get_tool("read_doc")
result=tool.fn_metadata.arg_model.model_validate({"path": "something"})
print(result.model_dump_one_level())
# {'topic': ''} <- no error, no signal that "path" was bogus
Expected behavior
An unknown argument in a tools/call request should fail validation immediately with a
clear error (e.g. Pydantic's own "Extra inputs are not permitted"), the same way a missing
required argument already does. This is especially important for MCP given the primary
caller is frequently an LLM: a wrong parameter name is a common, plausible failure mode, and
a loud, immediate error is far more useful (and self-correcting for the model) than a
silently-wrong "successful" response.
This should be safe: the fields validated by ArgModelBase subclasses are exactly the arguments dict of a tools/call request (i.e. exactly what's declared in the tool's inputSchema). Protocol-level fields (_meta, progressToken, etc.) live on the
surrounding params object, not inside arguments, and Context is injected separately
via arguments_to_pass_directly — neither passes through ArgModelBase. The side effect is
that model_json_schema() will now emit "additionalProperties": false on the published inputSchema, which seems like a feature, not a regression (it lets clients that validate
against the schema catch this class of error before the round-trip).
Scope checked
Confirmed present in mcp 1.27.2 and 1.28.1 (latest on PyPI as of 2026-07).
Confirmed still present on main (the in-progress v2, where fastmcp is being renamed to mcpserver): src/mcp/server/mcpserver/utilities/func_metadata.py has the same ArgModelBase without extra set.
I didn't find an existing issue about this specific behavior (searched for "extra fields",
"forbid", "unknown argument", "additionalProperties", "silently ignored").
Description
mcp.server.fastmcp.utilities.func_metadata.ArgModelBasedoes not setextrain itsmodel_config, so it inherits Pydantic v2's default,extra="ignore":Every per-tool
*Argumentsmodel that FastMCP generates viacreate_model(..., __base__=ArgModelBase, ...)inherits this. As a result, when a client calls a tool with an argument name that doesn't
exist on the function (a typo, or a hallucinated parameter name from an LLM), Pydantic
silently drops it instead of raising a validation error. The tool then runs with that
parameter at its default value, and returns a "successful" but semantically wrong result —
with no signal anywhere that anything was off.
Reproduction
Expected behavior
An unknown argument in a
tools/callrequest should fail validation immediately with aclear error (e.g. Pydantic's own "Extra inputs are not permitted"), the same way a missing
required argument already does. This is especially important for MCP given the primary
caller is frequently an LLM: a wrong parameter name is a common, plausible failure mode, and
a loud, immediate error is far more useful (and self-correcting for the model) than a
silently-wrong "successful" response.
Suggested fix
Set
extra="forbid"onArgModelBase.model_config:This should be safe: the fields validated by
ArgModelBasesubclasses are exactly theargumentsdict of atools/callrequest (i.e. exactly what's declared in the tool'sinputSchema). Protocol-level fields (_meta,progressToken, etc.) live on thesurrounding
paramsobject, not insidearguments, andContextis injected separatelyvia
arguments_to_pass_directly— neither passes throughArgModelBase. The side effect isthat
model_json_schema()will now emit"additionalProperties": falseon the publishedinputSchema, which seems like a feature, not a regression (it lets clients that validateagainst the schema catch this class of error before the round-trip).
Scope checked
mcp1.27.2 and 1.28.1 (latest on PyPI as of 2026-07).main(the in-progress v2, wherefastmcpis being renamed tomcpserver):src/mcp/server/mcpserver/utilities/func_metadata.pyhas the sameArgModelBasewithoutextraset."forbid", "unknown argument", "additionalProperties", "silently ignored").
(Zod's default
stripbehavior on unknown object keys) — seeAdditional parameters are not passed through to tools typescript-sdk#147
Happy to open a PR with the one-line change plus a regression test if that's useful.