{"record":{"id":"ee9d61e4c639fa58","repo":"tinyhumansai/openhuman","slug":"mcp-tool-tool-is-not-allowed-for-server","errorCode":null,"errorMessage":"MCP tool `{tool}` is not allowed for server `{}`","messagePattern":"MCP tool `(.+?)` is not allowed for server `(.+?)`","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/mcp/config_servers/registry.rs","lineNumber":222,"sourceCode":"            .get(server)\n            .ok_or_else(|| anyhow::anyhow!(\"unknown MCP server `{server}`\"))?;\n        let tools = server.client.list_tools().await?;\n        let safe = apply_safety_filter(&server.name, tools);\n        Ok(server.filter_allowed_tools(safe))\n    }\n\n    pub async fn call_tool(\n        &self,\n        server: &str,\n        tool: &str,\n        arguments: Value,\n    ) -> anyhow::Result<McpServerToolResult> {\n        let server = self\n            .get(server)\n            .ok_or_else(|| anyhow::anyhow!(\"unknown MCP server `{server}`\"))?;\n        let tool = tool.trim();\n        if !server.is_tool_allowed(tool) {\n            anyhow::bail!(\n                \"MCP tool `{tool}` is not allowed for server `{}`\",\n                server.name\n            );\n        }\n        server.client.call_tool(tool, arguments).await\n    }\n\n    pub async fn initialize(&self, server: &str) -> anyhow::Result<McpInitializeResult> {\n        let server = self\n            .get(server)\n            .ok_or_else(|| anyhow::anyhow!(\"unknown MCP server `{server}`\"))?;\n        server.client.initialize().await\n    }\n\n    pub async fn discover_authorization(\n        &self,\n        server: &str,\n    ) -> anyhow::Result<Option<McpAuthorizationContext>> {","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/mcp/config_servers/registry.rs#L204-L240","documentation":"Thrown by `McpConfigRegistry::call_tool` when the target tool fails `McpServerDefinition::is_tool_allowed`: the tool is listed in the server's `disallowed_tools`, or the server has a non-empty `allowed_tools` allowlist that does not contain it. It is an operator-config policy rejection raised before any bytes are sent to the MCP server. Note the tool name is trimmed before matching, and an empty tool name is also rejected.","triggerScenarios":"`[[mcp_client.servers]]` config with `allowed_tools = [\"search\"]` while calling `fetch`; tool present in `disallowed_tools`; passing an empty/whitespace tool name; tool-name drift after the server renames a tool (old name no longer allowlisted).","commonSituations":"Tightened allowlist shipped in config after an agent prompt still advertises the old tool; typo in the allowlist entry; model/agent invoking a tool it saw advertised elsewhere but which this operator deliberately blocked; server-side rename makes the allowlist entry stale.","solutions":["Check the server's `allowed_tools` / `disallowed_tools` in `[[mcp_client.servers]]` and add the tool name you intend to call.","List what is actually permitted via the server's `list_tools` (already filtered by `filter_allowed_tools`) and call only those names.","If the block is unintended, remove the tool from `disallowed_tools`.","Verify the exact spelling/case of the tool name — matching is exact string equality after trim."],"exampleFix":"# before (config.toml)\n[[mcp_client.servers.my-server]]\nallowed_tools = [\"search\"]\n\n# calling fetch -> \"MCP tool `fetch` is not allowed\"\n\n# after\n[[mcp_client.servers.my-server]]\nallowed_tools = [\"search\", \"fetch\"]\n# or drop allowed_tools entirely to permit every tool not explicitly disallowed","handlingStrategy":"validation","validationCode":"let server = registry.get(server_name)\n    .ok_or_else(|| anyhow::anyhow!(\"unknown MCP server `{server_name}`\"))?;\nanyhow::ensure!(\n    server.is_tool_allowed(tool),\n    \"tool '{tool}' blocked by allowed_tools/disallowed_tools for '{server_name}'\"\n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive callable tool lists from the server's filtered tools/list instead of hardcoding names.","Keep allowed_tools in sync when a server renames tools (pin server versions).","Prefer a short disallow list over an exhaustive allow list unless policy demands it."],"tags":["mcp","allowlist","authorization","config"],"backgroundTag":"permission-denied","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}