BerriAI/litellm · error · HTTPException

Access denied to MCP server {server_id}

Error message

Access denied to MCP server {server_id}

What it means

Returned (403) by the OAuth server-resolution helper when a non-admin-view caller references a server that was resolved from the temporary cache. Temporary servers come from the admin-only /server/oauth/session setup flow and are intentionally never exposed to non-admins, so any non-admin hitting them gets this blanket denial regardless of grants.

Source

Thrown at litellm/proxy/management_endpoints/mcp_management_endpoints.py:1806

            client_ip: Final = IPAddressUtils.get_mcp_client_ip(request) if request else None
            server = global_mcp_server_manager.get_mcp_server_by_id(
                server_id
            ) or global_mcp_server_manager.get_mcp_server_by_name(server_id, client_ip=client_ip)
        if server is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail={"error": f"MCP server {server_id} not found"},
            )

        # Per-server access policy mirrors `fetch_mcp_server`: admin-view
        # callers are unrestricted; non-admins must have the server in their
        # allowed-servers set. Temporary cached servers come from the
        # admin-only `/server/oauth/session` setup flow and are not exposed
        # to non-admins.
        if not _user_has_admin_view(user_api_key_dict):
            if resolved_from_temp_cache:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail={"error": f"Access denied to MCP server {server_id}"},
                )
            allowed_server_ids: Final[set[str]] = set()
            for auth_context in await build_effective_auth_contexts(user_api_key_dict):
                allowed_server_ids.update(await global_mcp_server_manager.get_allowed_mcp_servers(auth_context))
            if server.server_id not in allowed_server_ids:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail={"error": f"Access denied to MCP server {server_id}"},
                )
        return server

    @router.get(
        "/server/oauth/{server_id}/authorize",
        include_in_schema=False,
        dependencies=[Depends(_mcp_oauth_user_api_key_auth)],
    )

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Complete OAuth setup flows with the proxy admin/master key.
  2. For end users, register a permanent server first and run the user-side OAuth flow against its real server_id.
  3. Regenerate the temp session (it may also have expired) using an admin key.
Defensive patterns

Strategy: validation

Validate before calling

info = requests.get(f"{PROXY}/key/info", headers=AUTH, params={"key": KEY}).json()
role = info.get("key_info", info).get("user_role")
if role != "proxy_admin" and is_temp_session_id(server_id):
    raise PermissionError("temp session servers are admin-only; use an admin key")

Type guard

def is_temp_session_id(server_id: str) -> bool:
    return server_id.startswith("mcp-oauth-session-")  # match your deployment's temp id prefix

Try / catch

try:
    authorize(server_id)
except HTTPError as e:
    if e.response.status_code == 403:
        raise PermissionError("temp OAuth setup requires the admin key")
    raise

Prevention

When it happens

Trigger: Calling /server/oauth/{temp_session_id}/authorize (or /token) with a non-admin virtual key; a bookmarked OAuth URL containing a temp session id being opened by a regular user; sharing setup links with team members.

Common situations: Admin copies the OAuth setup URL and a teammate opens it with their own (non-admin) key; automation for the OAuth flow accidentally configured with a user-level key.

Understand the failure class

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/b659fea219dc9f9f. Report an issue: GitHub.