{"record":{"id":"b88e78b906749484","repo":"Hmbown/CodeWhale","slug":"missing-mcp-prefix","errorCode":null,"errorMessage":"missing mcp__ prefix","messagePattern":"missing mcp__ prefix","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/mcp/src/lib.rs","lineNumber":564,"sourceCode":"        let remaining = component_budget - server_len - tool_len;\n        if remaining > 0 {\n            let server_extra = (server.len() - server_len).min(remaining);\n            server_len += server_extra;\n            tool_len += (tool.len() - tool_len).min(remaining - server_extra);\n        }\n        name = format!(\n            \"mcp__{}__{}{}\",\n            &server[..server_len],\n            &tool[..tool_len],\n            suffix\n        );\n    }\n    name\n}\n\nfn parse_qualified_tool_name(value: &str) -> Result<(String, String)> {\n    let Some(stripped) = value.strip_prefix(\"mcp__\") else {\n        bail!(\"missing mcp__ prefix\");\n    };\n    let mut split = stripped.splitn(2, \"__\");\n    let server = split\n        .next()\n        .filter(|s| !s.is_empty())\n        .map(ToOwned::to_owned)\n        .context(\"missing server segment\")?;\n    let tool = split\n        .next()\n        .filter(|s| !s.is_empty())\n        .map(ToOwned::to_owned)\n        .context(\"missing tool segment\")?;\n    Ok((server, tool))\n}\n\n#[derive(Debug, Deserialize)]\nstruct JsonRpcRequest {\n    #[serde(default)]","sourceCodeStart":546,"sourceCodeEnd":582,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/mcp/src/lib.rs#L546-L582","documentation":"parse_qualified_tool_name() requires the string to start with the literal prefix 'mcp__'; the qualified format is mcp__<server>__<tool>. This bail (and the sibling 'missing server segment'/'missing tool segment' errors) means the caller passed a bare tool name, a server__tool pair without the prefix, or a name built by hand with the wrong shape.","triggerScenarios":"Passing a bare tool name ('search_code') or 'github__search_code' to call_qualified_tool(); constructing qualified names by string concatenation instead of using the crate's qualify helper; stripping or lowercasing the prefix during logging/round-tripping so 'MCP__' or 'mcp_' no longer matches.","commonSituations":"Tool names round-tripped through user input, logs, or prompts where the prefix was trimmed; LLM-emitted tool calls missing the prefix; copy-paste between systems that use different qualification conventions.","solutions":["Prefix the name: pass mcp__<server>__<tool> with non-empty server and tool segments","Build qualified names with the crate's formatting helper rather than manual concatenation","For bare names, call call_tool(server, tool, args) directly instead of the qualified API"],"exampleFix":"// before\nlet v = registry.call_qualified_tool(\"github__search_code\", args)?; // missing prefix\n\n// after\nlet v = registry.call_qualified_tool(\"mcp__github__search_code\", args)?;","handlingStrategy":"validation","validationCode":"fn is_qualified_tool_name(s: &str) -> bool {\n    let Some(rest) = s.strip_prefix(\"mcp__\") else { return false };\n    match rest.split_once(\"__\") {\n        Some((server, tool)) => !server.is_empty() && !tool.is_empty(),\n        None => false,\n    }\n}\n\nif !is_qualified_tool_name(&name) { bail!(\"expected mcp__<server>__<tool>, got {name}\"); }","typeGuard":"fn split_qualified(s: &str) -> Option<(&str, &str)> {\n    s.strip_prefix(\"mcp__\")?.split_once(\"__\")\n        .filter(|(srv, tool)| !srv.is_empty() && !tool.is_empty())\n}","tryCatchPattern":null,"preventionTips":["Validate the shape at the boundary where names enter (user input, LLM tool calls, config)","Generate names with qualify-style helpers instead of string concatenation","Use call_tool(server, tool) for bare names; reserve call_qualified_tool for genuinely qualified ones"],"tags":["mcp","qualified-name","format-validation","prefix"],"backgroundTag":"malformed-qualified-name","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}