{"record":{"id":"bb61e882f0d94dec","repo":"rohitg00/ai-engineering-from-scratch","slug":"canonical-collision-canonical-name","errorCode":null,"errorMessage":"canonical collision: {canonical_name}","messagePattern":"canonical collision: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py","lineNumber":520,"sourceCode":"            if peer.available:\n                result = self._request(peer, \"tools/list\", {})\n                peer.tools = sorted(result.get(\"tools\", []), key=lambda tool: tool[\"name\"])\n\n    def merge(self, policy: str = \"prefix-on-collision\") -> None:\n        if policy not in {\"prefix-on-collision\", \"reject\"}:\n            raise ValueError(\"policy must be prefix-on-collision or reject\")\n        self.registry.clear()\n        for peer_name in sorted(self.peers):\n            peer = self.peers[peer_name]\n            for tool in peer.tools:\n                local_name = tool[\"name\"]\n                canonical_name = local_name\n                if canonical_name in self.registry:\n                    if policy == \"reject\":\n                        continue\n                    canonical_name = f\"{peer.name}/{local_name}\"\n                    if canonical_name in self.registry:\n                        raise ValueError(f\"canonical collision: {canonical_name}\")\n                self.registry[canonical_name] = MergedTool(\n                    canonical_name=canonical_name,\n                    peer_name=peer.name,\n                    local_name=local_name,\n                    description=tool.get(\"description\", \"\"),\n                )\n        self.registry = dict(sorted(self.registry.items()))\n\n    def call(self, canonical_name: str, arguments: dict[str, Any]) -> dict[str, Any]:\n        merged = self.registry.get(canonical_name)\n        if merged is None:\n            return {\n                \"resultType\": \"complete\",\n                \"content\": [{\"type\": \"text\", \"text\": f\"Unknown tool: {canonical_name}\"}],\n                \"isError\": True,\n            }\n        peer = self.peers[merged.peer_name]\n        if not peer.available:","sourceCodeStart":502,"sourceCodeEnd":538,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py#L502-L538","documentation":"Under prefix-on-collision, a tool name that already exists in the registry is retried as '{peer.name}/{local_name}', and even that prefixed name is taken. This happens when two tools on the same peer (or an earlier prefix) collide, or one peer's name makes its prefixed tool equal another canonical entry, so no unique name can be produced.","triggerScenarios":"policy == 'prefix-on-collision', canonical_name is already registered, f'{peer.name}/{local_name}' is also already in self.registry.","commonSituations":"Duplicate tool names within one peer's tools/list; a peer literally named such that peer.name/tool collides with an existing canonical name; peers exposing overlapping prefixed namespaces.","solutions":["Inspect which tool names collide by dumping peer.tools per peer","Rename the duplicated tools on the server side","Use policy='reject' to skip collisions instead of failing","Change the colliding peer's name so prefixed names are unique"],"exampleFix":"// before\nclient.merge(\"prefix-on-collision\")  # raises on double collision\n\n// after\nclient.merge(\"reject\")  # colliding tools are skipped, merge completes","handlingStrategy":"fallback","validationCode":"seen = {}\nfor name in sorted(p.name for p in client.peers.values()):\n    for tool in tools_of(name):\n        for candidate in (tool, f'{name}/{tool}'):\n            if candidate in seen:\n                rename_or_reject(candidate)","typeGuard":"null","tryCatchPattern":"try:\n    client.merge()\nexcept ValueError as e:\n    if \"canonical collision\" in str(e):\n        client.merge('reject')  # skip colliding tools and proceed","preventionTips":["Enforce unique tool names per server","Choose peer names that cannot collide under prefixing","Default to 'reject' in environments with untrusted tool catalogs"],"tags":["mcp","name-collision","registry"],"backgroundTag":"duplicate-key-collision","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}