Hmbown/CodeWhale · error · anyhow::Error

Invalid MCP tool name: {prefixed_name}

Error message

Invalid MCP tool name: {prefixed_name}

What it means

parse_prefixed_name strips the literal prefix 'mcp_' from a tool name and returns (server, tool) for the unique advertised catalog match. A name that does not start with mcp_ — including empty strings, bare tool names, or names with a different prefix — fails here before any catalog lookup. This is the entry validation for the model-facing tool naming scheme where every MCP tool is exposed as mcp_{server}_{tool}.

Source

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

        let global_timeouts = self.config.timeouts;
        let conn = self.get_or_connect(server_name).await?;
        if !conn
            .prompts()
            .iter()
            .any(|prompt| prompt.name == prompt_name)
        {
            anyhow::bail!(
                "MCP prompt '{prompt_name}' was not advertised by server '{server_name}'"
            );
        }
        let timeout = conn.config().effective_execute_timeout(&global_timeouts);
        conn.get_prompt(prompt_name, arguments, timeout).await
    }

    /// 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"
                    );
                }

View on GitHub (pinned to 8880682c63)

Solutions

  1. Prefix the name: use mcp_{server}_{tool} exactly as advertised by the tool catalog (all_tools/to_api_tools).
  2. Route non-mcp_ names to the local tool dispatcher instead of the MCP resolver.
  3. Validate names at the boundary (before dispatch) with a simple starts_with check.
  4. Regenerate or re-read the advertised tool list to copy exact names.

Example fix

// before
let (server, tool) = pool.parse_prefixed_name("github_create_issue")?; // Err

// after
let (server, tool) = pool.parse_prefixed_name("mcp_github_create_issue")?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: check the prefix before resolution
ensure!(name.starts_with("mcp_"), "'{name}' is not an MCP tool (missing mcp_ prefix)");
let (server, tool) = pool.parse_prefixed_name(name)?;

Type guard

// Rust
fn is_mcp_tool_name(name: &str) -> bool {
    name.strip_prefix("mcp_").is_some_and(|rest| !rest.is_empty() && rest.contains('_'))
}

Try / catch

// Rust: route non-MCP names elsewhere
match pool.parse_prefixed_name(name) {
    Err(e) if e.to_string().contains("Invalid MCP tool name") => dispatch_local_tool(name).await,
    r => r,
}

Prevention

When it happens

Trigger: Calling a tool-resolution or dispatch API with a name like 'github_create_issue' (missing the mcp_ prefix), 'mcp' with nothing after, or a non-MCP tool name routed into the MCP resolver by mistake.

Common situations: The model omits the prefix when composing tool calls; callers forward user-typed tool names unvalidated; refactors that rename tools and drop the prefix; mixing up local tool names with MCP-prefixed ones.

Related errors


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