Table of contents
- The running example
- The three parties
- One complete flow at a glance
- Step 1: The client tries the MCP operation
- Step 2: The client reads PRM
- Step 3: The client reads authorization server metadata
- Step 4: The client identifies itself — why CIMD matters
- Step 5: The client prepares PKCE and asks for authorization
- Step 6: The user signs in; the browser returns a code
- Step 7: The client exchanges the code for a token
- Step 8: The client retries the MCP request
- Step 9: The MCP server validates authorization before executing
- How this connects to the MCP protocol
- What the client and server each store
- Common misunderstandings and failure cases
- Security checklist for the database server
- The video example: what remains useful and what changed
- A concise interview answer
- Sources
How an AI client discovers a login provider, identifies itself with CIMD, obtains a resource-bound token, and calls protected MCP tools.
The running example
Imagine a company has a database and exposes a few operations through a remote MCP server:
list_customersget_customerupdate_customer
An employee asks an AI application to retrieve a customer record. The application is the MCP host; inside it, an MCP client connects to the server. The server is available at https://mcp.example.com/mcp and protects customer data. A separate identity provider, such as the company’s Keycloak service or a hosted provider, signs the employee in and issues OAuth tokens.
The goal is to let the employee’s client use only the permitted tools, while the MCP server can verify who is calling, what access was granted, and whether the token was issued for this server.
The simple mental model: MCP describes the operation being requested. OAuth provides a verifiable access pass for the caller. The client carries the pass in the HTTP request; the server checks it before executing the operation.
The three parties
| Party | Responsibility | Example |
|---|---|---|
| MCP client | Sends MCP requests and obtains/attaches OAuth tokens | AI application or IDE |
| MCP server / resource server | Exposes tools and verifies incoming tokens | https://mcp.example.com/mcp |
| Authorization server (AS) | Signs users in and issues tokens | https://login.example.net |
The authorization server and MCP server are different roles, even when one vendor hosts both. In the common arrangement, the MCP server does not sign the user in and does not mint the access token. It publishes information about which authorization server it trusts, then validates tokens from that issuer.
OAuth is not mandatory for every MCP deployment. A local stdio server, a public read-only service, or an application with another trust model may not use this remote OAuth flow. This article concerns a protected remote MCP server over HTTP.
One complete flow at a glance
This is a learning diagram, not a claim that every network exchange uses the same HTTP endpoint or that every host exposes each step to the user. A real MCP client usually performs the discovery and token flow automatically.
Step 1: The client tries the MCP operation
The client wants to call get_customer. The MCP body might look like this in a sessionless protocol version:
POST /mcp HTTP/1.1 Host: mcp.example.com Content-Type: application/json MCP-Protocol-Version: 2026-07-28 Mcp-Method: tools/call Mcp-Name: get_customer { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_customer", "arguments": { "customer_id": "cust_123" }, "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {} } } } If there is no valid bearer token, the server rejects the HTTP request before the tool runs:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp", scope="customers:read" A 401 means the request has not presented acceptable authentication credentials. The WWW-Authenticate challenge tells the client how to begin authorization. It can point at PRM and identify the scope needed for access.
The actual MCP protocol revision matters. Older revisions use initialize and session identifiers; the 2026-07-28 revision has no initialize handshake or Mcp-Session-Id. OAuth is independent of that lifecycle: a protected HTTP request still carries an access token in either era.
Step 2: The client reads PRM
The client fetches the URL named in the challenge. The document is Protected Resource Metadata (PRM), defined by RFC 9728.
A simplified example:
{ "resource": "https://mcp.example.com/mcp", "authorization_servers": ["https://login.example.net"], "scopes_supported": ["customers:read", "customers:write"], "bearer_methods_supported": ["header"] } PRM describes the protected resource—in this example, the MCP endpoint. It says which authorization server or servers can issue acceptable tokens and may list supported scopes. It does not authenticate the user or issue a token.
Where does PRM come from? The MCP server operator configures and serves it as a public JSON document at the appropriate well-known URL. It must be reachable without an access token so an unauthenticated client can discover how to authenticate. The URL is derived from the protected resource URL according to RFC 9728; for a resource whose path is /mcp, current implementations commonly use a path-aware URL such as /.well-known/oauth-protected-resource/mcp.
Where does the client keep it? The client fetches the document over HTTPS and reads it to continue discovery. It may cache public metadata according to its implementation and HTTP caching rules. It is not an access token, a secret, or MCP _meta.
The MCP server is the source of its own PRM because it is the resource whose accepted issuer and canonical identifier the client needs to learn. Treating arbitrary metadata as trusted without validating its origin and values would be unsafe.
Step 3: The client reads authorization server metadata
PRM names an authorization-server issuer. The client fetches that issuer’s Authorization Server Metadata (ASM) document, using RFC 8414 or OpenID Connect Discovery where applicable.
A simplified example:
{ "issuer": "https://login.example.net", "authorization_endpoint": "https://login.example.net/authorize", "token_endpoint": "https://login.example.net/token", "jwks_uri": "https://login.example.net/.well-known/jwks.json", "code_challenge_methods_supported": ["S256"], "client_id_metadata_document_supported": true, "scopes_supported": ["customers:read", "customers:write"] } ASM describes the authorization server: where authorization and token requests go, and which OAuth features it supports. It is a separate document because the same authorization server can serve many resource servers.
The client checks that the metadata’s issuer matches the issuer it intended to contact. This helps prevent it from mixing up authorization servers. The jwks_uri is a public-key location; it is commonly used by the MCP server to obtain keys for verifying signed tokens. The client reads the metadata; the resource server uses the keys to validate tokens.
Step 4: The client identifies itself — why CIMD matters
Before the authorization server can process the login flow, it needs an OAuth client identifier: which software is asking to access the user’s data? This identifies the application, not the human user.
MCP’s 2026-07-28 client-registration guidance gives a selection order:
- Use a client registration already configured for this authorization server, if available.
- Otherwise use Client ID Metadata Documents (CIMD) if the authorization server advertises support.
- Otherwise use Dynamic Client Registration (DCR) if available, as a compatibility fallback.
- If no method is available, the client may have to ask the user or administrator for client details.
See the MCP client registration specification.
CIMD in the database example
With CIMD, the client uses a stable HTTPS URL as its client_id. That URL serves a JSON document describing the client. For example, a company might publish:
{ "client_id": "https://assistant.example.com/oauth/client-metadata.json", "client_name": "Example AI Assistant", "client_uri": "https://assistant.example.com", "redirect_uris": ["http://127.0.0.1:53682/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "token_endpoint_auth_method": "none" } This is illustrative; clients must follow the exact fields and validation requirements in the current CIMD specification. In particular, the client_id must be an HTTPS URL with a path, and its value must match the URL where the metadata is published exactly. The authorization server fetches the document, checks it, and validates that the requested redirect URI is one the document permits.
The authorization server advertises CIMD support in ASM, for example:
{ "client_id_metadata_document_supported": true } If supported, the client can send this URL as its client_id. The AS obtains the client’s metadata from that URL instead of requiring a new registration record for every client/authorization-server pairing.
Why CIMD is important
MCP clients often encounter authorization servers they have never seen before. Traditional pre-registration requires someone to create a client record in advance. DCR solved the onboarding problem by allowing clients to POST their metadata to a registration endpoint and receive a newly created client ID. But DCR requires the AS to operate a registration endpoint and manage registrations created dynamically.
CIMD changes where the client identity comes from: the client publishes its metadata at a stable HTTPS address it controls, and the AS retrieves that document when needed. The identifier can be reused with multiple authorization servers that support CIMD, without registering the client separately at each one. This reduces registration state and makes client identity more directly tied to control of a web domain.
CIMD is not proof that the user is safe, nor is it a replacement for user authentication, PKCE, scopes, or token validation. It identifies and describes the OAuth client. The authorization server still needs to validate the document and redirect URI; the user still signs in; and the MCP server still checks the resulting token.
DCR is deprecated in the current MCP specification, not instantly removed. It remains a compatibility path when an authorization server does not support CIMD. The video’s ScaleKit walkthrough is useful for understanding the DCR-based onboarding it demonstrates, but its statement that DCR is the default should not be repeated as current MCP guidance. Current MCP registration guidance
Step 5: The client prepares PKCE and asks for authorization
The client starts an OAuth Authorization Code flow. Before opening the browser, it generates a high-entropy random secret called the code verifier and keeps it locally. It derives a code challenge using SHA-256 and Base64URL encoding:
code_verifier = random secret stored temporarily by the client code_challenge = BASE64URL(SHA256(code_verifier)) The client sends the challenge to the authorization endpoint, not the verifier. The verifier is held back until the client redeems the authorization code.
A simplified authorization request might contain:
response_type=code client_id=https://assistant.example.com/oauth/client-metadata.json redirect_uri=http://127.0.0.1:53682/callback code_challenge=<derived-value> code_challenge_method=S256 resource=https://mcp.example.com/mcp scope=customers:read state=<random-one-time-value> The parameters have distinct jobs:
| Parameter | Meaning |
|---|---|
client_id | Which app is requesting authorization? With CIMD, this is the client metadata URL. |
redirect_uri | Where should the browser return after authorization? It must match a URI registered in the client metadata. |
code_challenge | What proof must be presented later to redeem the code? |
resource | Which protected resource should accept the resulting token? Here, the MCP server URL. |
scope | What kinds of access are requested? For example, read customer records. |
state | A client-generated value used to match the callback to the login attempt and defend against forged callbacks. |
Resource and scope are not the same. Resource answers “which service is this token for?” Scope answers “what access is being requested at that service?”
PKCE is especially useful for public clients such as desktop apps, CLI tools, and IDEs, which cannot safely keep a client secret. The AS should advertise and support S256; the client should not silently downgrade to a weaker method.
Step 6: The user signs in; the browser returns a code
The authorization server shows its sign-in and consent pages. The user signs in and approves access, if consent is required.
The AS then redirects the browser to the client’s registered redirect_uri, carrying a short-lived authorization code and the original state value. A local-app callback could look like:
http://127.0.0.1:53682/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=af0ifjsldkj The client is listening on that callback address (or has registered an application link) and reads the parameters. It checks that state matches the value it generated. Under the current MCP security guidance, the client also validates the authorization-response issuer (iss) when the AS provides it and advertises that behavior, to guard against authorization-server mix-up.
The browser redirect carries an authorization code, not the access token. The code is temporary and intended to be exchanged by the client.
Step 7: The client exchanges the code for a token
The client sends a back-channel HTTPS request to the AS token endpoint. In simplified form:
grant_type=authorization_code code=<authorization-code> redirect_uri=http://127.0.0.1:53682/callback client_id=https://assistant.example.com/oauth/client-metadata.json code_verifier=<original-random-secret> resource=https://mcp.example.com/mcp The AS verifies the authorization code, client identity, redirect URI, and PKCE verifier. The verifier must correspond to the challenge from the earlier authorization request. If checks pass, the AS issues an access token and may issue a refresh token according to its policy.
A token response can look conceptually like this:
{ "access_token": "<opaque-or-signed-token>", "token_type": "Bearer", "expires_in": 3600, "scope": "customers:read" } An access token is not necessarily a JWT. If it is a JWT, it may contain claims such as issuer (iss), subject (sub), audience (aud), scope, and expiration (exp). If it is opaque, the MCP server may validate it through introspection or another provider-supported method. Do not assume every provider returns a JWT.
Step 8: The client retries the MCP request
The client stores and manages the token according to its security model, then repeats the original MCP operation. It places the access token in the HTTP header:
Authorization: Bearer <access-token> The token does not go in MCP _meta. _meta carries MCP protocol information and client capabilities. The bearer token is an HTTP authorization credential and belongs in the Authorization header. A refresh token, when issued, is also OAuth credential material; it is not sent to the MCP server.
In the sessionless 2026-07-28 protocol, the request still includes the appropriate protocol headers and _meta, alongside the separate HTTP authorization header:
POST /mcp HTTP/1.1 Host: mcp.example.com Authorization: Bearer <access-token> Content-Type: application/json MCP-Protocol-Version: 2026-07-28 Mcp-Method: tools/call Mcp-Name: get_customer The client may reuse a valid token for later calls to that resource. When it expires, the client may use a refresh token to obtain another access token, subject to the authorization server’s rules.
Step 9: The MCP server validates authorization before executing
The MCP server must not trust a token just because it is present or because it decodes as a JWT. It validates the token using the authorization server’s supported verification method and applies authorization policy. Typical checks include:
- Signature or introspection: Was the token issued by a trusted AS and has it been tampered with?
- Issuer (
iss): Is it from the expected authorization server? - Audience/resource (
aud): Was it issued for this MCP server? - Expiration (
exp): Is it still valid? - Scope: Does it grant the access required for this tool?
- Application authorization: Is this user allowed to access this particular customer, organization, or record?
A valid customers:read scope does not automatically authorize access to every customer in the database. The handler must still enforce tenant and record-level permissions.
If the token is missing or invalid, the server returns HTTP 401 with an appropriate bearer challenge. If the token is valid but lacks a required scope, the server commonly returns HTTP 403 with an insufficient_scope challenge. Only an authorized request proceeds to the MCP handler.
How this connects to the MCP protocol
OAuth is adjacent to, but separate from, the protocol features learned in earlier days:
| MCP concept | Question it answers | OAuth relationship |
|---|---|---|
| JSON-RPC request ID and method | What operation is being requested, and which response matches it? | Not authentication. |
| MCP capabilities | Which protocol features can each side use? | Not permission to access customer data. |
| HTTP transport | How does a remote request travel? | Carries the Authorization header. |
Mcp-Session-Id (older protocol versions) | Which stateful protocol session is this? | Not an access token. |
_meta (modern protocol) | What protocol/client metadata accompanies this request? | Not a place to store bearer tokens. |
| OAuth access token | Who authenticated, what grants were issued, and for which resource? | Validated before MCP processing. |
| OAuth scope | What class of access was granted? | Used by the server’s authorization policy. |
| MCP roots | Which filesystem locations are relevant to the client? | Advisory, not an OAuth permission or OS sandbox. |
| MCP sampling | May the server request a client model completion? | Separate capability; it does not authorize data access. |
MRTR requestState | How can a paused MCP operation continue on a later round? | Not OAuth state or a bearer token. |
A modern MRTR retry can carry OAuth and MCP fields at the same time: the HTTP bearer token authenticates the caller; _meta describes MCP protocol/client details; inputResponses supplies the answers to the server’s round-trip requests; and requestState carries opaque server continuation data. They are not interchangeable.
What the client and server each store
Client side
The client may temporarily hold the PKCE verifier until the authorization-code exchange finishes. It then stores the access token and, if issued, refresh token using an application-appropriate secure storage mechanism. For a desktop app, that may be the operating system keychain. Browser apps use different storage and threat models. Exact behavior is client-specific.
The client may also cache public PRM/ASM documents and the CIMD metadata it serves, following implementation and HTTP cache rules. These documents are public metadata, not credentials.
Server side
The MCP server publishes its PRM and keeps the configuration necessary to validate tokens—such as the expected issuer, audience/resource, scopes, and public-key source or introspection settings. It does not need to store a protocol session merely to validate a bearer token. A stateless JWT validation path can validate each request independently, though revocation and opaque-token policies may require additional infrastructure.
Common misunderstandings and failure cases
“OAuth is MCP metadata”
No. The access token is sent as an HTTP bearer credential. MCP _meta is protocol metadata. Never put access tokens or refresh tokens in _meta, tool arguments, prompts, or logs.
“CIMD authenticates the user”
No. CIMD describes the client application. The user still authenticates at the authorization server. The MCP server still validates the resulting token.
“DCR has disappeared”
No. The 2026-07-28 MCP specification deprecates DCR in favor of CIMD, but keeps it as a backward-compatible option for authorization servers that do not support CIMD. Follow the server metadata and the current client-registration priority.
“The token is valid because it is signed”
A valid signature alone is not enough. The server must also check issuer, audience, expiry, scope, and application-level access. A token for another API must not be accepted just because the same identity provider signed it.
“A resource is a scope”
No. Resource identifies the intended server; scope identifies the requested level/type of access at that server.
“401 and 403 mean the same thing”
Usually, 401 means authentication is missing or invalid. 403 means the caller is authenticated but lacks permission for the requested operation, such as a required scope.
“Stateless MCP means there is no authentication”
No. Sessionless MCP removes protocol session setup from the 2026-07-28 revision, but a protected HTTP endpoint still authenticates each request using its bearer token.
“Roots enforce filesystem access”
No. Roots are a signal about relevant directories. The operating system, container, or sandbox must enforce actual file access limits.
Security checklist for the database server
- Serve PRM and authorization-server discovery metadata publicly over HTTPS so clients can bootstrap.
- Return a proper
WWW-Authenticatechallenge for missing or invalid credentials. - Require authorization before protected tool execution.
- Validate token signature/introspection, issuer, audience, expiration, and scopes.
- Ensure
resourceand the expected audience identify this MCP server, not a broad shared API audience. - Authorize record and tenant access inside tool handlers; scopes alone are not row-level access control.
- Use narrow scopes such as
customers:readandcustomers:writeand request only what is needed. - Use PKCE
S256; validate callbackstateand authorization-response issuer as applicable. - Do not log bearer tokens, authorization codes, or PKCE verifiers.
- Keep tokens out of MCP
_meta, tool arguments, model-visible content, and browser bundles. - Treat downstream database/API credentials as separate secrets; do not forward the MCP bearer token to an unrelated service.
- Make writes auditable and consider explicit user confirmation for consequential actions.
The video example: what remains useful and what changed
The video’s central picture remains useful: an MCP client needs an access token; the server challenges with 401; PRM points toward an authorization server; PKCE protects code redemption; the resource parameter helps bind the token to the intended server; and the server validates the token before allowing tool use.
Its client-registration segment needs a date-aware update. It presents DCR as the onboarding mechanism and CIMD as the newer preferred option. Under the MCP 2026-07-28 specification, CIMD is now the preferred dynamic registration approach, while DCR is deprecated but retained for compatibility. If the authorization server advertises client_id_metadata_document_supported: true, a capable client should use CIMD according to the registration priority. If CIMD is unsupported, DCR may still be used if the server advertises a registration endpoint.
The database example makes the roles tangible:
- The AI client is the app asking to use
get_customer. - CIMD identifies that app to the authorization server.
- The employee signs in as the human user.
- PKCE binds the authorization-code redemption to the client that initiated it.
-
resourcebinds the token request tohttps://mcp.example.com/mcp. - A scope such as
customers:readlimits the type of access. - The MCP server checks the token and then applies customer/tenant-level authorization before returning data.
A concise interview answer
“For a protected remote MCP server, the client first gets a 401 challenge pointing to RFC 9728 Protected Resource Metadata. That tells it which authorization server protects the MCP resource. It discovers the AS endpoints through RFC 8414 or OIDC metadata, then identifies itself using pre-registration if configured, otherwise CIMD when supported, and DCR only as a compatibility fallback. The client runs Authorization Code with PKCE S256, requests a token for the MCP server using the RFC 8707 resource indicator and the minimum needed scopes, receives the authorization code through the registered browser callback, exchanges it with the PKCE verifier, and retries the MCP request with an HTTP bearer token. The server validates issuer, audience, expiry, scopes, and user-level authorization before executing the tool.”
Sources
MCP and OAuth primary sources
- MCP Authorization, 2026-07-28
- MCP Client Registration, 2026-07-28
- MCP authorization tutorial, 2026-07-28
- MCP 2026-07-28 release notes
- RFC 9728: OAuth 2.0 Protected Resource Metadata
- RFC 8414: OAuth 2.0 Authorization Server Metadata
- RFC 7591: OAuth 2.0 Dynamic Client Registration
- RFC 8707: Resource Indicators for OAuth 2.0
- RFC 7636: Proof Key for Code Exchange (PKCE)
- OAuth Client ID Metadata Document draft

