{"record":{"id":"0f89344f42e71b68","repo":"Hmbown/CodeWhale","slug":"unknown-mcp-tool-name-name","errorCode":null,"errorMessage":"Unknown MCP tool name: {name}","messagePattern":"Unknown MCP tool name: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":2929,"sourceCode":"\n    pub(crate) fn tool_allowed(&self, name: &str) -> bool {\n        !crate::core::engine::tool_catalog::tool_matches_any_rule(&self.disallowed_tools, name)\n    }\n\n    fn require_server(&self, server: &str) -> Result<()> {\n        anyhow::ensure!(\n            self.server_allowed(server),\n            \"Failed to find MCP server: {server}\"\n        );\n        Ok(())\n    }\n\n    pub(crate) fn authorize_call(\n        rules: &[String],\n        name: &str,\n        input: &serde_json::Value,\n    ) -> Result<()> {\n        anyhow::ensure!(\n            !crate::core::engine::tool_catalog::tool_matches_any_rule(rules, name),\n            \"Unknown MCP tool name: {name}\"\n        );\n        if matches!(\n            name,\n            \"list_mcp_resources\"\n                | \"list_mcp_resource_templates\"\n                | \"mcp_read_resource\"\n                | \"read_mcp_resource\"\n                | \"mcp_get_prompt\"\n        ) && let Some(server) = input.get(\"server\").and_then(serde_json::Value::as_str)\n        {\n            anyhow::ensure!(\n                !Self::server_denied_by(rules, server),\n                \"Failed to find MCP server: {server}\"\n            );\n        }\n        Ok(())","sourceCodeStart":2911,"sourceCodeEnd":2947,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp.rs#L2911-L2947","documentation":"authorize_call() validates a proposed MCP tool name against the caller-supplied rules; if tool_matches_any_rule(rules, name) matches, the name is on a deny list and the call is rejected with \"Unknown MCP tool name\". The message is intentionally opaque so untrusted input cannot distinguish a denied tool from a nonexistent one.","triggerScenarios":"A tools/call (or resource/prompt access) whose tool name matches a deny rule in the rules slice passed to authorize_call — including model-supplied tool names that were not in the approved catalog.","commonSituations":"The model hallucinates or miscases a tool name; config deny-lists a tool that a client still tries to call; a renamed tool's old name is still being called.","solutions":["Call only tool names returned by the server's tools/list for the session.","Check the tool name against the configured deny rules before dispatching.","Fix casing/spelling — matching is rule-based and exact for plain names.","If the tool should be callable, remove it from the deny rules in config."],"exampleFix":"// before\nMcpConnection::authorize_call(&rules, \"shell_exec\", &input)?;\n// after: use a catalogued name\nlet name = catalog.resolve(\"shell\", \"exec\")?; // canonical name\nMcpConnection::authorize_call(&rules, &name, &input)?;","handlingStrategy":"validation","validationCode":"// Only dispatch names present in the session's tool catalog\nlet allowed: std::collections::HashSet<&str> = catalog.tools.iter().map(|t| t.name.as_str()).collect();\nassert!(allowed.contains(name.as_str()), \"tool not in catalog: {name}\");","typeGuard":"fn is_catalogued(catalog: &ToolCatalog, name: &str) -> bool {\n    catalog.tools.iter().any(|t| t.name == name)\n}","tryCatchPattern":"match McpConnection::authorize_call(&rules, &name, &input) {\n    Err(e) if e.to_string().contains(\"Unknown MCP tool name\") => {\n        eprintln!(\"tool '{name}' is denied or unknown; pick from tools/list\");\n    }\n    other => other?,\n}","preventionTips":["Always source tool names from the current tools/list response.","Keep the deny rules and the advertised catalog in sync after config edits.","Normalize casing of tool names before calling.","Remove old tool names from callers after a server tool rename."],"tags":["mcp","authorization","deny-list"],"backgroundTag":"permission-denied","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}