Bridge the gap between your IdP and the MCP World
So you've got your corporate IdP (Keycloak, Auth0, Okta, Azure AD, whatever) and now you want your MCP servers to use it for auth. You point Claude Code or Cursor at it, aaand... things break. Scope explosions on the consent screen, missing PKCE defaults, clients demanding Dynamic Client Registration your IdP doesn't serve the way MCP expects. Sound familiar? The MCP Authorization spec expects certain OAuth behaviors that most enterprise IdPs don't provide out of the box: MCP clients expect open Dynamic Client Registration. Most IdPs either don't expose it or lock it behind admin credentials. MCP clients tend to request all announced scopes. This isn't required by the spec -- it's just how most clients (Claude Code, Cursor, others) behave in practice. They read scopes_supported from discovery and request all of them. Your Keycloak announces 15 internal scopes? Congrats, users now see a consent screen from hell -- or the request gets rejected outright if client is not pre-approved for some announced scope. Many clients add offline_access unconditionally. Again, not a spec requirement -- just a common client behavior to ensure they get refresh tokens. This becomes a problem when your IdP restricts long-lived refresh tokens or requires client pre-approval for that scope. Discovery metadata needs filtering. IdPs expose dozens of OIDC fields (CIBA, device flow, logout endpoints...) that are irrelevant noise for MCP and can confuse clients. You could customize your IdP, but that's a maintenance rabbit hole -- especially when you need the same IdP for non-MCP and legacy apps. mcp-auth-adapter is a thin, stateless Node.js proxy that sits between your MCP clients and your existing IdP. It doesn't issue tokens or handle authentication -- all the real work stays on your IdP. It just makes the OAuth dance MCP-compatible. Filtered discovery -- Serves /.well-known/ IdP metadata with only the fields MCP clients actually need. Injects safe defaults (PKCE S256, authorization_code grant) when your IdP's metadata is incomplete, and own functionality where needed. Open DCR endpoint -- POST /register hands out a fixed, pre-configured client_id so MCP clients can self-register. No IdP-side DCR needed. Scope filtering -- Control what scopes reach your IdP. Strip offline_access, remove internal scopes, or use an allowlist: # Only these scopes will ever reach the upstream IdP MCP_PROXY_AUTH_SCOPES_PRESERVED=openid,api.read,api.write CIMD support (experimental) -- The MCP spec defaults to Client ID Metadata Documents for client identification, but it's an emerging IETF draft (not yet RFC) and no major IdP supports it natively today. This adapter bridges that gap -- it accepts CIMD-style client_id URLs from MCP clients, validates the metadata documents, and maps them to real upstream client_ids your IdP understands. So you get CIMD compatibility without waiting for your IdP vendor to implement it. Observability built in -- Prometheus metrics at /metrics, structured logging, health probes for k8s. Grab the container image and go: docker run -d --name mcp-auth-adapter \ -p 3000:3000 \ -e MCP_BASE_URL=https://mcp-auth.example.com \ -e MCP_UPSTREAM_SSO_URL=https://sso.example.com/auth/realms/external \ -e MCP_PROXY_DCR_CLIENT_ID=mcp-client \ ghcr.io/velias/mcp-auth-adapter:latest That's it. Three env vars for a basic setup: MCP_BASE_URL -- the public URL where this adapter lives MCP_UPSTREAM_SSO_URL -- your IdP's issuer URL MCP_PROXY_DCR_CLIENT_ID -- a public client pre-registered at your IdP Then point your MCP server's authorization_servers to MCP_BASE_URL and clients will discover everything via .well-known. For a real deployment you'll probably want scope control too: MCP_BASE_URL=https://mcp-auth.example.com MCP_UPSTREAM_SSO_URL=https://sso.example.com/auth/realms/external MCP_PROXY_DCR_CLIENT_ID=mcp-client MCP_WELL_KNOWN_SCOPES_SUPPORTED=openid,api.read,api.write MCP_PROXY_AUTH_SCOPES_REMOVED=offline_access This controls both sides: what clients see in discovery and what actually reaches your IdP. This adapter is intentionally minimal: No token issuing -- tokens come from your IdP, always No user database -- stateless, nothing to back up No rate limiting -- put it behind your existing reverse proxy / WAF No CORS -- designed for redirect-based flows, not browser fetch calls If you're running MCP servers (or planning to) and have an existing OAuth/OIDC provider, this saves you from bending your IdP config to accommodate MCP client quirks. Works with Keycloak, Auth0, Okta, Azure AD/Entra, Google Identity -- anything that serves standard OIDC discovery. Tested with Claude Code, Claude Desktop, Cursor IDE, Cursor Agent, Gemini CLI, VS Code, and more. MCP is moving fast, and auth is one of those things that should just work but often doesn't when you try to connect real-world IdPs to real-world MCP clients. Instead of fighting your IdP config or waiting for vendors to catch up with emerging standards like CIMD, drop a lightweight adapter in between and move on to building the actual MCP tools your users care about. If you hit rough edges or have ideas, open an issue -- the project is young and feedback shapes the roadmap. Apache 2.0 licensed. PRs welcome. GitHub: github.com/velias/mcp-auth-adapter
