Skip to content

Fix MCP tool name conflicts by adding server name prefixes #1178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions src/agents/mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,14 @@ async def list_tools(
# Reset the cache dirty to False
self._cache_dirty = False
# Fetch the tools from the server
self._tools_list = (await self.session.list_tools()).tools
tools = self._tools_list
tools = (await self.session.list_tools()).tools
# Add server name prefix to each tool's name to ensure global uniqueness
for tool in tools:
# Store original name for actual tool calls
tool.original_name = tool.name
# Prefix tool name with server name using underscore separator
tool.name = f"{self.name}_{tool.name}"
self._tools_list = tools

# Filter tools based on tool_filter
filtered_tools = tools
Expand All @@ -287,11 +293,23 @@ async def list_tools(
return filtered_tools

async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None) -> CallToolResult:
"""Invoke a tool on the server."""
"""Invoke a tool on the server.

Args:
tool_name: The name of the tool to call. This can be either the prefixed name (server_name_tool_name)
or the original tool name.
arguments: The arguments to pass to the tool.
"""
if not self.session:
raise UserError("Server not initialized. Make sure you call `connect()` first.")

return await self.session.call_tool(tool_name, arguments)
# If the tool name is prefixed with server name, strip it
if "_" in tool_name and tool_name.startswith(f"{self.name}_"):
original_tool_name = tool_name.split("_", 1)[1]
else:
original_tool_name = tool_name

return await self.session.call_tool(original_tool_name, arguments)

async def list_prompts(
self,
Expand Down
14 changes: 8 additions & 6 deletions src/agents/mcp/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ def to_function_tool(
on_invoke_tool=invoke_func,
strict_json_schema=is_strict,
)

@classmethod
async def invoke_mcp_tool(
cls, server: "MCPServer", tool: "MCPTool", context: RunContextWrapper[Any], input_json: str
Expand All @@ -178,21 +177,24 @@ async def invoke_mcp_tool(
f"Invalid JSON input for tool {tool.name}: {input_json}"
) from e

# Use original tool name for server call (strip server prefix if present)
original_name = getattr(tool, 'original_name', tool.name)

if _debug.DONT_LOG_TOOL_DATA:
logger.debug(f"Invoking MCP tool {tool.name}")
logger.debug(f"Invoking MCP tool {tool.name} (original: {original_name})")
else:
logger.debug(f"Invoking MCP tool {tool.name} with input {input_json}")
logger.debug(f"Invoking MCP tool {tool.name} (original: {original_name}) with input {input_json}")

try:
result = await server.call_tool(tool.name, json_data)
result = await server.call_tool(original_name, json_data)
except Exception as e:
logger.error(f"Error invoking MCP tool {tool.name}: {e}")
raise AgentsException(f"Error invoking MCP tool {tool.name}: {e}") from e

if _debug.DONT_LOG_TOOL_DATA:
logger.debug(f"MCP tool {tool.name} completed.")
logger.debug(f"MCP tool {tool.name} (original: {original_name}) completed.")
else:
logger.debug(f"MCP tool {tool.name} returned {result}")
logger.debug(f"MCP tool {tool.name} (original: {original_name}) returned {result}")

# The MCP tool result is a list of content items, whereas OpenAI tool outputs are a single
# string. We'll try to convert.
Expand Down
Loading
Loading