{"record":{"id":"4bd289978af6db05","repo":"Hmbown/CodeWhale","slug":"qualified-mcp-tool-name-qualified-name-is-ambiguous-between","errorCode":null,"errorMessage":"qualified MCP tool name '{qualified_name}' is ambiguous between {}","messagePattern":"qualified MCP tool name '(.+?)' is ambiguous between (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/lib.rs","lineNumber":416,"sourceCode":"        for (server_name, (_, filter)) in &self.configs {\n            let Some(client) = self.clients.get(server_name) else {\n                continue;\n            };\n            let tools = client.list_tools_with_input_schemas()?;\n            for (tool, input_schema) in tools {\n                if !allowed_by_filter(&tool.tool_name, filter) {\n                    continue;\n                }\n                let qualified_name = qualify_tool_name(server_name, &tool.tool_name);\n                if let Some((prior_server, prior_tool)) = qualified_origins.get(&qualified_name)\n                    && (prior_server != server_name || prior_tool != &tool.tool_name)\n                {\n                    let mut origins = [\n                        format!(\"{prior_server}:{prior_tool}\"),\n                        format!(\"{server_name}:{}\", tool.tool_name),\n                    ];\n                    origins.sort();\n                    bail!(\n                        \"qualified MCP tool name '{qualified_name}' is ambiguous between {}\",\n                        origins.join(\" and \")\n                    );\n                }\n                qualified_origins.insert(\n                    qualified_name.clone(),\n                    (server_name.clone(), tool.tool_name.clone()),\n                );\n                out.push((\n                    McpToolDescriptor {\n                        server_name: server_name.clone(),\n                        tool_name: tool.tool_name,\n                        qualified_name,\n                        description: tool.description,\n                    },\n                    input_schema,\n                ));\n            }","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/lib.rs#L398-L434","documentation":"Raised by `list_tools_with_input_schemas` (crates/mcp/src/lib.rs) while building the cross-server tool listing: two different servers each expose a tool that qualifies to the same `mcp__<server-segment>__<tool>` name (their sanitized server components collide), so one qualified name maps to two origins. The library sorts both origins ('prior_server:prior_tool' and 'server:tool') and reports the ambiguity instead of picking one.","triggerScenarios":"Calling `list_tools` when two registered server names sanitize to the same component (e.g. 'fs-local' and 'fs.local') and both expose same-named tools, or when duplicate servers with colliding sanitized names are registered through separate paths.","commonSituations":"Multiple config sources each registering similarly named filesystem/server MCP instances; case or punctuation variants of a server name across environments; a renamed legacy server left registered alongside its replacement.","solutions":["Rename one of the colliding servers so sanitized names are unique (see the register_server collision error).","Unregister the redundant server before listing.","Apply a ToolFilter scoped to one server so the ambiguous qualified name has a single origin."],"exampleFix":"// before: 'fs-local' and 'fs.local' both sanitize to 'fs'\n{\"fs-local\": {...}, \"fs.local\": {...}}\n// after\n{\"fs-local\": {...}, \"fs-remote\": {...}}","handlingStrategy":"validation","validationCode":"// Rust: detect cross-server qualified collisions before list_tools\nlet mut seen = std::collections::HashMap::new();\nfor t in manager.list_tools()? {\n    anyhow::ensure!(seen.insert(qualify_tool_name(&t.server, &t.tool_name), t.server.clone()).is_none(),\n        \"cross-server collision on {}\", t.tool_name);\n}","typeGuard":"fn no_cross_server_collision(tools: &[McpToolDescriptor]) -> bool {\n    let mut q = std::collections::HashSet::new();\n    tools.iter().all(|t| q.insert(qualify_tool_name(&t.server, &t.tool_name)))\n}","tryCatchPattern":"match manager.list_tools() {\n    Err(e) if e.to_string().contains(\"is ambiguous between\") => {\n        // parse both origins from the message; unregister or rename one server\n    }\n    other => other?,\n}","preventionTips":["Keep sanitized server names globally unique across all config sources.","Run a startup lint that registers servers and lists tools before exposing them to the model.","Remove legacy servers when their renamed replacements land."],"tags":["mcp","naming-collision","tool-listing"],"backgroundTag":"tool-namespace-collision","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}