Real OAuth for MCP: how "Authorize" replaces pasting tokens
claude.ai and ChatGPT expect an OAuth flow, not a token in the URL. Here is the small OAuth 2.1 server we built for the gateway — discovery, dynamic client registration, PKCE.
If you paste a gateway URL into ChatGPT and leave auth on OAuth, it fails: "MCP server does not implement OAuth". The client is trying to discover an OAuth config that isn't there. So we built one.
The moving parts
The MCP authorization spec is standard OAuth 2.1:
- Discovery —
/.well-known/oauth-protected-resourcepoints the client at the authorization server;/.well-known/oauth-authorization-serverlists the endpoints. A401from the gateway carries aWWW-Authenticateheader so clients find it automatically. - Dynamic Client Registration (RFC 7591) — claude.ai / ChatGPT register themselves and get a
client_id. No manual setup. - Authorize + consent — the user signs into Unyly and approves.
- Token exchange with PKCE — the code is swapped for a bearer token.
The trick that kept it simple
The issued access token is a per-user gateway token. So the gateway's existing Authorization: Bearer check authenticates it unchanged — no second auth path to maintain.
One caveat
Session cookies are host-only, so the authorization server lives on unyly.org while the resource is gateway.unyly.org/mcp. Standard split — resource server ≠ authorization server — and it sidesteps cross-subdomain cookie pain.
Now you just pick OAuth and click Authorize. Try it.