tinyhumansai/openhuman · error · anyhow::Error

MCP tool `{tool}` is not allowed for server `{}`

Error message

MCP tool `{tool}` is not allowed for server `{}`

What it means

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.

Source

Thrown at src/openhuman/mcp/config_servers/registry.rs:222

            .get(server)
            .ok_or_else(|| anyhow::anyhow!("unknown MCP server `{server}`"))?;
        let tools = server.client.list_tools().await?;
        let safe = apply_safety_filter(&server.name, tools);
        Ok(server.filter_allowed_tools(safe))
    }

    pub async fn call_tool(
        &self,
        server: &str,
        tool: &str,
        arguments: Value,
    ) -> anyhow::Result<McpServerToolResult> {
        let server = self
            .get(server)
            .ok_or_else(|| anyhow::anyhow!("unknown MCP server `{server}`"))?;
        let tool = tool.trim();
        if !server.is_tool_allowed(tool) {
            anyhow::bail!(
                "MCP tool `{tool}` is not allowed for server `{}`",
                server.name
            );
        }
        server.client.call_tool(tool, arguments).await
    }

    pub async fn initialize(&self, server: &str) -> anyhow::Result<McpInitializeResult> {
        let server = self
            .get(server)
            .ok_or_else(|| anyhow::anyhow!("unknown MCP server `{server}`"))?;
        server.client.initialize().await
    }

    pub async fn discover_authorization(
        &self,
        server: &str,
    ) -> anyhow::Result<Option<McpAuthorizationContext>> {

View on GitHub (pinned to 7491200858)

Solutions

  1. Check the server's `allowed_tools` / `disallowed_tools` in `[[mcp_client.servers]]` and add the tool name you intend to call.
  2. List what is actually permitted via the server's `list_tools` (already filtered by `filter_allowed_tools`) and call only those names.
  3. If the block is unintended, remove the tool from `disallowed_tools`.
  4. Verify the exact spelling/case of the tool name — matching is exact string equality after trim.

Example fix

# before (config.toml)
[[mcp_client.servers.my-server]]
allowed_tools = ["search"]

# calling fetch -> "MCP tool `fetch` is not allowed"

# after
[[mcp_client.servers.my-server]]
allowed_tools = ["search", "fetch"]
# or drop allowed_tools entirely to permit every tool not explicitly disallowed
Defensive patterns

Strategy: validation

Validate before calling

let server = registry.get(server_name)
    .ok_or_else(|| anyhow::anyhow!("unknown MCP server `{server_name}`"))?;
anyhow::ensure!(
    server.is_tool_allowed(tool),
    "tool '{tool}' blocked by allowed_tools/disallowed_tools for '{server_name}'"
);

Prevention

When it happens

Trigger: `[[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).

Common situations: 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.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/ee9d61e4c639fa58. Report an issue: GitHub.