Skip to content

fix(fastmcp): refuse an undeclared tool argument instead of dropping it - #106

Merged
ciaransweet merged 1 commit into
mainfrom
fix/forbid-undeclared-tool-arguments
Aug 26, 2026
Merged

fix(fastmcp): refuse an undeclared tool argument instead of dropping it#106
ciaransweet merged 1 commit into
mainfrom
fix/forbid-undeclared-tool-arguments

Conversation

@ciaransweet

@ciaransweet ciaransweet commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

An argument a tool does not declare is currently accepted, discarded, and never
mentioned. The tool then runs as though it had never been asked for.

langchain_mcp_adapters.to_fastmcp publishes the input schema from
tool_call_schema, but validates against a separate model built from that
schema's fields alone:

arg_model = create_model(f"{tool.name}Arguments", **field_definitions, __base__=ArgModelBase)

ArgModelBase sets no extra policy, so pydantic's default applies and the
extras are stripped before the tool is called.

That is a silent wrong answer rather than a loud failure: the call succeeds, the
result looks ordinary, and the one thing that would explain it — that an
argument went missing — is the one thing nobody is told. It is easy to hit
wherever a tool renames or reshapes a parameter that a model has seen named
another way somewhere else; the model sends the name it saw, and gets a
confident answer to a question it did not ask.

Two lines, both needed:

  • extra="forbid" on fn_metadata.arg_model — the enforcement.
  • additionalProperties: false on the published schema — what tells a client
    the rule exists. Upstream publishes no extra policy at all, so enforcing on
    its own would refuse calls the advertised schema allowed.

./scripts/test 375 passed, ./scripts/lint clean.

Behaviour change worth a look before merging: any caller that has been
sending a stray argument starts getting an error rather than being quietly
tolerated. That is the point, but it will surface wherever it has been
happening. It surfaces as a tool-call error result, not a raised exception —
the same path an argument with a wrong type already takes today — so the model
gets a ToolMessage it can act on and retries, and the thread stays
well-formed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SqYYX2DusBMsnR58iDro1Y

Upstream to_fastmcp builds its validation model from tool_call_schema's fields
alone, and ArgModelBase sets no extra policy — so pydantic's default applies and
an argument the tool does not declare is discarded before the tool is called,
with nothing said to anyone.

Mark that model extra="forbid", and stamp additionalProperties: false on the
published input schema so a client is told the rule rather than surprised by it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqYYX2DusBMsnR58iDro1Y
@ciaransweet
ciaransweet force-pushed the fix/forbid-undeclared-tool-arguments branch from 99b87c8 to 510431a Compare August 26, 2026 12:55
@ciaransweet
ciaransweet merged commit 82e6bf7 into main Aug 26, 2026
4 checks passed
@ciaransweet
ciaransweet deleted the fix/forbid-undeclared-tool-arguments branch August 26, 2026 12:57
ciaransweet pushed a commit that referenced this pull request Aug 26, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.8.1](mcp-toolsets-runtime-v0.8.0...mcp-toolsets-runtime-v0.8.1)
(2026-08-26)


### Bug Fixes

* **fastmcp:** refuse an undeclared tool argument instead of dropping it
([#106](#106))
([82e6bf7](82e6bf7))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: ds-release-bot[bot] <116609932+ds-release-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant