How to Handle Tool Name Conflicts Between MCP Tools and Dify Built-in Tools
The junjiem/dify-plugin-agent-mcp_sse plugin automatically resolves collisions by prefixing conflicting MCP tool names with their server identifier and a double underscore, ensuring unique entries in the internal tool registry.
When integrating external MCP (Model Context Protocol) servers into Dify agents, you may need to handle tool name conflicts when MCP tools have the same name as Dify built-in tools. The junjiem/dify-plugin-agent-mcp_sse repository solves this by implementing an automatic deduplication strategy in the MCP client that renames conflicting tools before they reach the agent's vocabulary. This approach allows both native Dify capabilities and third-party MCP tools to coexist without manual configuration or naming conventions.
How the MCP Client Detects and Resolves Conflicts
The conflict resolution logic resides in utils/mcp_client.py, specifically within the McpClients._iter_tools method (lines 71-78). As the client iterates over tools returned by an MCP server, it maintains a flat dictionary called _tool_actions that serves as the single source of truth for all available tools.
Conflict Detection in the Tool Registry
During initialization, the client checks each incoming tool name against the existing keys in self._tool_actions. This registry contains both Dify's native tools and any previously registered MCP tools. If the name already exists, the client identifies a collision that must be resolved to prevent routing ambiguity.
Automatic Renaming with Server Prefix
When a conflict is detected, the client automatically rewrites the tool identifier by prepending the server name and a double underscore (__). For example, a tool named search from a server named my_mcp becomes my_mcp__search.
# utils/mcp_client.py – lines 71-78
if name in self._tool_actions: # <-- conflict detected
name = f"{server_name}__{name}" # <-- rename with server prefix
self._tool_actions[name] = ToolAction(
tool_name=name,
server_name=server_name,
action_type=ActionType.TOOL,
action_feature=tool,
)
This deterministic renaming ensures reproducible identifiers, meaning the same tool name always resolves to the same fully-qualified key regardless of client reinitialization.
Runtime Resolution in Function-Calling Agents
The agent strategy implemented in strategies/function_calling.py (lines 84-106) resolves tool calls by performing exact lookups in _tool_actions. Because renamed MCP tools carry the {server}__ prefix while built-in Dify tools use their original names, the agent can unambiguously route requests to either the native implementation or the appropriate MCP server.
When the LLM emits a function call, it uses the renamed identifier (e.g., my_mcp__search). The agent queries _tool_actions with this key and forwards the request via the MCP client, leaving Dify's native search tool untouched for other operations.
Handling Resources and Prompts
The same conflict-resolution strategy applies to MCP resources and prompts to ensure complete namespace isolation. The methods _iter_resources (lines 86-98) and _iter_prompts (lines 49-57) in utils/mcp_client.py implement identical prefixing logic. When these capabilities are dynamically converted to tools, they are checked against _tool_actions and prefixed with their server name if conflicts exist with existing tools or other MCP servers.
Practical Implementation Examples
Fetching Tools with Automatic Conflict Handling
When initializing McpClients, duplicate names are renamed internally without requiring manual intervention.
from utils.mcp_client import McpClients
# Configuration containing a 'search' tool that collides with Dify's built-in search
servers_cfg = {
"my_mcp": {
"url": "https://mcp.example.com/api",
"transport": "sse"
}
}
clients = McpClients(servers_cfg)
all_tools = clients.fetch_tools()
# Output contains "my_mcp__search" if "search" conflicts with a Dify tool
print(all_tools)
Invoking Renamed Tools from an Agent
When using FunctionCallingAgentStrategy, the renamed tool names are transparently passed to the LLM in the function-calling payload.
from strategies.function_calling import FunctionCallingAgentStrategy
params = {
"query": "Find AI research papers",
"model": {"provider": "openai", "name": "gpt-4"},
"tools": ["search"], # Dify built-in tool
"mcp_servers_config": '{"mcpServers": {"academic_mcp": {"url": "https://scholar.example.com/mcp"}}}',
"maximum_iterations": 3,
}
agent = FunctionCallingAgentStrategy()
for msg in agent.invoke(params):
# Tool calls use names like "academic_mcp__search" when conflicts exist
print(msg)
Customizing the Naming Separator
If the default double-underscore separator does not fit your naming conventions, you can modify the prefixing logic in utils/mcp_client.py (line 73).
# Override in utils/mcp_client.py
if name in self._tool_actions:
# Custom separator example
name = f"{server_name}--{name}"
After reloading the plugin, the new format (e.g., my_mcp--search) applies to all subsequent tool registrations.
Summary
- Automatic detection: The
McpClientsclass checks_tool_actionsduring tool iteration in_iter_tools(lines 71-78) to identify duplicate names. - Server-prefix renaming: Conflicting tools are renamed to
{server_name}__{tool_name}to ensure global uniqueness across all MCP servers and Dify built-ins. - Transparent routing: The function-calling agent in
strategies/function_calling.py(lines 84-106) resolves tool calls using the renamed identifiers, routing requests without ambiguity. - Extended coverage: Resources and prompts receive identical treatment via
_iter_resources(lines 86-98) and_iter_prompts(lines 49-57), preventing collisions across all MCP capabilities.
Frequently Asked Questions
How does the plugin detect conflicts between MCP tools and Dify built-in tools?
The plugin maintains a flat dictionary called _tool_actions in McpClients that registers every available tool. When iterating over tools from an MCP server in _iter_tools (lines 71-78), it checks if the tool name already exists as a key in this registry. If the name exists—whether from a previous MCP server or a Dify native tool—the conflict is detected immediately before the tool is registered.
Can I customize the separator between server and tool names?
Yes. While the default implementation in utils/mcp_client.py (line 73) uses a double underscore (__) in the f-string f"{server_name}__{name}", you can modify this to use any separator you prefer, such as a single dash or dot. This change affects all subsequent tool, resource, and prompt registrations after the plugin reloads.
Does this conflict resolution work for MCP resources and prompts as well?
Yes. The same renaming logic is applied in McpClients._iter_resources (lines 86-98) and McpClients._iter_prompts (lines 49-57). When resources or prompts are converted to tools, they are checked against _tool_actions and prefixed with their server name if conflicts exist with built-in Dify tools or other MCP capabilities.
What happens if two different MCP servers expose tools with identical names?
The first server to register a tool name claims the plain identifier in _tool_actions. When the second server attempts to register the same name, the plugin detects the conflict in _iter_tools and prefixes the second tool with its server name (e.g., server2__tool_name). Both tools remain accessible via their respective fully-qualified names, allowing multiple MCP servers to provide semantically similar tools without collision.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →