Hmbown/CodeWhale · error · anyhow::Error

Ambiguous MCP tool name '{prefixed_name}' matches more than

Error message

Ambiguous MCP tool name '{prefixed_name}' matches more than one server/tool authority

What it means

parse_prefixed_name iterates every catalog-authorized connection and collects every (server, tool) pair whose composed name format!("{server}_{tool}") equals the suffix after mcp_. If more than one pair matches, the name is ambiguous and resolution aborts rather than silently picking one — e.g. server 'a' with tool 'b_c' and server 'a_b' with tool 'c' both produce mcp_a_b_c.

Source

Thrown at crates/tui/src/mcp.rs:3032

    /// Parse a prefixed name into (server_name, tool_name)
    pub(crate) fn parse_prefixed_name(&self, prefixed_name: &str) -> Result<(String, String)> {
        let Some(rest) = prefixed_name.strip_prefix("mcp_") else {
            anyhow::bail!("Invalid MCP tool name: {prefixed_name}");
        };

        let mut matched: Option<(String, String)> = None;
        for (server, connection) in &self.connections {
            if !connection.catalog_authorized() {
                continue;
            }
            for tool in connection.tools() {
                if !connection.config().is_tool_enabled(&tool.name)
                    || format!("{server}_{}", tool.name) != rest
                {
                    continue;
                }
                if matched.is_some() {
                    anyhow::bail!(
                        "Ambiguous MCP tool name '{prefixed_name}' matches more than one server/tool authority"
                    );
                }
                matched = Some((server.clone(), tool.name.clone()));
            }
        }
        if let Some(matched) = matched {
            return Ok(matched);
        }

        Err(anyhow::anyhow!("Unknown MCP tool name: {prefixed_name}"))
    }

    /// Resolve an MCP tool through an exact advertised catalog. A configured
    /// but lazy server may be connected and asked for `tools/list`; the
    /// requested suffix is never treated as authority on its own.
    async fn resolve_advertised_tool(&mut self, prefixed_name: &str) -> Result<McpToolRoute> {
        if let Ok((server_name, tool_name)) = self.parse_prefixed_name(prefixed_name) {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Rename one of the colliding servers (or the colliding tool on the server side) so the composed mcp_{server}_{tool} strings differ.
  2. Remove duplicate registrations — check both static config and dynamic_servers for the same underlying server/tool.
  3. After renaming, refresh the model-facing catalog so stale names disappear.
  4. Avoid server names that are prefixes of other server names plus underscore boundaries when tools share suffixes.

Example fix

# before (.mcp.json) — both produce mcp_db_backup_list
{"servers": {"db": {"command": "m1"}, "db_backup": {"command": "m2"}}}
# db exposes tool 'backup_list'; db_backup exposes tool 'list'

# after
{"servers": {"db": {"command": "m1"}, "backup": {"command": "m2"}}}
# composed names: mcp_db_backup_list vs mcp_backup_list — unambiguous
Defensive patterns

Strategy: validation

Validate before calling

// Rust: detect ambiguity before dispatching
fn unique_owner(pool: &McpPool, rest: &str) -> Option<(String, String)> {
    let mut hits = pool.connections().iter()
        .filter(|(_, c)| c.catalog_authorized())
        .flat_map(|(s, c)| c.tools().iter().filter(move |t| format!("{s}_{}", t.name) == rest).map(move |t| (s.clone(), t.name.clone())))
        .collect::<Vec<_>>();
    (hits.len() == 1).then(|| hits.pop().unwrap())
}
ensure!(unique_owner(&pool, rest).is_some(), "ambiguous or unknown tool");

Try / catch

// Rust: surface both colliding owners to the operator
match pool.parse_prefixed_name(name) {
    Err(e) if e.to_string().contains("Ambiguous MCP tool name") => {
        // report the colliding (server, tool) pairs so one can be renamed
        bail!("{e:#}; rename a server or tool so names differ")
    }
    r => r,
}

Prevention

When it happens

Trigger: Two servers whose names/tool names concatenate to the same string (server 'db' + tool 'backup_list' vs server 'db_backup' + tool 'list'); duplicate server registrations in static and dynamic registries exposing the same tool; a dynamic server registered under a name that prefixes another server's name.

Common situations: Registering the same MCP server twice under different names (one static, one dynamic); renaming servers without checking composed-name collisions; plugin bundles that add servers whose names collide with user-configured ones.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/4ce222924f345e41. Report an issue: GitHub.