BerriAI/litellm · error · HTTPException

MCP server {server_id} not found

Error message

MCP server {server_id} not found

What it means

Returned (404) by the shared OAuth helper used by the /server/oauth/{server_id}/... browser endpoints (authorize, token, callback) when the id resolves nowhere: not in the temporary-server cache, not in the registry by id, and not in the registry by name. Temporary session servers live in Redis only for TEMPORARY_MCP_SERVER_TTL_SECONDS, so expiry is a first-class cause.

Source

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

    async def _get_cached_temporary_mcp_server_or_404(
        server_id: str,
        user_api_key_dict: UserAPIKeyAuth,
        request: Request | None = None,
    ) -> MCPServer:
        server = await get_cached_temporary_mcp_server(server_id)
        resolved_from_temp_cache: Final = server is not None
        if server is None:
            # Fall back to real DB/config server (e.g. for the user-side OAuth flow
            # which calls these endpoints with a real server_id, not a temp session id).
            from litellm.proxy.auth.ip_address_utils import IPAddressUtils

            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))

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. If you were using a temporary session server, redo the OAuth session setup to mint a fresh temp id and complete the flow promptly.
  2. For the user-side OAuth flow, pass the real (DB/config) server_id, which does not expire.
  3. Double-check the id for typos and confirm the server still exists via the list endpoint.
Defensive patterns

Strategy: validation

Validate before calling

resp = requests.get(f"{PROXY}/v1/mcp/server", headers=AUTH)
ids = {s["server_id"] for s in resp.json()["servers"]}
# temp session ids are admin-flow artifacts; verify freshness before use
if server_id not in ids and not server_id.startswith("temp-"):
    raise ValueError(f"unknown server id: {server_id}")

Try / catch

try:
    authorize(server_id)
except HTTPError as e:
    if e.response.status_code == 404:
        session = create_oauth_session()  # mint a fresh temp id (TTL ~5 min)
        authorize(session["server_id"])
    else:
        raise

Prevention

When it happens

Trigger: Starting the OAuth authorize/token flow with a temp session server_id that already expired (TTL ~5 minutes elapsed between setup and authorize); using a server_id that was never created; typo'd id; server deleted between setup and use.

Common situations: Admin sets up an OAuth-connected MCP server, gets distracted, then completes the browser consent after the temp cache expired; browser tab left open on an authorize URL whose session id has since expired; mixing up the temp session id with the permanent server_id.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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