How the Dify MCP SSE Plugin Handles Tool Name Collisions Across Multiple MCP Servers

The Dify MCP SSE plugin automatically resolves tool name collisions by prefixing duplicate tool names with their originating server identifier using the format server_name__tool_name, ensuring every tool maintains a unique key in the internal registry.

When integrating multiple Model Context Protocol (MCP) servers into Dify workflows, tool name collisions are inevitable. The junjiem/dify-plugin-tools-mcp_sse repository implements a deterministic deduplication strategy that prevents ambiguous routing while maintaining transparency about tool origins.

The Collision Resolution Mechanism

The plugin's collision handling occurs during the tool discovery phase within the McpClients class. When fetch_tools() retrieves tool definitions from configured servers, it maintains an internal registry called self._tool_actions that tracks all registered tool names.

How fetch_tools() Handles Duplicates

During initialization, the McpClients.__init__ method prepares the client to aggregate tools from multiple sources. The fetch_tools() method then iterates through each server's tool list and applies the following logic:

  1. Extract the original tool name from tool["name"]
  2. Check if this name already exists in self._tool_actions
  3. If a collision is detected, rewrite the name as f"{server_name}__{original_name}"
  4. Store the tool in the action map using the unique name

This implementation appears in utils/mcp_client.py:


# utils/mcp_client.py

# Inside McpClients.fetch_tools()

for tool in tools:
    name = tool["name"]
    if name in self._tool_actions:
        name = f"{server_name}__{name}"          # ← collision handling

    self._tool_actions[name] = ToolAction(...)

According to the source code at lines 73-75, this logic ensures that the first server to register a tool name retains the original identifier, while subsequent servers with identical names receive prefixed variants.

Technical Implementation Details

Consistent Naming Across Tool Types

The collision resolution strategy extends beyond basic tools to include resources and prompts when they are exposed as tools. This consistency ensures that regardless of the MCP capability type, the naming scheme remains predictable and prevents namespace pollution across different server configurations.

Deterministic Routing Benefits

The prefixing strategy provides three critical advantages for Dify workflow stability:

  • Unambiguous Resolution: When a workflow invokes serverB__search, the plugin routes the request exclusively to serverB, eliminating any ambiguity about which server should handle the operation.
  • Safety Guarantees: Users cannot accidentally trigger tools from the wrong server due to naming overlaps, preventing unintended side effects in multi-server environments.
  • Debugging Transparency: The double-underscore separator (__) clearly indicates tool provenance in logs and UI displays, simplifying troubleshooting when identical tools exist across different MCP servers.

Practical Example: Listing Tools with Collisions

Consider a scenario where two MCP servers both expose a tool named "search". The following code demonstrates how the mcp_list_tools utility surfaces these tools with unique identifiers:

from dify_plugin.entities.tool import ToolInvokeMessage
from dify_plugin.tools import mcp_list_tools

# Two MCP servers configured in servers_config:

#   - serverA exposes a tool named "search"

#   - serverB also exposes a tool named "search"

list_tool = mcp_list_tools.McpListTools()
for msg in list_tool.invoke({
    "servers_config": {
        "serverA": {"url": "http://localhost:8001", "transport": "sse"},
        "serverB": {"url": "http://localhost:8002", "transport": "sse"},
    },
    "resources_as_tools": False,
    "prompts_as_tools": False,
}):
    print(msg.content)   # → contains both "search" and "serverB__search"

Output Behavior:

  • The first processed server retains the original name: search
  • The second server's tool is automatically renamed to: serverB__search

As implemented in junjiem/dify-plugin-tools-mcp_sse, this transformation occurs transparently during the tool fetch operation, ensuring that downstream components in tools/mcp_call_tool.py always receive unique tool identifiers for invocation.

Key Source Files for Collision Logic

Understanding the deduplication implementation requires examining three core files:

File Purpose
utils/mcp_client.py Contains the McpClients class and the fetch_tools() method where collision detection and prefixing logic resides (lines 73-75).
tools/mcp_list_tools.py Implements the Dify tool that lists available MCP tools, relying on the deduplicated registry populated by McpClients.fetch_tools().
tools/mcp_call_tool.py Handles tool execution by looking up tools in the deduplicated self._tool_actions registry, ensuring requests route to the correct server.

These components work together to guarantee that name clashes are resolved deterministically, preserving the stability of multi-server MCP integrations within Dify workflows.

Summary

  • Automatic Prefixing: The plugin detects duplicate tool names in self._tool_actions and automatically prefixes subsequent duplicates with server_name__ to ensure uniqueness.
  • First-come Priority: The first server to register a tool name retains the original identifier, while colliding tools from other servers receive prefixed names.
  • Comprehensive Coverage: The deduplication logic applies uniformly to tools, resources, and prompts exposed as tools through MCP servers.
  • Deterministic Routing: Prefixed names enable unambiguous tool invocation routing, preventing accidental cross-server execution when identical tool names exist across multiple MCP servers.

Frequently Asked Questions

How does the plugin decide which server keeps the original tool name?

The plugin processes servers in the order they appear in the configuration. The first server to register a specific tool name retains the original identifier in self._tool_actions. When subsequent servers attempt to register tools with identical names, the collision detection logic in utils/mcp_client.py triggers the prefixing mechanism, rewriting those names as server_name__original_name.

Does the prefixing strategy apply to MCP resources and prompts as well?

Yes, the same collision resolution strategy applies to resources and prompts when they are exposed as tools. The fetch_tools() method treats all MCP capabilities uniformly, ensuring that any name collisions across servers—regardless of whether they originate from tools, resources, or prompts—are resolved using the server_name__ prefixing convention.

What separator does the plugin use between the server name and tool name?

The plugin uses a double underscore (__) as the separator when constructing prefixed tool names. Specifically, the format is f"{server_name}__{original_name}" as implemented in the collision handling code within McpClients.fetch_tools(). This separator provides clear visual distinction between the server identifier and the original tool name in Dify's interface and logs.

Can two different servers expose tools with the same prefixed name?

No, the prefixing mechanism prevents this scenario. Since the server name itself is part of the unique key, and server names must be unique within the servers_config configuration object, the resulting prefixed name server_name__tool_name is guaranteed to be unique. The plugin only prefixes when the base name already exists, creating a deterministic hierarchy that avoids nested collision scenarios.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →