Skip to content

Python: Reject MCP servers passed as tools to Claude and Copilot agents - #7835

Open
NekoPunch (orangeCatDeveloper) wants to merge 1 commit into
microsoft:mainfrom
orangeCatDeveloper:python-mcp-tools-claude-copilot
Open

Python: Reject MCP servers passed as tools to Claude and Copilot agents#7835
NekoPunch (orangeCatDeveloper) wants to merge 1 commit into
microsoft:mainfrom
orangeCatDeveloper:python-mcp-tools-claude-copilot

Conversation

@orangeCatDeveloper

@orangeCatDeveloper NekoPunch (orangeCatDeveloper) commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

An MCP server handed to ClaudeAgent or GitHubCopilotAgent is silently dropped: no error, no warning, and none of its tools reach the model.

weather = MCPStdioTool(name="weather", command="python", args=["weather_server.py"])

agent = ClaudeAgent(tools=[weather])
await agent.run("What is the weather in Beijing?")   # "I have no tool for that"

The same code works on ChatAgent, so the failure looks like a model problem rather than a wiring one.

Description & Review Guide

  • What are the major changes?

Both provider agents convert tools themselves and only recognise FunctionTool; anything else is skipped, and an MCPTool -- a server connection rather than a callable tool -- fell into that branch.

Neither agent should be connecting to MCP servers in the first place: both SDKs run their own tool loop and take MCP servers as configuration (ClaudeAgentOptions.mcp_servers, create_session(mcp_servers=...)), which is what the existing samples use. What a framework-managed MCPTool promises on top of that -- middleware, tracing, approval routing, result parsing -- cannot survive a connection the framework does not own, so the agents now refuse it and quote the native configuration to write instead, filled in with the server's own name.

Both checks run on the normalized tool list, since normalize_tools() flattens tool-collection wrappers that would otherwise hide an MCPTool, and Copilot checks the option-supplied tools as well as the constructor's. Claude's tool classification was only treating list as a collection, so a tuple bypassed it -- it now follows the same rule as normalize_tools(), which also fixes built-in tool names passed as a tuple being mistaken for custom tools.

  • What is the impact of these changes?

A configuration that silently did nothing now fails, at construction time where the tools are passed there. Nobody's working code changes behaviour: an MCPTool never contributed anything to these agents.

I first implemented this as a translation into each SDK's native mcp_servers config, so that ChatAgent code would port over unchanged, and dropped it. Rejection can only name the options an MCPTool carries explicitly; it cannot cover what the framework supplies ambiently -- middleware, tracing, approval routing -- because those are not options anyone passed. Ported code would have run and quietly behaved differently, which is a worse failure than the one this PR started from. Happy to revisit if the project does want tools= to accept MCPTool everywhere.

Before / after, the server from the snippet above:

no error; configured tools: [<agent_framework._mcp.MCPStdioTool object at 0x108990ad0>]
mcp_servers handed to the SDK: {}

TypeError: MCP server 'weather' cannot be passed to ClaudeAgent as a tool: the Claude
Agent SDK connects to MCP servers itself, so a framework-managed MCPTool would keep none of
its framework behavior. Configure the server natively instead, for example
default_options={'mcp_servers': {'weather': {'type': 'stdio', 'command': 'python',
'args': ['server.py']}}}, or use a ChatAgent, where the framework owns the connection.

Related Issue

Fixes #3651

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.

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

Adds shared MCP tool expansion so Claude and GitHub Copilot agents can expose MCP server functions.

Changes:

  • Adds _expand_mcp_tools() with connection, validation, and deduplication.
  • Integrates expansion and lifecycle management into both provider agents.
  • Adds MCP expansion and concurrency tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
python/packages/core/agent_framework/_mcp.py Adds shared MCP expansion helper.
python/packages/core/tests/core/test_mcp.py Tests expansion and validation.
python/packages/claude/agent_framework_claude/_agent.py Integrates MCP tools into Claude SDK options.
python/packages/claude/tests/test_claude_agent.py Tests Claude MCP integration.
python/packages/github_copilot/agent_framework_github_copilot/_agent.py Integrates MCP tools into Copilot sessions.
python/packages/github_copilot/tests/test_github_copilot_agent.py Tests Copilot MCP integration.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread python/packages/claude/agent_framework_claude/_agent.py Outdated
@giles17

Copy link
Copy Markdown
Contributor

/review

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/claude/agent_framework_claude
   _agent.py3693191%440–441, 445, 458–460, 470–472, 474, 487–488, 538–539, 584, 588, 590, 594, 603, 649, 652, 692, 697–698, 773, 861, 874, 936–939
packages/github_copilot/agent_framework_github_copilot
   _agent.py5152595%105–106, 151, 160–162, 166, 325–326, 330, 806, 821–822, 901, 914, 1051, 1055, 1224, 1258–1259, 1298, 1301, 1438, 1524, 1542
TOTAL48262448190% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
9825 36 💤 0 ❌ 0 🔥 2m 15s ⏱️

@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: Findings reported
Scope: full PR (2 commit(s)): cabe20e88030, 68d2e04cce20
Model: gpt-5.6-sol

Overview

The PR centralizes MCP expansion and gives both provider agents explicit connection ownership, duplicate-name validation, progressive-disclosure rejection, approval integration, and concurrency guards. The new behavior is well covered across agent-, default-, and run-level tools, but the provider dependency bounds do not yet guarantee the new core helper exists. Copilot also retains dynamically supplied per-run MCP connections for the lifetime of the agent, allowing resources to accumulate across completed runs.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
3 verified findings remained after source verification (2 high, 1 medium) across 2 files. Details are attached to the affected lines below.

Affected areas: python/packages/claude/agent_framework_claude/_agent.py, python/packages/github_copilot/agent_framework_github_copilot/_agent.py

Comment thread python/packages/claude/agent_framework_claude/_agent.py Outdated
Comment thread python/packages/github_copilot/agent_framework_github_copilot/_agent.py Outdated
Comment thread python/packages/github_copilot/agent_framework_github_copilot/_agent.py Outdated
@eavanvalkenburg

Copy link
Copy Markdown
Member

NekoPunch (@orangeCatDeveloper) Giles Odigwe (@giles17) I think we are doing the wrong thing here, both Claude and GithubCopilotAgents have specific MCP server config inputs, so AFAIC we should not allow MCPTool on those and raise and point people to use the MCP server config instead, that will be a better experience I think. What do you both think?

@orangeCatDeveloper NekoPunch (orangeCatDeveloper) changed the title Python: Pass MCP server tools to Claude and Copilot agents Python: Reject MCP servers passed as tools to Claude and Copilot agents Aug 29, 2026
@orangeCatDeveloper

Copy link
Copy Markdown
Contributor Author

I think we are doing the wrong thing here, both Claude and GithubCopilotAgents have specific MCP server config inputs, so AFAIC we should not allow MCPTool on those and raise and point people to use the MCP server config instead, that will be a better experience I think. What do you both think?

Eduard van Valkenburg (@eavanvalkenburg) Agreed—done. Both agents now reject MCPTool, and the error carries the
equivalent native mcp_servers config for the server it refused.

self._custom_tools.extend(normalize_tools(non_builtin_tools))
# Check after normalizing: it flattens tool-collection wrappers, which can hide an MCPTool.
normalized = normalize_tools(non_builtin_tools)
for tool in normalized:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do we have to loop again here, can't we check for MCPTool in the loop on line 424?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The second loop checks the normalized tools because normalize_tools() expands tool-collection wrappers that may contain an MCPTool. Checking only in the first loop would miss those wrapped tools.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

minor comment left, should be good otherwise

Comment thread python/packages/claude/agent_framework_claude/_agent.py Outdated
An MCPTool handed to ClaudeAgent or GitHubCopilotAgent was silently
dropped: no error, no warning, and none of its tools reached the model.
Both SDKs connect to MCP servers themselves and take their own
mcp_servers config, so the framework cannot honor a framework-managed
MCPTool there. It is now refused with the native configuration to write
instead.
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.

Python: [Feature]: Add Support for Converting Agent Framework MCPTools to GitHub Copilot SDK / Claude SDK

4 participants