No. 02 · APR 2026 · 6 Min Read
MCP Is the OAuth Layer
Abstract
The anti-MCP take is a single-user take. When your agent serves multiple users accessing their data on remote services, you need a protocol. MCP is the one.
There was a stretch where every other post on my timeline was some variation of “MCP is overengineered, just use skills.” I get the impulse. The protocol has rough edges. The ecosystem is noisy. Running a server feels like ceremony when you could just ship a tool definition in your system prompt.
But I keep noticing something about the people making this argument. They’re almost always building for one user. Themselves. Their personal agent. Their local setup. And when you’re the only user, sure, skills are fine. Bake in your tokens. Hardcode your access. Ship it.
The moment you have a second user, the argument falls apart.
The Single-User Trap
Skills, as they exist in the various agent frameworks, are a tool definition plus some code. They execute in the context of whoever is running them. If that’s you, the credentials are yours. Your Google token. Your GitHub PAT. Your database connection string. Whatever your skill needs, you give it, because you are the user and the developer and the operator all at once.
Now imagine shipping that to a hundred users. Each one has their own Google account. Their own GitHub. Their own data. You can’t hardcode anything. You need a flow for each user to authorize your agent to access their data on a third-party service.
This is the problem OAuth solved for web apps. We are re-learning it for agents, and some people are going to re-learn it the hard way.
The Authorization Architecture
MCP authorization is an HTTP transport concern. A protected remote MCP server is an OAuth 2.1 resource server. The MCP client is the OAuth client, acting for the resource owner. The authorization server handles sign-in, consent, and token issuance.1
The authorization server may share a deployment with the MCP server or run as a separate service. Both are valid. The important separation is logical: the MCP resource-server side accepts and validates access tokens; the authorization-server side issues them. Hosting both at one URL does not turn token validation into a reason to trust every token it sees.1
That prescription stops at the network boundary. The authorization specification covers HTTP-based transports. A local stdio tool should get its credentials from the environment and should not be sent through a remote OAuth dance.1
The Current Flow
The client first reaches the protected MCP endpoint without a usable token. The server can return 401 Unauthorized with a WWW-Authenticate header whose resource_metadata value points to Protected Resource Metadata. If that pointer is absent, the client tries the RFC 9728 well-known metadata locations. The metadata identifies one or more authorization servers. The client selects one, then fetches its OAuth authorization-server metadata or OpenID Connect discovery document and validates the issuer it found there.2
Registration comes before the authorization-code flow. The client uses pre-registered credentials when it already has a relationship with that authorization server. Otherwise, if the server advertises client_id_metadata_document_supported, it uses a Client ID Metadata Document: an HTTPS URL with a path that is itself the client_id and serves the client’s name and redirect URIs. Dynamic Client Registration is the fallback when the server supports it. It is deprecated and retained for backwards compatibility. If none of those paths exist, the client needs a way for the user to supply client details.3
Then it runs Authorization Code with PKCE. Before sending the user to the authorization server, the client records the issuer from the validated metadata with the PKCE verifier and request state. If the authorization response carries iss, the client compares it exactly before redeeming the code. That shuts down a class of authorization-server mix-up attacks.1
The token must be for this MCP server, not merely for an account at the same authorization server. The client includes the canonical MCP server URI in the resource parameter of both its authorization request and its token request. The server validates the access token under OAuth 2.1, including that it was issued for the server’s own audience. It accepts only tokens valid for its resources and does not relay other tokens through itself.14
Scopes stay narrow, too. A server can name the scopes needed for the present operation in the challenge. The client treats that challenge as authoritative and requests additional scopes only when the work requires them.1
That’s the layer. It is conventional OAuth, with the discovery and resource-binding rules that make it safe to use between an agent and a remote tool server.
”But Skills Can Do Auth Too”
Sure. You can bolt OAuth onto a skill. Store per-user tokens in your backend. Handle refresh flows. Manage consent screens. Route each tool invocation through middleware that looks up the calling user, finds their token for the target service, and makes the request on their behalf. Handle token expiration. Handle revocation. Build a dashboard so users can see what your agent is connected to. Build a revoke button. Build audit logs because someone’s compliance team is going to ask.
Now you own an authorization layer.
The skills-only crowd isn’t arguing against the hard parts. The moment a skill needs to access a user’s data on a remote service, you need discovery, per-user consent, secure token storage and refresh, tight scope, resource binding, and revocation. You can invent a private protocol for that or use the one tool servers can already speak.
Every sufficiently advanced skill with multi-user auth is an ad-hoc, informally-specified, bug-ridden implementation of half of MCP.5
The Composability Argument
Even if you enjoy reinventing OAuth flows, the proprietary approach has a second problem. It doesn’t compose.
Without a protocol, every tool integration is bespoke. You build the Google auth flow for your agent. Then the Slack flow. Then GitHub. Then Notion. Each one is its own dance. Each one has its own failure modes. Each one needs its own dashboard UI. And none of this work transfers to the next agent your team builds.
With MCP, tool servers are interchangeable. The client discovers what a server needs, follows a shared authorization flow, and keeps each credential bound to the resource it was issued for. The tool server’s author handles the integration with their service. You handle the integration with the protocol. Once.
This is the same reason we have HTTP instead of each website inventing its own wire format. Boring convergence on a shared protocol is how infrastructure gets built.
Where I’ll Concede Ground
MCP’s tool-calling interface is not obviously better than the native function calling offered by OpenAI or Anthropic. For local tools, running in-process, with no remote authorization concerns, skills and function definitions are lighter, faster, and tighter. I will not die on the hill of “use MCP for everything.”
MCP’s value is specific. It is the remote, multi-user, authorized access pattern. That is a pattern skills cannot replicate without building the same authorization layer.
The Question to Ask
The relevant design question is how a product lets a user authorize its agent to access data on a third-party service.
If the answer involves OAuth, the product needs an OAuth layer. MCP gives remote tool servers and clients a shared way to build it.