{"record":{"id":"fc0951d105f1590d","repo":"Hmbown/CodeWhale","slug":"unknown-mcp-tool-name-prefixed-name","errorCode":null,"errorMessage":"Unknown MCP tool name: {prefixed_name}","messagePattern":"Unknown MCP tool name: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":3043,"sourceCode":"            for tool in connection.tools() {\n                if !connection.config().is_tool_enabled(&tool.name)\n                    || format!(\"{server}_{}\", tool.name) != rest\n                {\n                    continue;\n                }\n                if matched.is_some() {\n                    anyhow::bail!(\n                        \"Ambiguous MCP tool name '{prefixed_name}' matches more than one server/tool authority\"\n                    );\n                }\n                matched = Some((server.clone(), tool.name.clone()));\n            }\n        }\n        if let Some(matched) = matched {\n            return Ok(matched);\n        }\n\n        Err(anyhow::anyhow!(\"Unknown MCP tool name: {prefixed_name}\"))\n    }\n\n    /// Resolve an MCP tool through an exact advertised catalog. A configured\n    /// but lazy server may be connected and asked for `tools/list`; the\n    /// requested suffix is never treated as authority on its own.\n    async fn resolve_advertised_tool(&mut self, prefixed_name: &str) -> Result<McpToolRoute> {\n        if let Ok((server_name, tool_name)) = self.parse_prefixed_name(prefixed_name) {\n            return self.capture_tool_route(server_name, tool_name);\n        }\n        let Some(rest) = prefixed_name.strip_prefix(\"mcp_\") else {\n            anyhow::bail!(\"Invalid MCP tool name: {prefixed_name}\");\n        };\n        let mut candidates = {\n            let dynamic = self.dynamic_servers.read();\n            self.config\n                .servers\n                .iter()\n                .filter_map(|(name, config)| {","sourceCodeStart":3025,"sourceCodeEnd":3061,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L3025-L3061","documentation":"parse_prefixed_name's fallback: the name had the mcp_ prefix (and passed the shape check) but no connected, catalog-authorized server advertises a tool whose composed name equals the remainder, considering per-tool enablement (is_tool_enabled). It means 'well-formed but unknown' — distinct from the invalid-prefix and ambiguous cases.","triggerScenarios":"Dispatching to a tool from a stale catalog after the server was removed or renamed; the tool exists on the server but is disabled via tool-level config (is_tool_enabled false); the connection exists but its reviewed-plugin catalog is not current (catalog_authorized false) so it is skipped; simple typos in the server or tool portion.","commonSituations":"Model uses a tool name remembered from an earlier turn after a config reload removed that server; tool filtered out by allow/deny tool lists in config; plugin catalog updated so the connection is skipped during matching; misspelled suffix.","solutions":["Compare the requested name against the current advertised list (all_tools / to_api_tools) and use an exact entry.","If the tool is tool-level disabled, enable it in the server's tool config or stop calling it.","Force a reconnect/refresh so lazy or stale servers publish their catalogs, then retry.","Fix typos in the server or tool portion of the name."],"exampleFix":"# before\n# config removed server 'jira', model still calls mcp_jira_search\nlet route = pool.resolve_advertised_tool(\"mcp_jira_search\").await?; // Unknown MCP tool name\n\n# after\n# re-add the server, or call a tool that is currently advertised:\nlet route = pool.resolve_advertised_tool(\"mcp_github_search\").await?;","handlingStrategy":"validation","validationCode":"// Rust: confirm the exact name is currently advertised\nlet advertised: Vec<String> = pool.all_tools().into_keys().collect();\nensure!(\n    advertised.iter().any(|n| n == requested),\n    \"tool '{requested}' not advertised; available: {}\",\n    advertised.join(\", \")\n);","typeGuard":"// Rust\nfn is_advertised_mcp_tool(pool: &McpPool, name: &str) -> bool {\n    name.starts_with(\"mcp_\") && pool.all_tools().contains_key(name)\n}","tryCatchPattern":"// Rust: unknown tool — refresh and re-check once\nmatch pool.resolve_advertised_tool(name).await {\n    Err(e) if e.to_string().contains(\"Unknown MCP tool name\") => {\n        pool.connect_all().await; // refresh lazy catalogs\n        pool.resolve_advertised_tool(name).await\n    }\n    r => r,\n}","preventionTips":["Pass only names copied from the live advertised catalog (all_tools/to_api_tools).","Check tool-level enablement: a disabled tool is invisible to the matcher even if the server lists it.","After removing/renaming servers, expect stale names to fail this way — regenerate tool lists the model sees."],"tags":["mcp","tools","catalog","lookup"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}