BerriAI/litellm · error · HTTPException
User not allowed to call this tool.
Error message
User not allowed to call this tool.
What it means
Server-level authorization on the MCP tool-call path: after resolving which server owns the tool, LiteLLM checks that server's name against the caller's allowed MCP servers (derived from key/team/org mcp_servers grants). If the server is not in the allowed list, the call fails with HTTP 403 "User not allowed to call this tool." — enforced only when a server could be resolved.
Source
Thrown at litellm/proxy/_experimental/mcp_server/server.py:2734
"message": (
f"Tool '{name}' belongs to MCP server "
f"'{mcp_server.name}' but request specified "
f"server_id for '{requested_server.name}'."
),
},
)
if mcp_server is None:
mcp_server = requested_server
server_name = requested_server.name
original_tool_name = strip_known_server_prefix(name, requested_server)
# Only enforce server-level permissions when we can resolve a server
if server_name:
if not MCPRequestHandler.is_tool_allowed(
allowed_mcp_servers=[server.name for server in allowed_mcp_servers],
server_name=server_name,
):
raise HTTPException(
status_code=403,
detail="User not allowed to call this tool.",
)
standard_logging_mcp_tool_call: Final[StandardLoggingMCPToolCall] = _get_standard_logging_mcp_tool_call(
name=original_tool_name, # Use original name for logging
arguments=arguments,
server_name=server_name,
session_id=_mcp_session_id_from_headers(raw_headers),
)
litellm_logging_obj: Final[LiteLLMLoggingObj | None] = kwargs.get("litellm_logging_obj", None)
if litellm_logging_obj:
litellm_logging_obj.model_call_details["mcp_tool_call_metadata"] = standard_logging_mcp_tool_call
litellm_logging_obj.model = f"MCP: {name}"
litellm_logging_obj.model_call_details["model"] = f"MCP: {name}"
# Resolve the MCP server early so BYOK checks and credential injection
# apply to ALL dispatch paths (local tool registry AND managed MCP server).
if mcp_server is None:View on GitHub (pinned to 77b7c6c40c)
Solutions
- Use a key whose grants include the server that owns the tool.
- Admin: add the server (or a wildcard) to the key's or team's mcp_servers scope in the admin UI.
- Verify visibility first: GET /mcp-rest/tools/list with the same key shows exactly which servers/tools are permitted.
Defensive patterns
Strategy: validation
Validate before calling
async def tool_visible_to_key(client: httpx.AsyncClient, tool_name: str) -> bool:
resp = await client.get(f"{base}/mcp-rest/tools/list")
tools = resp.json().get("tools", [])
return any(t.get("name") == tool_name for t in tools)
if not await tool_visible_to_key(client, name):
raise PermissionError(f"key cannot access tool {name}; request grant first") Try / catch
except httpx.HTTPStatusError as e:
if e.response.status_code == 403 and "not allowed to call this tool" in e.response.text:
# scope problem, not transient: request access or switch key; do not retry
raise ToolNotPermitted(name) from e
raise Prevention
- List tools with the production key before wiring a tool into an agent — visibility equals permission.
- When admins change key/team mcp_servers scopes, re-run the tool-visibility check in CI.
When it happens
Trigger: A key scoped to specific mcp_servers calls a tool from an ungranted server; an unprefixed tool name resolves to a server outside the caller's grants.
Common situations: An admin narrows key or team scopes after an incident; a new MCP server is registered but never added to the team's grants; users moved between teams keep calling old tools.
Related errors
- forbidden
- access_denied
- Access denied to prompt version '{prompt_version_id}'. Check
- Access denied to file '{file_path}'. Check your BitBucket pe
- User does not have permission to test MCP server connections
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/883b86ede7b385f8.
Report an issue: GitHub.