<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vlastimil Elias</title>
    <description>The latest articles on DEV Community by Vlastimil Elias (@velias).</description>
    <link>https://dev.to/velias</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3933314%2F2e21561c-bfcf-4ed5-a823-8b5c60c1e630.png</url>
      <title>DEV Community: Vlastimil Elias</title>
      <link>https://dev.to/velias</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/velias"/>
    <language>en</language>
    <item>
      <title>Bridge the gap between your IdP and the MCP World</title>
      <dc:creator>Vlastimil Elias</dc:creator>
      <pubDate>Fri, 15 May 2026 14:23:40 +0000</pubDate>
      <link>https://dev.to/velias/bridge-the-gap-between-your-idp-and-the-mcp-world-3gbn</link>
      <guid>https://dev.to/velias/bridge-the-gap-between-your-idp-and-the-mcp-world-3gbn</guid>
      <description>&lt;p&gt;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?&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization" rel="noopener noreferrer"&gt;MCP Authorization spec&lt;/a&gt; expects certain OAuth behaviors that most enterprise IdPs don't provide out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP clients expect open Dynamic Client Registration.&lt;/strong&gt; Most IdPs either don't expose it or lock it behind admin credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP clients tend to request all announced scopes.&lt;/strong&gt; This isn't required by the spec - it's just how most clients (Claude Code, Cursor, others) behave in practice. They read &lt;code&gt;scopes_supported&lt;/code&gt; from discovery and request &lt;em&gt;all of them&lt;/em&gt;. 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Many clients add &lt;code&gt;offline_access&lt;/code&gt; unconditionally.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discovery metadata needs filtering.&lt;/strong&gt; IdPs expose dozens of OIDC fields (CIBA, device flow, logout endpoints...) that are irrelevant noise for MCP and can confuse clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter mcp-auth-adapter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/velias/mcp-auth-adapter" rel="noopener noreferrer"&gt;&lt;strong&gt;mcp-auth-adapter&lt;/strong&gt;&lt;/a&gt; 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.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Filtered discovery&lt;/strong&gt; - Serves &lt;code&gt;/.well-known/&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open DCR endpoint&lt;/strong&gt; - &lt;code&gt;POST /register&lt;/code&gt; hands out a fixed, pre-configured &lt;code&gt;client_id&lt;/code&gt; so MCP clients can self-register. No IdP-side DCR needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope filtering&lt;/strong&gt; - Control what scopes reach your IdP. Strip &lt;code&gt;offline_access&lt;/code&gt;, remove internal scopes, or use an allowlist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Only these scopes will ever reach the upstream IdP&lt;/span&gt;
&lt;span class="nv"&gt;MCP_PROXY_AUTH_SCOPES_PRESERVED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;openid,api.read,api.write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CIMD support (experimental)&lt;/strong&gt; - The MCP spec defaults to &lt;a href="https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/" rel="noopener noreferrer"&gt;Client ID Metadata Documents&lt;/a&gt; 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 &lt;code&gt;client_id&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability built in&lt;/strong&gt; - Prometheus metrics at &lt;code&gt;/metrics&lt;/code&gt;, structured logging, health probes for k8s.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy it in 30 seconds
&lt;/h2&gt;

&lt;p&gt;Grab the container image and go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; mcp-auth-adapter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MCP_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://mcp-auth.example.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MCP_UPSTREAM_SSO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://sso.example.com/auth/realms/external &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MCP_PROXY_DCR_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mcp-client &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/velias/mcp-auth-adapter:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Three env vars for a basic setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MCP_BASE_URL&lt;/code&gt; - the public URL where this adapter lives&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MCP_UPSTREAM_SSO_URL&lt;/code&gt; - your IdP's issuer URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MCP_PROXY_DCR_CLIENT_ID&lt;/code&gt; - a public client pre-registered at your IdP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then point your MCP server's &lt;code&gt;authorization_servers&lt;/code&gt; to &lt;code&gt;MCP_BASE_URL&lt;/code&gt; and clients will discover everything via &lt;code&gt;.well-known&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Typical production config
&lt;/h3&gt;

&lt;p&gt;For a real deployment you'll probably want scope control too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MCP_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://mcp-auth.example.com
&lt;span class="nv"&gt;MCP_UPSTREAM_SSO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://sso.example.com/auth/realms/external
&lt;span class="nv"&gt;MCP_PROXY_DCR_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mcp-client
&lt;span class="nv"&gt;MCP_WELL_KNOWN_SCOPES_SUPPORTED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;openid,api.read,api.write
&lt;span class="nv"&gt;MCP_PROXY_AUTH_SCOPES_REMOVED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;offline_access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This controls both sides: what clients &lt;em&gt;see&lt;/em&gt; in discovery and what actually &lt;em&gt;reaches&lt;/em&gt; your IdP.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do
&lt;/h2&gt;

&lt;p&gt;This adapter is intentionally minimal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No token issuing&lt;/strong&gt; - tokens come from your IdP, always&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No user database&lt;/strong&gt; - stateless, nothing to back up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No rate limiting&lt;/strong&gt; - put it behind your existing reverse proxy / WAF&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No CORS&lt;/strong&gt; - designed for redirect-based flows, not browser fetch calls&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who is this for?
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Tested with Claude Code, Claude Desktop, Cursor IDE, Cursor Agent, Gemini CLI, VS Code, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;MCP is moving fast, and auth is one of those things that &lt;em&gt;should&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;If you hit rough edges or have ideas, open an issue - the project is young and feedback shapes the roadmap.&lt;/p&gt;




&lt;p&gt;Apache 2.0 licensed. PRs welcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/velias/mcp-auth-adapter" rel="noopener noreferrer"&gt;github.com/velias/mcp-auth-adapter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
