shareAI-lab/learn-claude-code · error · ValueError
Duplicate MCP tool name on server {self.name!r}
Error message
Duplicate MCP tool name on server {self.name!r} What it means
MCPClient.register() rejects tool_defs containing duplicate names, since the handlers dict is keyed by name and a duplicate would silently shadow one tool's handler. Uniqueness is checked per server before any tool is exposed.
Source
Thrown at s14_mcp_plugin/code.py:173
}
# -- New in s14: MCP discovery and dispatch --
class MCPClient:
"""Small in-process stand-in for MCP tools/list and tools/call."""
def __init__(self, name: str):
self.name = name
self.tools: list[dict] = []
self._handlers: dict[str, callable] = {}
def register(self, tool_defs: list[dict], handlers: dict[str, callable]):
names = [tool.get("name") for tool in tool_defs]
if any(not isinstance(name, str) or not name for name in names):
raise ValueError("Every MCP tool needs a non-empty name")
if len(set(names)) != len(names):
raise ValueError(f"Duplicate MCP tool name on server {self.name!r}")
missing = [name for name in names if name not in handlers]
if missing:
raise ValueError(f"Missing MCP handlers: {', '.join(missing)}")
self.tools = list(tool_defs)
self._handlers = dict(handlers)
def call_tool(self, tool_name: str, args: dict) -> str:
handler = self._handlers.get(tool_name)
if not handler:
return f"MCP error: unknown tool '{tool_name}'"
try:
return str(handler(**args))
except Exception as exc:
return f"MCP error: {type(exc).__name__}: {exc}"
mcp_clients: dict[str, MCPClient] = {}
mcp_tool_policies: dict[str, str] = {}View on GitHub (pinned to 985456f4ad)
Solutions
- Deduplicate by name before register(): keep the entry per name you actually want.
- Rename one of the colliding tools to reflect its distinct purpose.
- Add a unit test asserting tool name uniqueness per server.
Example fix
// before
server.register(docs_tools + docs_tools_v2, handlers) // both define 'search'
// after
merged = {t['name']: t for t in docs_tools + docs_tools_v2}
server.register(list(merged.values()), handlers) Defensive patterns
Strategy: validation
Validate before calling
def tool_names_unique(tool_defs: list[dict]) -> bool:
names = [t['name'] for t in tool_defs]
return len(set(names)) == len(names) Prevention
- Deduplicate merged tool lists by name before register().
- Add a unit test asserting per-server name uniqueness.
- When copy-pasting a tool def, rename it immediately.
When it happens
Trigger: register([{'name': 'search', ...}, {'name': 'search', ...}], handlers) — e.g. concatenating two tool lists that both define 'search', or copy-pasting a def and editing only its description.
Common situations: Merging tool catalogs from multiple modules; copy-paste tool authoring; regenerating defs where an old entry wasn't removed.
Related errors
- Every MCP tool needs a non-empty name
- Missing MCP handlers: {', '.join(missing)}
- MCP tool name is longer than 64 characters: {prefixed}
- Invalid input schema for {origin}
- Every MCP tool needs a non-empty name
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/6aff50bc7b4429d9.
Report an issue: GitHub.