Detect and reject duplicate tool names within individual MCP servers#3561
Detect and reject duplicate tool names within individual MCP servers#3561mshsheikh wants to merge 1 commit into
Conversation
## Problem
The existing duplicate tool name validation only checked for conflicts across multiple MCP servers. It collected names from a single server using a set comprehension, which inherently discards duplicates. When a misconfigured or buggy MCP server returned multiple tools with identical names, the later entry would silently overwrite the earlier one. This caused non-deterministic tool registration, unpredictable agent behavior, and made it extremely difficult to trace the root cause of tool routing failures.
## Solution
Introduced an explicit intra-server duplicate detection step before the existing cross-server validation. The updated logic uses collections.Counter to track the frequency of each tool name returned by a server. If any name appears more than once, the system raises a UserError with a precise message identifying the offending server and the duplicated names. The original cross-server duplicate check remains fully intact and operates on the deduplicated set of names.
## Technical Implementation
- Replaced the initial set comprehension with a Counter to capture name frequencies per server response
- Added a filtered iteration to identify names with a count greater than one
- Introduced a targeted UserError for intra-server duplicates that includes the server name for immediate troubleshooting
- Converted the Counter keys back to a set to preserve the existing cross-server intersection logic
- Leveraged the already-imported collections.Counter, requiring zero new dependencies
- Maintains full backward compatibility with no changes to public method signatures, error types, or external APIs
## Code Changes
Location: MCPUtil.get_all_function_tools() inside the for server in servers: loop
### Before:
```python
server_tool_names = {tool.name for tool in server_tools}
duplicate_tool_names = sorted(server_tool_names & tool_names)
if duplicate_tool_names:
raise UserError(
"Duplicate tool names found across MCP servers: "
f"{', '.join(duplicate_tool_names)}"
)
```
### After:
```python
server_tool_name_counts = Counter(tool.name for tool in server_tools)
intra_server_duplicate_tool_names = sorted(
name for name, count in server_tool_name_counts.items() if count > 1
)
if intra_server_duplicate_tool_names:
raise UserError(
"Duplicate tool names found within MCP server "
f"'{server.name}': {', '.join(intra_server_duplicate_tool_names)}"
)
server_tool_names = set(server_tool_name_counts)
duplicate_tool_names = sorted(server_tool_names & tool_names)
if duplicate_tool_names:
raise UserError(
"Duplicate tool names found across MCP servers: "
f"{', '.join(duplicate_tool_names)}"
)
```
## Impact and Verification
- Prevents silent tool overwrites when an MCP server returns duplicate tool definitions
- Provides clear, actionable error messages that immediately identify the source of naming conflicts
- Preserves all existing cross-server validation behavior and error formatting
- Verified against the existing test suite, which passes without modification
- Introduces no new dependencies or runtime overhead
- Safe for immediate merge as a backward-compatible stability improvement
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 29a2b4f3b7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| server_tool_name_counts = Counter(tool.name for tool in server_tools) | ||
| intra_server_duplicate_tool_names = sorted( | ||
| name for name, count in server_tool_name_counts.items() if count > 1 | ||
| ) | ||
| if intra_server_duplicate_tool_names: |
There was a problem hiding this comment.
Reject intra-server duplicates when prefixing
This duplicate-name check only runs after the include_server_in_tool_names branch has already returned, so an MCP server that lists two tools with the same name is still accepted whenever an agent enables server-name prefixing. In that mode the two public tool names can be made unique, but invocation still uses server.call_tool(tool.name, ...), so both wrappers target the same underlying MCP name and the ambiguity this change is trying to reject remains.
Useful? React with 👍 / 👎.
Problem
The existing duplicate tool name validation only checked for conflicts across multiple MCP servers. It collected names from a single server using a set comprehension, which inherently discards duplicates. When a misconfigured or buggy MCP server returned multiple tools with identical names, the later entry would silently overwrite the earlier one. This caused non-deterministic tool registration, unpredictable agent behavior, and made it extremely difficult to trace the root cause of tool routing failures.
Solution
Introduced an explicit intra-server duplicate detection step before the existing cross-server validation. The updated logic uses collections.Counter to track the frequency of each tool name returned by a server. If any name appears more than once, the system raises a UserError with a precise message identifying the offending server and the duplicated names. The original cross-server duplicate check remains fully intact and operates on the deduplicated set of names.
Technical Implementation
Code Changes
Location: MCPUtil.get_all_function_tools() inside the for server in servers: loop
Before:
After:
Impact and Verification