Python: Add progressive MCP disclosure#6850
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Pull request overview
Adds progressive disclosure support for Python MCP tools so agents can start with a minimal model-facing tool surface (stable list/load “loader” tools + optionally always-loaded tools) and progressively load additional allowed MCP tool schemas on demand, reducing upfront context bloat while preserving allowed_tools boundaries and existing MCP behaviors.
Changes:
- Added
use_progressive_disclosureandalways_loadoptions across Python MCP tool classes and implemented loader tools (list_mcp_tools/load_tool, prefix-aware) that progressively add tools viaFunctionInvocationContext.add_tools(...). - Added unit tests covering constructor behavior, filtering semantics, prefix behavior, idempotent loading, approval preservation, and runtime kwargs/header-provider behavior.
- Added documentation updates and a runnable sample demonstrating progressive disclosure with a self-spawned stdio MCP server.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| python/packages/core/agent_framework/_mcp.py | Implements progressive disclosure options, loader tools, and progressive tool resolution/loading logic. |
| python/packages/core/tests/core/test_mcp.py | Adds test coverage validating progressive disclosure behavior and invariants. |
| python/packages/core/AGENTS.md | Documents progressive MCP disclosure semantics and how it interacts with allowed-tools, approvals, and tool exposure. |
| python/samples/02-agents/mcp/mcp_progressive_disclosure.py | New sample showing progressive disclosure against a self-spawned MCP stdio server. |
| python/samples/02-agents/mcp/README.md | Adds the progressive disclosure sample to the MCP samples index and notes no extra server setup is needed. |
|
Flagged issue
Source: automated DevFlow PR review |
There was a problem hiding this comment.
Automated Code Review
Reviewers: 5 | Confidence: 87%
✓ Correctness
The progressive MCP disclosure implementation is well-structured and correct. The loader tools properly leverage existing FunctionTool context injection and the FunctionInvocationContext.add_tools API. The always_load boundary is correctly gated by allowed_tools via _filtered_functions(). The identity-based loaded checks are safe because the same FunctionTool objects are reused throughout. No correctness, security, or data-loss issues found.
✓ Security Reliability
The progressive MCP disclosure implementation is well-designed from a security and reliability perspective. The
allowed_toolsboundary is consistently enforced:_resolve_progressive_functionvalidates the model-suppliedtoolparameter against_filtered_functions(),_list_progressive_mcp_toolsonly reveals allowed tools, andload_toolcorrectly rejects filtered tools. Loaded tools preserve their approval modes, argument filtering, and header-provider behavior. The loader tools appropriately useapproval_mode="never_require"since they only make tools available without executing remote code. The idempotent load check prevents duplicate tool additions. No injection risks, resource leaks, or unhandled failure modes were identified.
✓ Test Coverage
The progressive MCP disclosure feature has good test coverage for the main happy paths and key security boundary (allowed_tools filtering). However, there are a few untested error/edge-case branches in the new implementation: the
ctx.tools is Noneguard in_load_progressive_mcp_tool, the ambiguous tool name resolution path, and theloaded: Truestate reporting fromlist_mcp_toolsafter a tool has been loaded. These are minor gaps that don't block the PR but would strengthen confidence in the feature.
✓ Failure Modes
The progressive disclosure implementation is well-structured and integrates cleanly with the existing progressive tool exposure mechanism. However,
_load_progressive_mcp_toolis a synchronous function, which means it gets dispatched to a thread viaasyncio.to_threadduring agent execution. Since the framework's function-calling loop executes batch tool calls concurrently viaasyncio.gather(line 1788 of _tools.py), twoload_toolcalls in the same model batch will race onctx.add_tools(), potentially silently losing one tool's addition. Making the functionasyncwould keep it in the event loop thread where cooperative scheduling prevents the race.
✗ Design Approach
I found one design issue in the progressive disclosure implementation: it introduces synthetic loader tool names without reserving them against remote MCP tool names, even though the existing MCP loading and agent tool-merging paths already enforce unique local tool names. That means a valid server exposing
list_mcp_tools,load_tool, or a prefixed equivalent can no longer be used safely with progressive disclosure.
Flagged Issues
-
MCPTool.functionsprepends synthetic loader tools without checking for collisions with server-advertised tool names, but the repo already treats duplicate local tool names as invalid. A server tool namedlist_mcp_tools,load_tool, or the prefixed equivalent will either makeAgent.run()fail with a duplicate-name error or make that remote tool impossible to load. Reject reserved-name collisions up front (the same way colliding local MCP names are rejected today) or otherwise guarantee these synthetic names cannot overlap remote tools.
Suggestions
- Add a test for
_load_progressive_mcp_toolwhenctx.tools is None— this exercises the guard at _mcp.py:940-941 that raises ToolExecutionException. - Add a test for the ambiguous tool name resolution path in
_resolve_progressive_function(_mcp.py:905-906) wherelen(matches) > 1.
Automated review by eavanvalkenburg's agents
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed the DevFlow flagged issue in d039c1b: progressive disclosure now treats the loader names as reserved in the progressive surface, filters always_load/discovery collisions, and returns a clear load_tool error that points users to tool_name_prefix or excluding the colliding MCP tool. Added regression tests for these cases. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Motivation & Context
Large MCP servers can expose many tools, which frontloads a lot of schema into the model context even when only a small subset is needed. This adds progressive disclosure support for MCP tools so agents can discover and load MCP tool schemas on demand while keeping the existing
allowed_toolsboundary intact.Description & Review Guide
use_progressive_disclosureandalways_loadconstructor options to all Python MCP tool classes immediately afterallowed_tools.list_mcp_toolsandload_toolloader tools, plus any allowed tools selected byalways_load; other allowed tools are added throughFunctionInvocationContext.add_tools(...)when loaded.load_tools=Falsevalidation.allowed_tools,always_load, prefixed loader names, and the use of the existing progressive tool exposure mechanism.Related Issue
Fixes #5821
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.