Skip to content

Detect and reject duplicate tool names within individual MCP servers#3561

Closed
mshsheikh wants to merge 1 commit into
openai:mainfrom
mshsheikh:patch-44
Closed

Detect and reject duplicate tool names within individual MCP servers#3561
mshsheikh wants to merge 1 commit into
openai:mainfrom
mshsheikh:patch-44

Conversation

@mshsheikh
Copy link
Copy Markdown
Contributor

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:

            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:

            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

## 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
@mshsheikh mshsheikh closed this Jun 1, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment thread src/agents/mcp/util.py
Comment on lines +280 to +284
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:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

@mshsheikh mshsheikh deleted the patch-44 branch June 1, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant