Python: feat(python): add as_progressive_tools() to MCPTool for progressive discovery mode#5947
Conversation
Implements progressive MCP discovery/dispatch mode per issue microsoft#5821. Adds two FunctionTools (list_mcp_tools + call_mcp) that let the model discover and call tools on demand instead of loading all schemas upfront. Reduces token overhead for large MCP servers.
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
Adds a “progressive discovery” exposure mode for Python MCP servers so agents can discover MCP tools on-demand (via a small stable tool surface) instead of injecting all remote tool schemas into the model context up front—targeting large MCP servers and reducing token overhead.
Changes:
- Added
MCPTool.as_progressive_tools()to expose two FunctionTools: a discovery tool (list_mcp_tools) and a dispatcher (call_mcp). - Added unit tests validating the progressive tools’ basic shape, naming customization, server filtering, dispatch routing, and error propagation.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| python/packages/core/agent_framework/_mcp.py | Adds as_progressive_tools() with discovery + dispatch FunctionTools backed by the MCPTool’s loaded functions. |
| python/packages/core/tests/core/test_mcp.py | Adds tests for progressive discovery/dispatch behavior. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Motivation and Context
Large production MCP servers can expose 50–200+ tools. Today, MCPTool loads and exposes every remote tool schema upfront as individual FunctionTools — injecting all of them into the model's context window on every agent turn. This causes significant token overhead, slower inference, and couples agent prompts to every remote tool shape even when most tools are never used.
This PR adds a progressive discovery mode that lets the model find and call tools on demand, instead of receiving the full tool surface upfront. Fixes #5821.
Description
Adds as_progressive_tools() to the MCPTool base class, returning exactly two FunctionTools:
list_mcp_tools(server?) — a read-only discovery tool that returns the names, descriptions, and parameter schemas of all allowed tools on the server as JSON. Set to approval_mode="never_require" since it performs no side effects.
call_mcp(server, tool, arguments?) — a dispatch tool that validates the requested tool exists in the allowed list, then routes execution through the existing FunctionTool.invoke() path. This preserves all SDK-owned behavior: OTel propagation, isError handling, MCP result parsing, connection retry, and approval middleware.
The method is placed on MCPTool (not just MCPStreamableHTTPTool) because the logic — reading self.functions and routing through invoke() — is fully transport-agnostic. This means MCPStdioTool, MCPStreamableHTTPTool, and MCPWebsocketTool all get progressive mode automatically.
Usage:
Before: exposes all remote tool schemas upfront
agent = Agent(..., tools=[*mcp.functions])
After: model discovers and calls tools on demand
agent = Agent(..., tools=[*mcp.as_progressive_tools()])
10 new unit tests added covering: return shape, custom names, discovery filtering, dispatch routing, argument defaulting, server validation, allowed-tool enforcement, and error propagation.
Contribution Checklist
as_progressive_tools()is a purely additive method. No existing interfaces are modified.