Hmbown/CodeWhale · error · anyhow::Error

qualified MCP tool name '{qualified_tool_name}' is ambiguous

Error message

qualified MCP tool name '{qualified_tool_name}' is ambiguous across servers: {}

What it means

McpRegistry::call_qualified_tool() could not resolve the qualified name against an exact registered server, so it scanned every running server's tools for ones whose qualified name equals the request — and found two or more. Sanitization folds '-', '.', and case into '_', and the mcp__<server>__<tool> join uses '__', so different (server, tool) pairs can flatten to the same string (e.g. server 'a' tool 'b__c' vs server 'a_b' tool 'c' both yield mcp__a__b__c). The message lists the sorted server names that match; dispatch refuses to pick one because iteration order is not a choice.

Source

Thrown at crates/mcp/src/lib.rs:454

            }
        }
        match matches.len() {
            0 => {}
            1 => {
                let (server_name, tool_name) = &matches[0];
                let client = self
                    .clients
                    .get(*server_name)
                    .with_context(|| format!("MCP server '{server_name}' not available"))?;
                return client.call_tool(tool_name, arguments);
            }
            _ => {
                matches.sort();
                let servers: Vec<&str> = matches
                    .iter()
                    .map(|(server_name, _)| server_name.as_str())
                    .collect();
                bail!(
                    "qualified MCP tool name '{qualified_tool_name}' is ambiguous across servers: \
                     {}",
                    servers.join(", ")
                );
            }
        }

        let (server_name, tool_name) = parsed?;
        self.call_tool(&server_name, &tool_name, arguments)
    }

    /// List all resources from all running servers.
    pub fn list_resources(&self) -> Result<Vec<McpResourceDescriptor>> {
        let mut out = Vec::new();
        for server_name in self.configs.keys() {
            let Some(client) = self.clients.get(server_name) else {
                continue;
            };

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Use the exact registered server name in the server segment so the exact-registration path resolves without scanning
  2. Prefer call_tool(server, tool, args) with explicit names when you know the target server
  3. Rename one of the colliding servers or tools so their qualified names no longer flatten to the same string

Example fix

// before
let v = registry.call_qualified_tool("mcp__a__b__c", args)?; // matches a/b__c and a_b/c

// after: name the server exactly
let v = registry.call_tool("a_b", "c", args)?;
Defensive patterns

Strategy: validation

Validate before calling

// Prefer the unambiguous direct API when you know the server:
registry.call_tool("a_b", "c", args)?;

// If you must use qualified names, ensure the server segment exactly matches a registered name
// and the name was produced by the crate's qualify helper, not hand-built.

Type guard

fn is_exact_qualified_name(name: &str, registered: &[String]) -> bool {
    name.starts_with("mcp__")
        && name[5..].split_once("__").is_some_and(|(server, _)| {
            registered.iter().any(|r| r == server)
        })
}

Try / catch

match registry.call_qualified_tool(name, args) {
    Ok(v) => Ok(v),
    Err(err) if err.to_string().contains("is ambiguous across servers") => {
        // pick a server from the listed candidates and call it directly
        let server = pick_server_from_error(&err)?;
        registry.call_tool(&server, &tool, args)
    }
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: Passing a qualified name whose server segment does not exactly match a registered server name, forcing the scan path, while two servers expose tools that flatten to that same qualified name; servers whose names contain underscores combined with tools whose names contain '__'; duplicates of the same server registered under near-identical names.

Common situations: Hand-built qualified names copied from logs; tool catalogs where generic tool names ('run', 'search') exist on several servers and the caller dropped or mistyped the server segment; name changes after sanitization rules were tightened.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/37bb22a2a9576561. Report an issue: GitHub.