Python: fix MCP tool argument shadowing the remote tool name - #7799
Conversation
The generated MCP function held the remote tool name as the default of a keyword-only parameter. Tool arguments are splatted into that function, so an argument named `_remote_tool_name` bound to the parameter instead of `**kwargs` and changed which remote tool was called. Move the remote tool name into a factory closure so it is no longer part of the generated function's signature, matching the prompt path which already binds the name positionally via `partial`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4cb58e38-4af2-485d-b734-7d70972959f2
There was a problem hiding this comment.
Pull request overview
Fixes MCP-generated tools so model arguments cannot override the remote tool target.
Changes:
- Captures the remote tool name in a factory closure.
- Adds a regression test verifying calls cannot be redirected.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
python/packages/core/agent_framework/_mcp.py |
Moves the remote tool name out of the generated callable’s signature. |
python/packages/core/tests/core/test_mcp.py |
Tests argument filtering and correct remote-tool routing. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
MAF Automated Review — Iteration 1
Result: No findings
Scope: full PR (1 commit(s)): f7618b30a0bc
Model: gpt-5.6-sol
Overview
The change moves the remote MCP tool name out of the generated callable's model-visible signature and captures it in a per-tool closure while preserving argument filtering, trusted metadata precedence, approval, and dispatch behavior. The regression test exercises the previously vulnerable permissive-schema path and confirms that model-supplied arguments cannot redirect the call or leak undeclared arguments downstream; no publishable residual risk was established.
Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4cb58e38-4af2-485d-b734-7d70972959f2
There was a problem hiding this comment.
MAF Automated Review — Iteration 2
Result: No findings
Scope: 1 net-new commit(s): 1b187c7547aa
Model: gpt-5.6-sol
Overview
This incremental update only refines the regression test's AsyncMock.await_args handling by asserting it is non-null before inspecting the captured call. The test still verifies that model-supplied arguments cannot redirect the closure-bound MCP tool name and that undeclared arguments are filtered, with no production behavior or public contract changed.
Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.
Motivation & Context
MCPToolgenerates one localFunctionToolper remote MCP tool. The generated functioncarried the remote tool name as the default value of a keyword-only parameter:
Tool arguments are splatted into that callable, so an argument named
_remote_tool_namebinds to the keyword-only parameter instead of landing in
**kwargs, and the generatedfunction calls a different remote tool than the one it represents. Unknown argument names are
only rejected when the tool's
inputSchemacarriesadditionalProperties: false, which MCPservers are not required to emit, so the argument reaches the function on many servers.
The equivalent prompt path is already unaffected because it binds the remote name positionally
via
partial(self.get_prompt, prompt.name).Description & Review Guide
signature and into a
_make_mcp_tool_caller(mcp_tool, remote_tool_name)factory closure.The parameter default was there to bind the name per loop iteration; a factory gives the
same per-iteration binding without exposing the name as a parameter, so it can only ever be
reached through
**kwargs(which is forwarded as tool arguments and filtered by_prepare_call_kwargs). Adds a regression test covering an argument named_remote_tool_name.private and undocumented, and nothing in the repository passed it. The
ctxhandling,_metaprecedence, argument filtering, approval mode, and telemetry paths are unchanged.versus a private method plus
partial, which would more closely mirror the prompt path.Related Issue
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.