Building MCP servers
Authenticating MCP servers
Governing MCP across your organization?
Speakeasy’s AI control plane connects, secures, and observes every agent and MCP server from one entry point — with SSO, RBAC, runtime guardrails, and a full audit trail.
Client registration has changed
MCP 2026-07-28 deprecated Dynamic Client Registration (DCR) in favor of Client ID Metadata Documents. Clients use pre-registered credentials when available; DCR remains a compatibility fallback. The proxy examples below explain an approach developed for earlier registration requirements. A proxy is not required solely because a provider lacks DCR.
The Model Context Protocol (MCP) Specification is clear about what is expected from MCP servers to implement OAuth support. Those words may seem reassuring, especially if you already implement OAuth in your products.
However, your OAuth server most likely doesn’t comply with MCP’s requirements. The MCP Specification expects authorization servers to meet a highly specific set of OAuth features that many OAuth servers do not support.
This mismatch between current OAuth implementations and what MCP expects is a common point of friction holding back enterprise adoption of MCP. If you’re still in the exploratory phase of MCP development for your product, you may not have bumped into this problem yet. The OAuth situation usually only rears its head when you get to the point at which you consider the practical steps a user will take when authenticating with your service and authorizing the MCP server to act on their behalf.
In this guide, we’ll explain this mismatch in plain terms, offer alternatives, and hopefully help you find your way.
One URL for every MCP
Point Claude Code, Cursor, and Copilot at one URL. SSO, per-team policy, and an audit log on every tool call are already built.
The perfect world that doesn’t exist
In an ideal universe, here’s how MCP OAuth would work: Your company already has an OAuth server1 that perfectly complies with the MCP Specification. All you need to do is host a well-formed /.well-known/oauth-protected-resource file on your MCP server, and you’re done! The MCP client handles the entire OAuth flow automatically.
The June 2025 revision of the
specification
settled how discovery works: the MCP server is an OAuth resource server that
publishes /.well-known/oauth-protected-resource (RFC 9728) and returns it
via the WWW-Authenticate header on a 401. The client reads that document
to find the separate authorization server, then fetches the authorization
server’s own /.well-known/oauth-authorization-server metadata (RFC 8414).
Current MCP clients follow this flow, but you’ll still find older servers and
clients that expected the MCP server to be its own authorization server.
In reality, however, only a handful of OAuth providers have added support for the specific features required by the MCP Specification. Most OAuth providers would need to change their implementation significantly to support MCP’s requirements.
The specification splits the requirements across the two roles. Your MCP server (the resource server) must publish Protected Resource Metadata (RFC 9728), and clients must send the RFC 8707 resource parameter. Your authorization server needs to support:
- OAuth 2.1 with mandatory PKCE2
- Authorization Server Metadata (RFC 8414)
- Client ID Metadata Documents, which clients and authorization servers should support for client registration.
Dynamic Client Registration (DCR)3 is deprecated. It remains available as a fallback for authorization servers that do not support Client ID Metadata Documents.
The DCR problem
When creating an OAuth client, the usual workflow is for the developer to manually get a client_id and client_secret from the authorization server, then configure their application with these credentials.
This traditional approach is aimed at developers, not end users. It assumes that the developer has access to the OAuth server’s management interface and can create an application manually.
Earlier MCP revisions recommended DCR to avoid manual registration. The current specification standardizes pre-registration and Client ID Metadata Documents as alternatives. Clients should support static credentials and may prompt users to enter client information when automatic options are unavailable.
But here’s the thing, and it cannot be stressed enough: many OAuth providers still don’t support Dynamic Client Registration (and yours may not either).
Support is improving as MCP adoption grows, and some providers and identity platforms (such as WorkOS and Auth0) now offer it, but the big consumer identity providers you’d reach for first have historically not. Google, GitHub, and Microsoft Entra ID (formerly Azure AD) have all lacked open DCR, and your company’s homegrown OAuth solution that Bob from IT built five years ago almost certainly doesn’t have it either.
If you’re thinking, “But wait, I’ve seen MCP servers with OAuth!” you’re right, but most are not doing what you think they’re doing.
The OAuth proxy solution
For deployments that still need DCR compatibility, OAuth proxies can bridge providers that do not support it.
An OAuth proxy sits between the MCP client and your actual OAuth provider. Think of it as a translator that speaks “MCP OAuth” on one side and “regular OAuth” on the other. The most popular implementation uses Cloudflare Workers4, which has become the de facto standard for this architectural pattern.
Here’s how it works:
- The proxy exposes its own OAuth endpoints that comply with MCP requirements (including DCR support).
- The MCP client only talks to the proxy, never directly to your real OAuth provider.
- The proxy handles all the translation between MCP’s expectations and your OAuth provider’s reality.
- Fake tokens all the way down: The tokens the MCP client receives are generated by the proxy; they never see your actual OAuth provider’s tokens.
The Cloudflare OAuth wrapper exposes endpoints like:
/.well-known/oauth-authorization-server/register(for dynamic client registration)/authorize/token
Behind the scenes, the proxy stores all the mapping data in Cloudflare KV5, maintains the relationship between its “fake” tokens and the real provider tokens, and handles all the complexity of token refresh and validation.
This is quite a bit of responsibility for a proxy to take on, and the implications for your security posture are significant. You’re essentially building an entire OAuth authorization server to bridge the gap between MCP’s requirements and existing OAuth providers.
The hidden complexity of OAuth proxies
Building an OAuth proxy isn’t just a weekend project. You need to handle:
- PKCE verification6 (because MCP requires OAuth 2.1)
- Token lifecycle management (storing, refreshing, and revoking)
- Security considerations (you’re now responsible for token security)
- Custom adapters (for each OAuth provider you want to support)
- Performance optimization (every auth check now has additional hops)
The fun doesn’t stop there.
The illusion of Dynamic Client Registration
Most current implementations store a single client_id and client_secret for the entire proxy. This is how both Cloudflare’s proxy and Stytch currently work.
Despite the technical implementation of Dynamic Client Registration within the proxy, from the upstream OAuth provider’s perspective, there’s only one registered application.
This means every user of your MCP server is effectively acting under the same OAuth application. It’s not true Dynamic Client Registration in the sense that each MCP client gets its own distinct OAuth application identity with the upstream provider. Depending on your use case, this might be fine, or it might be a compliance nightmare.
Up until now, your customers have been using your OAuth server with their own client IDs and secrets; but now they all share the same credentials. This can lead to issues with rate limiting, token revocation, and auditing.
Simply plopping a Cloudflare OAuth proxy in front of your MCP server doesn’t magically solve these problems. You may need to make significant changes to your own systems to accommodate this new architecture.
MCP OAuth example: WorkOS and Cloudflare Workers
The Cloudflare AI repository provides several live examples demonstrating OAuth proxy implementations. The WorkOS example illustrates how an OAuth wrapper is registered using the Cloudflare provider, with custom adapters that enable the Cloudflare OAuth server to proxy requests to WorkOS.
After analyzing the complete Cloudflare provider source implementation, several key architectural patterns emerge that are consistent across all examples:
-
The Cloudflare wrapper exposes its own complete OAuth, including
.well-known/oauth-authorization-server,register,authorize, andtokenendpoints. The MCP client never directly interacts with the downstream OAuth provider and remains unaware of the actual provider’s server details. -
All server metadata returned in
well-known/oauth-authorization-serverpoint exclusively to the Cloudflare server. The authorization codes and access tokens received by the MCP client are proxy tokens generated by the Cloudflare wrapper, completely isolated from the underlying WorkOS tokens. -
The Cloudflare provider uses Cloudflare KV for persistent storage of grant data, authorization codes, and provider access tokens.
-
Despite the proxy architecture, the proxy still redirects users to the authentic OAuth consent screen of the underlying provider.
-
The Cloudflare provider handles PKCE verification, ensuring OAuth 2.1 security standards are maintained throughout the proxy flow.
-
Custom WorkOS adapters integrate with the Cloudflare OAuth wrapper through
OAUTH_PROVIDER.parseAuthRequestandOAUTH_PROVIDER.completeAuthorizationfunctions. ThecompleteAuthorizationmethod allows the Cloudflare wrapper to complete a token exchange with the real OAuth server, store the resulting token in KV storage, and subsequently initiate the MCP client callback. The final token exchange occurs exclusively between the MCP client and the Cloudflare wrapper. -
On the WorkOS side, you configure your Cloudflare MCP domain callback URL as a valid redirect URL in WorkOS and use your single WorkOS
ClientIDandClientSecretin the server adapter. Regardless of how many MCP clients connect, they all funnel through one WorkOS OAuth application registration.
How MCP servers use OAuth today
So who is actually using OAuth with MCP servers today? The answer reveals a lot about the state of the ecosystem.
GitHub’s remote MCP server supports OAuth access tokens obtained through a GitHub App or OAuth App, as well as personal access tokens. It does not support Dynamic Client Registration, so clients using OAuth must configure an app registration. See GitHub’s authentication documentation.
The architectural implications of OAuth proxies
Building an OAuth proxy means you’re essentially building a distributed authorization server. Your proxy needs to handle all the stateful operations that OAuth requires: storing authorization codes, managing token lifecycles, and maintaining secure mappings between proxy tokens and real provider tokens.
Every API call to your MCP server now involves additional hops through the proxy. Token validation that used to be a simple database lookup now involves multiple service calls.
Security gets more complex. You’re now responsible for securing the proxy infrastructure itself, protecting the token mapping database, ensuring proper token rotation and expiration, and all the edge cases that come with handling OAuth tokens.
You also may need to update your logging and monitoring practices. When all users share the same OAuth application credentials, depending on your current setup, you may lose granular visibility into who’s doing what. Your audit logs show one application making all the requests.
These are not trivial changes. They require careful planning, additional resources, and a deep understanding of both OAuth and MCP.
The developer experience gap
This brings us to MCP auth’s most difficult challenge: the mismatch between developer expectations and reality.
Developers coming to MCP expect OAuth to work like it does everywhere else: get your client credentials, configure your app, and done. Instead, they find themselves in a quagmire of proxy architectures, token mapping strategies, and the intricacies of DCR.
What should be a simple authentication problem has turned into an architectural redesign that touches security, performance, and compliance.
Looking forward
The June 2025 revision of the specification settled the biggest architectural question by making the MCP server a plain OAuth resource server that delegates to a separate authorization server, discovered through Protected Resource Metadata (RFC 9728) and bound to its audience with Resource Indicators (RFC 8707). That removes the ambiguity about whether your MCP server should also be an authorization server: it shouldn’t.
The 2026-07-28 revision deprecated DCR in favor of Client ID Metadata Documents. Teams should first check support for that mechanism and pre-registration before adding a proxy solely for client registration.
Footnotes
-
An OAuth server (or authorization server) is the system that handles user authentication and issues access tokens. Think of it as the bouncer at a club who checks IDs and gives out wristbands. ↩
-
PKCE (Proof Key for Code Exchange) is a security extension to OAuth that prevents authorization code interception attacks. It’s like adding a secret handshake to the authentication process. ↩
-
Dynamic Client Registration (DCR) allows applications to register themselves as OAuth clients programmatically, without manual configuration. Imagine if every app could automatically add itself to your Google account’s authorized apps list - that’s DCR. ↩
-
Cloudflare Workers are serverless functions that run at the edge of Cloudflare’s network. Think of them as tiny programs that can intercept and modify web requests. ↩
-
Cloudflare KV is a key-value storage system. Essentially, it’s a simple database for storing data like tokens and their mappings. ↩
-
PKCE verification ensures that the same client that started the OAuth flow is the one completing it, preventing certain types of attacks. ↩