Skip to content

Python: fix MCP tool argument shadowing the remote tool name - #7799

Merged
Evan Mattson (moonbox3) merged 2 commits into
microsoft:mainfrom
giles17:fix-mcp-tool-name-override
Aug 21, 2026
Merged

Python: fix MCP tool argument shadowing the remote tool name#7799
Evan Mattson (moonbox3) merged 2 commits into
microsoft:mainfrom
giles17:fix-mcp-tool-name-override

Conversation

@giles17

Copy link
Copy Markdown
Contributor

Motivation & Context

MCPTool generates one local FunctionTool per remote MCP tool. The generated function
carried the remote tool name as the default value of a keyword-only parameter:

async def _call_tool_with_runtime_kwargs(
    ctx: FunctionInvocationContext,
    *,
    _remote_tool_name: str = tool.name,
    **kwargs: Any,
) -> str | list[Content]:
    ...
    return await self.call_tool(_remote_tool_name, **call_kwargs)

Tool arguments are splatted into that callable, so an argument named _remote_tool_name
binds to the keyword-only parameter instead of landing in **kwargs, and the generated
function calls a different remote tool than the one it represents. Unknown argument names are
only rejected when the tool's inputSchema carries additionalProperties: false, which MCP
servers 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

  • What are the major changes? The remote tool name moves out of the generated function's
    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.
  • What is the impact of these changes? No public API or behavior change. The parameter was
    private and undocumented, and nothing in the repository passed it. The ctx handling,
    _meta precedence, argument filtering, approval mode, and telemetry paths are unchanged.
  • What do you want reviewers to focus on? Whether the factory is the right shape here
    versus a private method plus partial, which would more closely mirror the prompt path.

Related Issue

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

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
Copilot AI balanced review requested due to automatic review settings August 20, 2026 18:10
@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Aug 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _mcp.py139411591%282, 288, 397, 416, 637, 716–717, 831, 856, 907, 1022, 1025, 1035, 1039, 1086–1087, 1092, 1099–1100, 1107, 1112–1113, 1120–1121, 1125, 1130–1131, 1140, 1147–1148, 1166, 1179, 1203–1204, 1223–1226, 1228–1229, 1233, 1259, 1293–1295, 1297, 1351–1353, 1412–1413, 1696, 1737–1738, 1751, 1754, 1763–1764, 1769–1770, 1776, 1830–1831, 1851–1852, 1861–1862, 1867–1868, 1874, 1952, 1955, 1982, 2005–2009, 2032–2034, 2039, 2043–2044, 2146, 2153, 2155, 2226, 2241–2242, 2249–2250, 2255–2256, 2261, 2265, 2280, 2342, 2525, 2527, 2549, 2551–2554, 2567–2568, 2612, 2674, 3113–3114, 3159, 3379–3380, 3398
TOTAL47240436390% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
9612 36 💤 0 ❌ 0 🔥 2m 44s ⏱️

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@moonbox3
Evan Mattson (moonbox3) added this pull request to the merge queue Aug 21, 2026
Merged via the queue into microsoft:main with commit aeaabe5 Aug 21, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants