BerriAI/litellm · error · HTTPException
access_denied
access_denied
Error message
The key is not allowed to access server {server_id} What it means
On the REST tool-call route, the server exists and is IP-accessible, but server_id is not in the union of MCP servers the caller's key is allowed to use, so the proxy returns 403 error=access_denied. Allowlists are inclusive: an explicit mcp_servers list on the key or team excludes everything not on it.
Source
Thrown at litellm/proxy/_experimental/mcp_server/rest_endpoints.py:487
detail={
"error": "ip_filtering",
"message": (
f"MCP server '{server_id}' is not accessible from your IP address "
f"({_rest_client_ip}). This server is restricted to internal "
"networks only. To make it externally accessible, set "
"'available_on_public_internet: true' in the server configuration."
),
},
)
if _server is None:
raise HTTPException(
status_code=404,
detail={
"error": "server_not_found",
"message": f"MCP server '{server_id}' was not found",
},
)
raise HTTPException(
status_code=403,
detail={
"error": "access_denied",
"message": f"The key is not allowed to access server {server_id}",
},
)
# Build allowed_mcp_servers list (only include allowed servers)
allowed_mcp_servers: Final[list[MCPServer]] = []
for allowed_server_id in allowed_server_ids_set:
server = global_mcp_server_manager.get_mcp_server_by_id(allowed_server_id)
if server is not None:
allowed_mcp_servers.append(server)
return allowed_mcp_servers, canonical_server_id
async def _get_tools_for_single_server(
server,View on GitHub (pinned to 77b7c6c40c)
Solutions
- Add the server_id to the key's mcp_servers list (/key/update or dashboard), or add it to the team's allowlist.
- Or remove the per-key mcp_servers restriction so the key inherits the general-settings server set.
- Verify with the key info endpoint (/key/info) that the server now appears in the key's allowed mcp_servers.
Defensive patterns
Strategy: validation
Validate before calling
async def key_allows_server(key_info: dict, server_id: str) -> bool:
allowed = key_info.get("mcp_servers")
return allowed is None or server_id in allowed # None = inherits general settings Try / catch
resp = await client.post(f"{proxy}/mcp/tool-call", json=payload, headers=headers)
if resp.status_code == 403 and resp.json().get("detail", {}).get("error") == "access_denied":
raise PermissionError(f"key not granted {payload['server_id']}; update key/team mcp_servers") from None
resp.raise_for_status() Prevention
- When rolling out a new MCP server, update every key/team allowlist that names servers explicitly.
- Check /key/info for the effective mcp_servers set before wiring new integrations.
- Prefer inheriting general settings over per-key allowlists unless least-privilege demands it.
When it happens
Trigger: Key or team carries an mcp_servers allowlist that omits this server_id; a personal key used against a server granted only to a team; a server rolled out after the key's allowlist was frozen.
Common situations: New MCP server added to general settings but keys keep old explicit allowlists; least-privilege keys scoped for one integration then reused for another; team grants vs key grants confusion.
Related errors
- forbidden
- User not allowed to call this tool.
- 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/e8ec820ec47f8c51.
Report an issue: GitHub.