{"record":{"id":"23ffbe244cb0ac4d","repo":"shareAI-lab/learn-claude-code","slug":"duplicate-mcp-tool-name-on-server-self-name-r-23ffbe","errorCode":null,"errorMessage":"Duplicate MCP tool name on server {self.name!r}","messagePattern":"Duplicate MCP tool name on server (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"s15_integrated_harness/code.py","lineNumber":2434,"sourceCode":"# -- MCP System --\n\n# MCP is modeled as late-bound tools: connect first, then discovered server\n# tools are merged into the normal tool pool with mcp__server__tool names.\nclass MCPClient:\n    \"\"\"Small in-process stand-in for MCP tools/list and tools/call.\"\"\"\n\n    def __init__(self, name: str):\n        self.name = name\n        self.tools: list[dict] = []\n        self._handlers: dict[str, callable] = {}\n\n    def register(self, tool_defs: list[dict],\n                 handlers: dict[str, callable]):\n        names = [tool.get(\"name\") for tool in tool_defs]\n        if any(not isinstance(name, str) or not name for name in names):\n            raise ValueError(\"Every MCP tool needs a non-empty name\")\n        if len(set(names)) != len(names):\n            raise ValueError(f\"Duplicate MCP tool name on server {self.name!r}\")\n        missing = [name for name in names if name not in handlers]\n        if missing:\n            raise ValueError(f\"Missing MCP handlers: {', '.join(missing)}\")\n        self.tools = list(tool_defs)\n        self._handlers = dict(handlers)\n\n    def call_tool(self, tool_name: str, args: dict) -> str:\n        handler = self._handlers.get(tool_name)\n        if not handler:\n            return f\"MCP error: unknown tool '{tool_name}'\"\n        try:\n            return str(handler(**args))\n        except Exception as exc:\n            return f\"MCP error: {type(exc).__name__}: {exc}\"\n\n\nmcp_clients: dict[str, MCPClient] = {}\n_DISALLOWED_CHARS = re.compile(r\"[^a-zA-Z0-9_-]\")","sourceCodeStart":2416,"sourceCodeEnd":2452,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s15_integrated_harness/code.py#L2416-L2452","documentation":"MCPClient.register() rejects a tool list containing the same name twice on one server. Handlers are keyed by name, so duplicates would silently bind all calls to whichever handler was inserted last; registration fails fast and names the offending server in the message.","triggerScenarios":"Calling register() with tool_defs where two entries share a 'name' — e.g. concatenating two tool lists with overlapping names, a copy-paste duplication while hand-building a test server, or a source server that genuinely advertises a duplicate (misconfigured gateway aggregating two plugins with the same tool name).","commonSituations":"Test doubles assembled by extending a list; merging tools from multiple upstream MCP servers into one client without namespacing; gateway config duplicating a plugin.","solutions":["De-duplicate tool_defs by name before register() (keep one, or namespace the names).","When merging servers, prefix each tool name with its source server to guarantee uniqueness.","Fix the upstream server/gateway so tools/list itself has unique names."],"exampleFix":"// before\ntools = read_tools + read_tools  # duplicate names\nclient.register(tools, handlers)\n\n// after\nseen, unique = set(), []\nfor t in tools:\n    if t[\"name\"] not in seen:\n        seen.add(t[\"name\"])\n        unique.append(t)\nclient.register(unique, handlers)","handlingStrategy":"validation","validationCode":"names = [t[\"name\"] for t in tool_defs if isinstance(t.get(\"name\"), str) and t[\"name\"]]\nassert len(names) == len(set(names)), f\"duplicate tool names: {names}\"\n\n# or de-duplicate, keeping first occurrence:\nseen, unique = set(), []\nfor t in tool_defs:\n    if t[\"name\"] not in seen:\n        seen.add(t[\"name\"])\n        unique.append(t)","typeGuard":"def names_unique(tool_defs) -> bool:\n    names = [t.get(\"name\") for t in tool_defs]\n    return len(set(names)) == len(names)","tryCatchPattern":"try:\n    client.register(tool_defs, handlers)\nexcept ValueError as e:\n    if \"Duplicate MCP tool name\" in str(e):\n        # namespace by server or drop dupes, then retry once\n        raise","preventionTips":["Namespace merged tool names by source server (server__tool).","De-duplicate lists before register().","Check aggregating gateways for plugins advertising the same tool name."],"tags":["mcp","tool-registration","duplicates","validation"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}