odysseus-dev/odysseus · error · HTTPException

Server has no OAuth config

Error message

Server has no OAuth config

What it means

On GET /oauth/authorize/{server_id}, the server row exists but srv.oauth_config is empty/null, so there is no OAuth configuration to drive the flow and the route returns 400. The field is populated at registration time from the oauth_file/oauth_config form parameters, so this means the server was registered without OAuth credentials.

Source

Thrown at routes/mcp/mcp_routes.py:438

            db.commit()

            return {"id": server_id, "disabled_count": len(disabled)}
        finally:
            db.close()

    # ── OAuth flow for Google MCP servers ──────────────────────────

    @router.get("/oauth/authorize/{server_id}")
    def oauth_authorize(server_id: str, request: Request):
        """Show OAuth authorization page with Google sign-in link."""
        require_admin(request)
        db = SessionLocal()
        try:
            srv = db.query(McpServer).filter(McpServer.id == server_id).first()
            if not srv:
                raise HTTPException(404, "Server not found")
            if not srv.oauth_config:
                raise HTTPException(400, "Server has no OAuth config")

            oauth_cfg = _sanitize_mcp_oauth_config(json.loads(srv.oauth_config))
            keys_file = oauth_cfg.get("keys_file", "")
            if not keys_file or not os.path.exists(keys_file):
                raise HTTPException(400, "OAuth keys file not found")

            with open(keys_file, encoding="utf-8") as f:
                keys_data = json.load(f)
            keys = keys_data.get("installed") or keys_data.get("web")
            if not keys:
                raise HTTPException(400, "Invalid OAuth keys file format")

            client_id = keys["client_id"]
            scopes = oauth_cfg.get("scopes", [])

            # For Desktop App creds, default to localhost — the user will
            # paste the resulting URL back if they're on a different device.
            redirect_uri = _mcp_oauth_redirect_uri()

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Re-register or update the server with an oauth_file/oauth_config under the mcp_oauth base dir.
  2. Confirm registration succeeded with OAuth fields by inspecting the server row (GET /servers) before calling authorize.
  3. Only call authorize for servers that genuinely need Google auth.

Example fix

# before: registered without oauth
requests.post(f"{base}/servers", data={"transport": "http", "url": u, "name": "gdrive"})

# after
requests.post(f"{base}/servers", data={"transport": "http", "url": u, "name": "gdrive",
    "oauth_file": "client_secret.json"})
Defensive patterns

Strategy: validation

Validate before calling

def server_has_oauth(srv_row: dict) -> bool:
    return bool(srv_row.get("oauth_config"))

Try / catch

On 400 'Server has no OAuth config', re-register the server with oauth_file instead of retrying authorize.

Prevention

When it happens

Trigger: Registering a server with transport sse/http and no oauth_file/oauth_config, then hitting authorize; registering with an oauth_file whose path failed confinement (error 641) so the config never persisted; a stdio server that never carries OAuth.

Common situations: Assuming every listed server supports Google OAuth; OAuth config lost during registration due to path validation; server created before the OAuth feature existed.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/9b812a192b3e7eb4. Report an issue: GitHub.