{"record":{"id":"378c7d414c211e6a","repo":"Hmbown/CodeWhale","slug":"qualified-mcp-tool-name-qualified-tool-name-is-ambiguous","errorCode":null,"errorMessage":"qualified MCP tool name '{qualified_tool_name}' is ambiguous within server '{server_name}'","messagePattern":"qualified MCP tool name '(.+?)' is ambiguous within server '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/lib.rs","lineNumber":312,"sourceCode":"        server_name: &str,\n        tool_segment: &str,\n        qualified_tool_name: &str,\n    ) -> Result<String> {\n        let Some(client) = self.clients.get(server_name) else {\n            return Ok(tool_segment.to_string());\n        };\n        let Ok(tools) = client.list_tools() else {\n            return Ok(tool_segment.to_string());\n        };\n        let filter = self.configs.get(server_name).map(|(_, filter)| filter);\n        let mut matches = tools.iter().filter(|tool| {\n            filter.is_none_or(|filter| allowed_by_filter(&tool.tool_name, filter))\n                && qualify_tool_name(server_name, &tool.tool_name) == qualified_tool_name\n        });\n        match (matches.next(), matches.next()) {\n            (Some(tool), None) => Ok(tool.tool_name.clone()),\n            (None, _) => Ok(tool_segment.to_string()),\n            (Some(_), Some(_)) => bail!(\n                \"qualified MCP tool name '{qualified_tool_name}' is ambiguous within server '{server_name}'\"\n            ),\n        }\n    }\n\n    /// Start all registered servers, emitting status updates via the callback.\n    ///\n    /// Returns a summary of which servers are ready, failed, or cancelled.\n    pub fn start_all<F>(&self, mut emit: F) -> McpStartupCompleteEvent\n    where\n        F: FnMut(McpStartupUpdateEvent),\n    {\n        let mut ready = Vec::new();\n        let mut failed = Vec::new();\n        let mut cancelled = Vec::new();\n        for (server_name, (cfg, _)) in &self.configs {\n            if !cfg.enabled {\n                emit(McpStartupUpdateEvent {","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/lib.rs#L294-L330","documentation":"Raised by `resolve_original_tool_name` (crates/mcp/src/lib.rs) during `call_qualified_tool`: more than one tool within a single server's tool list qualifies to the same qualified name `mcp__<server>__<tool>`, so the library cannot tell which underlying tool the caller meant. It refuses to guess and returns the ambiguity error.","triggerScenarios":"Calling `call_qualified_tool` when one server exposes two tools whose names sanitize to the same component (e.g. 'run.tests' and 'run_tests'), making `qualify_tool_name` match multiple entries in the filtered tool list.","commonSituations":"An MCP server publishes tools with names differing only by punctuation/case after sanitization; a server upgrade renames tools into a collision; the caller hand-built the qualified string from a stale tool listing.","solutions":["Fix or configure the MCP server so its tool names sanitize to distinct components.","List the server's tools (`list_tools`) to see the conflicting names, then call the intended one by its unique unqualified name.","Filter tools (ToolFilter) so only one of the colliding tools is visible, making resolution unambiguous.","If a server upgrade caused it, pin or roll back the server version until names are unique again."],"exampleFix":"// before: server exposes 'run.tests' and 'run_tests' -> both qualify as mcp__srv__run_tests\ncall_qualified_tool(\"srv\", \"mcp__srv__run_tests\", input)\n// after: distinct tool names on the server\ncall_qualified_tool(\"srv\", \"mcp__srv__run_tests\", input) // with 'run.tests' renamed server-side","handlingStrategy":"validation","validationCode":"// Rust: ensure the qualified name is unique within the server before calling\nlet target = \"mcp__srv__run_tests\";\nlet matches: Vec<_> = manager.list_tools()?.into_iter()\n    .filter(|t| t.server == \"srv\" && qualify_tool_name(\"srv\", &t.tool_name) == target)\n    .collect();\nanyhow::ensure!(matches.len() == 1, \"ambiguous tool: {}\", target);","typeGuard":"fn resolves_uniquely(server: &str, qualified: &str, tools: &[McpToolDescriptor]) -> bool {\n    tools.iter().filter(|t| qualify_tool_name(server, &t.tool_name) == qualified).count() == 1\n}","tryCatchPattern":"match manager.call_qualified_tool(server, qualified, input) {\n    Err(e) if e.to_string().contains(\"is ambiguous within server\") => {\n        // list tools, pick the exact unqualified name, retry\n    }\n    other => other?,\n}","preventionTips":["Keep tool names within one server unique after sanitizing (no case/punctuation-only differences).","Build qualified names from list_tools output, never by hand-concatenation.","Use ToolFilter to hide deprecated duplicates when a server publishes both."],"tags":["mcp","naming-collision","tool-resolution"],"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"}