Part 1 · The scenario
Same company, new player: an AI agent joins the cast
The setup extends the first primer's world. AcmeNetworks.com is a SaaS that provides telemetry services for example.com; employees access it with their corporate identity (Acme's authorization server federates to example.com's Okta, which federates to Microsoft — the full chain from Primer 1). Acme has now shipped an MCP Telemetry Server at mcp.acmenetworks.com/mcp, so customers' AI tools can query telemetry directly. Alice wants her Claude Code instance to use it.
Runs on Alice's laptop. Speaks MCP to servers it has never seen before — which is the defining constraint of this whole primer. Acme has never heard of this particular client instance.
mcp.acmenetworks.com/mcp. Exposes tools like get_device_telemetry and list_alerts. Validates Bearer tokens; never logs anyone in itself — exactly like the Telemetry API of Primer 2.
auth.acmenetworks.com — Acme's own token mint, the same one behind its web app. For example.com users it federates upstream: Okta → Microsoft Entra, precisely Primer 1's chain.
Appears for one brief moment mid-flow — to authenticate with her corporate identity and approve. Everything else is machinery she never sees.
The punchline, up front: when this flow finishes, Claude Code holds a token that (a) identifies Alice of example.com, (b) works only at Acme's MCP server and nowhere else, and (c) carries only the permissions example.com's admin enabled for her in Acme's tenant. Alice typed her password once — at Microsoft, as always. Claude Code was never pre-registered with Acme. Every piece of that was set up by the machinery of the two previous primers, plus a discovery layer MCP adds on top.
Part 2 · What MCP adds
The bootstrapping problem
Every flow so far had one silent prerequisite: somebody configured the client beforehand. AcmeNetworks' developers registered their web app in Okta by hand — copied a client_id, uploaded keys, whitelisted a redirect URI. That works when one app talks to one identity provider it was built for.
An MCP client breaks that assumption. Claude Code is general-purpose: today Alice points it at Acme's telemetry server, tomorrow at three other MCP servers from vendors nobody predicted. Pre-registering with each is impossible. So MCP's authorization spec — a profile of OAuth 2.1, not a new protocol — must answer three bootstrap questions at runtime:
- Who protects this server? → Protected Resource Metadata (RFC 9728)
- How do I talk to that protector? → Authorization Server Metadata (RFC 8414 / OIDC Discovery)
- How do I introduce myself without an appointment? → Client ID Metadata Documents (with Dynamic Client Registration as the deprecated fallback)
Everything else — PKCE, authorization codes, Bearer tokens, refresh tokens, federation — is exactly the machinery of the first two primers. The role mapping is exact:
| OAuth 2.1 role | In Primers 1–2 | In this primer |
|---|---|---|
| Client | Acme web app / acmectl | Claude Code (MCP client) |
| Resource server | Telemetry API | Acme MCP Telemetry Server |
| Authorization server | Okta | Acme AS — federating to Okta → Entra behind the scenes |
| Resource owner | Alice | Alice, unchanged |
| Grant | Auth code + PKCE / client credentials | Auth code + PKCE (PKCE mandatory — this is 2.1) |
Scope note: MCP authorization applies to HTTP-based transports — remote servers like Acme's. Local MCP servers spawned over stdio explicitly do not use this flow; they read credentials from the environment (an API key in an env var), because a subprocess on your own machine has no trust boundary to negotiate. Authorization is optional in the spec — but a server holding customer telemetry will obviously require it.
Part 3 · The three self-describing documents
Discovery: nobody knows anybody, everybody publishes
MCP's answer to bootstrapping is symmetry: each party publishes a machine-readable description of itself at a well-known URL. The resource server describes its protector; the authorization server describes its endpoints; the client describes its own identity. Three documents, three directions of trust:
Protected Resource Metadata
mcp.acmenetworks.com/.well-known/oauth-protected-resource
The MCP server says: "here is who protects me." Mandatory (RFC 9728). This is how the client learns which authorization server to talk to — it never guesses.
{
"resource":
"https://mcp.acmenetworks.com/mcp",
"authorization_servers":
["https://auth.acmenetworks.com"],
"scopes_supported":
["telemetry:read","alerts:read"]
}
Authorization Server Metadata
auth.acmenetworks.com/.well-known/oauth-authorization-server
The AS says: "here is how to work with me." RFC 8414, or OIDC Discovery (Primer 1's openid-configuration) — clients must support both.
{
"issuer":
"https://auth.acmenetworks.com",
"authorization_endpoint": "…/authorize",
"token_endpoint": "…/token",
"code_challenge_methods_supported":
["S256"],
"client_id_metadata_document_supported":
true
}
Client ID Metadata Document
claude.ai/.well-known/claude-code-client.json (illustrative)
The client says: "here is who I am." Its client_id is this URL — the AS dereferences it and caches. Registration becomes a fetch. This replaces Dynamic Client Registration (RFC 7591), which MCP's authorization profile deprecates but retains for compatibility.
{
"client_id": "https://claude.ai/
.well-known/claude-code-client.json",
"client_name": "Claude Code",
"redirect_uris":
["http://127.0.0.1:3000/callback",
"http://localhost:3000/callback"],
// loopback URIs: the port may vary at
// authorization time (RFC 8252 §7.3)
"grant_types":
["authorization_code","refresh_token"],
"token_endpoint_auth_method": "none"
}
Client identification has a defined priority: pre-registered credentials if the client already holds them for this AS → CIMD if the AS advertises support → Dynamic Client Registration as the legacy fallback → asking the user to paste client details. Note token_endpoint_auth_method: none in the client document: Claude Code on Alice's laptop is a public client — it can't hold a client_secret (anything shipped to a laptop is extractable). Its protections are PKCE plus the exact-redirect and audience rules — which is precisely why OAuth 2.1 made PKCE mandatory.
Part 4 · First contact, animated
Claude Code meets a server it has never seen
Alice runs claude mcp add --transport http acme-telemetry https://mcp.acmenetworks.com/mcp and asks Claude about her network. Watch the three discovery documents do their work, then the familiar Primer-1 machinery take over. Dashed arrows are front channel (via a browser), solid are back channel:
Count what Alice did: she typed a URL into a config command, clicked once in a familiar corporate login (her Okta/Microsoft session likely made even that instant), and approved one consent screen. Everything else — six HTTP exchanges of pure discovery and cryptography — was automatic. That ratio (one human moment, full mutual verification) is what the spec's ceremony buys.
Part 5 · Authorization in action
Where Alice's permissions actually come from
Connecting is only half the story. Alice is in — but what may she do? Two independent gates decide, and it's worth keeping them distinct:
Gate 1 — Entitlements set by the admin
In Acme's SaaS console, example.com's tenant admin assigned Alice a role: Network Analyst → may read telemetry and alerts, may not change device configs. This lives in Acme's tenant database, decided long before any token existed. Alice cannot consent her way past it.
Gate 2 — Scopes carried by the token
The access token's scp claim records what this particular client session was granted: the intersection of what Claude Code requested and what Gate 1 allows. Even a fully entitled user can hold a narrowly scoped token — least privilege per session.
The token Claude Code received in the previous diagram encodes both gates plus the audience binding:
| Claim | Value | Meaning |
|---|---|---|
sub | alice@example.com | Who — proven via Okta → Microsoft, exactly Primer 1 |
tid | example-com | Which customer tenant — data isolation in a multi-tenant SaaS |
aud | https://mcp.acmenetworks.com/mcp | Where it works — this MCP server and nowhere else |
scp | telemetry:read alerts:read | What it permits — Gate 1 ∩ Gate 2 |
exp | +1 hour | How long — after which the refresh token (if the AS issued one) takes over silently |
(That table shows an illustrative decoded JWT — the contract between the authorization server and the resource server. The client never inspects these claims: to Claude Code the access token is an opaque string, and an AS that issued opaque tokens validated via introspection would work identically.)
Watch the gates operate — a successful tool call, a denied one (with MCP's step-up authorization attempting to help and Gate 1 having the final word), and a silent token refresh:
The step-up dance is a feature, not a failure. The 403 + insufficient_scope challenge tells the client exactly which scope the operation needs, and the client re-authorizes requesting the union of old and new scopes — so permissions grow incrementally instead of every client demanding everything upfront. But consent can only narrow, never expand: when the authorization server checked Gate 1 and found example.com's admin hadn't granted config:write, no amount of Alice-clicking-Approve could mint that scope. Admin entitlements bound user consent — the same principle as admin pre-consent in Primer 1, inverted.
Part 6 · Why the ceremony
The threat model: one client, many strangers
Classic OAuth deployments had one app talking to one IdP it trusted by construction. An MCP client talks to many servers, some of which may be malicious — Alice might add a sketchy MCP server tomorrow. Each mandatory rule in the spec closes a specific door:
resource= in both the authorize and token requests; the server must verify aud is itself. Without it, a malicious MCP server that received Alice's token could replay it against her other MCP servers — the classic confused-deputy move. With it, a token for Acme is inert everywhere else.iss parameter on the callback. Defeats mix-up attacks where one rogue authorization server answers a flow another one started — a real risk when a client juggles several ASes at once.localhost redirect — the code crosses a browser and a loopback port. PKCE (Primer 1's glossary) makes an intercepted code unredeemable. OAuth 2.1 requires it even for confidential clients.Authorization: Bearer header on every single request — and never, ever in a URL query string (URLs leak: logs, history, referrers — the same reason Primer 1 kept tokens off the front channel).Part 7 · Reference
Glossary — the MCP additions
/.well-known/oauth-protected-resource — the resource server's self-description: its canonical identity, its authorization server(s), its baseline scopes.client_id is an HTTPS URL pointing at its own metadata. The AS fetches it on first sight. Successor to Dynamic Client Registration, which MCP's authorization profile deprecates but keeps for compatibility (RFC 7591 itself lives on outside MCP).resource= and matched against aud — lowercase scheme/host, no fragment, most specific URI for the server (e.g. https://mcp.acmenetworks.com/mcp). Sloppy matching here reopens the replay door.insufficient_scope challenge + re-authorization loop: the server names the missing scopes, the client re-requests the union of old + new, with retry limits. Permissions grow on demand instead of maximally upfront.Part 8 · Go deeper
Sources & further reading
- MCP specification — Authorization. The primary source: roles, discovery chain, scope selection, step-up flow, iss validation table, token rules. Both diagrams follow its normative flow.
- MCP blog — the 2026-07-28 release candidate. The revision deprecating Dynamic Client Registration for CIMD and hardening iss validation.
- RFC 9728 — Protected Resource Metadata, RFC 8414 — AS Metadata, RFC 8707 — Resource Indicators, RFC 9207 — Issuer Identification. The four load-bearing RFCs of the discovery-and-binding story.
- IETF draft — OAuth Client ID Metadata Documents. The CIMD mechanism itself.
- OAuth 2.1 (draft-ietf-oauth-v2-1). The base protocol MCP profiles.
- Stack Overflow blog — Authentication and authorization in MCP and Cloudflare — MCP authorization. Practitioner-level walkthroughs and an implementation platform's view.