{"record":{"id":"2c095c9738e784d3","repo":"Hmbown/CodeWhale","slug":"invalid-mcp-tool-name-prefixed-name","errorCode":null,"errorMessage":"Invalid MCP tool name: {prefixed_name}","messagePattern":"Invalid MCP tool name: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":3017,"sourceCode":"        let global_timeouts = self.config.timeouts;\n        let conn = self.get_or_connect(server_name).await?;\n        if !conn\n            .prompts()\n            .iter()\n            .any(|prompt| prompt.name == prompt_name)\n        {\n            anyhow::bail!(\n                \"MCP prompt '{prompt_name}' was not advertised by server '{server_name}'\"\n            );\n        }\n        let timeout = conn.config().effective_execute_timeout(&global_timeouts);\n        conn.get_prompt(prompt_name, arguments, timeout).await\n    }\n\n    /// Parse a prefixed name into (server_name, tool_name)\n    pub(crate) fn parse_prefixed_name(&self, prefixed_name: &str) -> Result<(String, String)> {\n        let Some(rest) = prefixed_name.strip_prefix(\"mcp_\") else {\n            anyhow::bail!(\"Invalid MCP tool name: {prefixed_name}\");\n        };\n\n        let mut matched: Option<(String, String)> = None;\n        for (server, connection) in &self.connections {\n            if !connection.catalog_authorized() {\n                continue;\n            }\n            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                }","sourceCodeStart":2999,"sourceCodeEnd":3035,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L2999-L3035","documentation":"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}.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Prefix the name: use mcp_{server}_{tool} exactly as advertised by the tool catalog (all_tools/to_api_tools).","Route non-mcp_ names to the local tool dispatcher instead of the MCP resolver.","Validate names at the boundary (before dispatch) with a simple starts_with check.","Regenerate or re-read the advertised tool list to copy exact names."],"exampleFix":"// before\nlet (server, tool) = pool.parse_prefixed_name(\"github_create_issue\")?; // Err\n\n// after\nlet (server, tool) = pool.parse_prefixed_name(\"mcp_github_create_issue\")?;","handlingStrategy":"validation","validationCode":"// Rust: check the prefix before resolution\nensure!(name.starts_with(\"mcp_\"), \"'{name}' is not an MCP tool (missing mcp_ prefix)\");\nlet (server, tool) = pool.parse_prefixed_name(name)?;","typeGuard":"// Rust\nfn is_mcp_tool_name(name: &str) -> bool {\n    name.strip_prefix(\"mcp_\").is_some_and(|rest| !rest.is_empty() && rest.contains('_'))\n}","tryCatchPattern":"// Rust: route non-MCP names elsewhere\nmatch pool.parse_prefixed_name(name) {\n    Err(e) if e.to_string().contains(\"Invalid MCP tool name\") => dispatch_local_tool(name).await,\n    r => r,\n}","preventionTips":["Validate the mcp_ prefix at the dispatch boundary, not deep in the resolver.","Build tool names from the advertised catalog (mcp_{server}_{tool}) rather than by string concatenation at call sites.","Keep local (non-MCP) tool dispatch separate so prefixless names never reach this code."],"tags":["mcp","tools","naming","validation"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}