Skip to content

Python: Fix MCP tools duplicated on second turn when runtime tools are present#4432

Merged
giles17 merged 4 commits intomicrosoft:mainfrom
giles17:mcp-tool-dedup
Mar 4, 2026
Merged

Python: Fix MCP tools duplicated on second turn when runtime tools are present#4432
giles17 merged 4 commits intomicrosoft:mainfrom
giles17:mcp-tool-dedup

Conversation

@giles17
Copy link
Contributor

@giles17 giles17 commented Mar 3, 2026

Summary

Fixes #4381

When AgentFrameworkAgent (from agent-framework-ag-ui) is used with an Agent that has an MCPStdioTool, 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_tools functions were unconditionally appended to final_tools even when they were already present from runtime tools (pre-expanded by AG-UI's collect_server_tools on turn 2 when the MCP server is connected).

Fix

Added name-based deduplication before extending final_tools with 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_context
  • packages/core/tests/core/test_agents.py - added test_mcp_tools_not_duplicated_when_passed_as_runtime_tools

Verification

Reproduced the issue using the repro script from the issue, confirmed turn 2 no longer duplicates tools:

# Before fix:
[API call #1] 3 tools: ['client_tool', 'tool_a', 'tool_b']
[API call #2] 5 tools: ['tool_a', 'tool_b', 'client_tool', 'tool_a', 'tool_b']

# After fix:
[API call #1] 3 tools: ['client_tool', 'tool_a', 'tool_b']
[API call #2] 3 tools: ['tool_a', 'tool_b', 'client_tool']

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.

Fixes microsoft#4381

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 3, 2026 19:12
@github-actions github-actions bot changed the title Fix MCP tools duplicated on second turn when runtime tools are present Python: Fix MCP tools duplicated on second turn when runtime tools are present Mar 3, 2026
@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Mar 3, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _agents.py3434188%435, 439, 491, 856, 892, 908, 991–994, 1057, 1179, 1195, 1197, 1210, 1216, 1252, 1254, 1263–1268, 1273, 1275, 1281–1282, 1289, 1291–1292, 1300–1301, 1304–1306, 1314–1315, 1317, 1322, 1324
TOTAL22530279987% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
4581 25 💤 0 ❌ 0 🔥 1m 22s ⏱️

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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_context before extending final_tools with mcp_server.functions, skipping any function already present by name.
  • Added a regression test test_mcp_tools_not_duplicated_when_passed_as_runtime_tools that 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.

@giles17 giles17 enabled auto-merge March 3, 2026 22:57
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@giles17 giles17 added this pull request to the merge queue Mar 4, 2026
Merged via the queue into microsoft:main with commit b0ac393 Mar 4, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

5 participants