Skip to content

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

Merged
Giles Odigwe (giles17) merged 4 commits into
microsoft:mainfrom
giles17:mcp-tool-dedup
Mar 4, 2026
Merged

Python: Fix MCP tools duplicated on second turn when runtime tools are present#4432
Giles Odigwe (giles17) merged 4 commits into
microsoft:mainfrom
giles17:mcp-tool-dedup

Conversation

@giles17

@giles17Giles Odigwe (giles17) commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

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.
Fixesmicrosoft#4381
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 3, 2026 19:12
@markwallace-microsoftMark Wallace (markwallace-microsoft) added the python Usage: [Issues, PRs], Target: Python label Mar 3, 2026
@github-actionsgithub-actionsBot changed the title Fix MCP tools duplicated on second turn when runtime tools are presentPython: Fix MCP tools duplicated on second turn when runtime tools are presentMar 3, 2026
@markwallace-microsoft

Mark Wallace (markwallace-microsoft) commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

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

TestsSkippedFailuresErrorsTime
458125 💤0 ❌0 🔥1m 22s ⏱️

CopilotAI 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

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.

FileDescription
python/packages/core/agent_framework/_agents.pyAdds existing_names set before extending from self.mcp_tools, skipping functions already present in final_tools
python/packages/core/tests/core/test_agents.pyNew 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.

Comment threadpython/packages/core/agent_framework/_agents.py Outdated
Comment threadpython/packages/core/tests/core/test_agents.py Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@giles17
Giles Odigwe (giles17) added this pull request to the merge queueMar 4, 2026
Merged via the queue into microsoft:main with commit b0ac393Mar 4, 2026
30 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pythonUsage: [Issues, PRs], Target: Python

Projects

None yet

5 participants

@giles17@markwallace-microsoft@dmytrostruk@moonbox3