Uh oh!
There was an error while loading. Please reload this page.
Python: Fix MCP tools duplicated on second turn when runtime tools are present - #4432
Conversation
When AG-UI's collect_server_tools pre-expands MCP functions on turn 2 (after the MCP server is connected), _prepare_run_context unconditionally appends them again from self.mcp_tools, duplicating every MCP tool. Skip MCP functions whose names already exist in the final tool list, following the same name-based dedup pattern used in _merge_options. Fixesmicrosoft#4381 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
This PR fixes a bug (issue #4381) where MCPStdioTool functions are duplicated on the second and subsequent turns when AgentFrameworkAgent (AG-UI) is used with an Agent that also receives client-side tools at runtime. On turn 2, AG-UI's collect_server_tools pre-expands MCP functions (since the server is now connected) and passes them as runtime tools=, but _prepare_run_context unconditionally extended final_tools from self.mcp_tools again — doubling every MCP function.
Changes:
- Added name-based deduplication in
_prepare_run_contextbefore extendingfinal_toolswithmcp_server.functions, skipping any function already present by name. - Added a regression test
test_mcp_tools_not_duplicated_when_passed_as_runtime_toolsthat simulates the turn-2 scenario with a connected mock MCP tool.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
python/packages/core/agent_framework/_agents.py | Adds existing_names set before extending from self.mcp_tools, skipping functions already present in final_tools |
python/packages/core/tests/core/test_agents.py | New regression test verifying no duplication when MCP functions are passed as runtime tools |
There is one bug in the fix itself: the existing_names set comprehension at line 1054 uses t.name directly on all items in final_tools, but final_tools can contain Mapping/dict-based tool definitions (which pass through normalize_tools and the else branch at line 1052) that do not have a .name attribute. This would cause an AttributeError at runtime when any runtime tool is a dict-style tool definition.
The file already defines _get_tool_name(t) (lines 84–91) that safely handles both dict-based tools and attribute-based tools, and is used for exactly this purpose in _merge_options (line 110). The fix should use _get_tool_name instead of direct .name access.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Summary
Fixes#4381
When
AgentFrameworkAgent(fromagent-framework-ag-ui) is used with anAgentthat has anMCPStdioTool, and the AG-UI request includes client-side tools, the MCP tool functions are expanded twice on the second and subsequent turns - producing a tool list with every MCP tool name duplicated.Root Cause
In
_prepare_run_context(_agents.py),self.mcp_toolsfunctions were unconditionally appended tofinal_toolseven when they were already present from runtimetools(pre-expanded by AG-UI'scollect_server_toolson turn 2 when the MCP server is connected).Fix
Added name-based deduplication before extending
final_toolswith MCP server functions - collecting existing tool names first and skipping any MCP functions already present. This follows the same dedup pattern used in_merge_options.Changes
packages/core/agent_framework/_agents.py- 2-line fix in_prepare_run_contextpackages/core/tests/core/test_agents.py- addedtest_mcp_tools_not_duplicated_when_passed_as_runtime_toolsVerification
Reproduced the issue using the repro script from the issue, confirmed turn 2 no longer duplicates tools: