MCP authentication and OAuth 2.1, step by step

How MCP authentication works: the 401 challenge, discovery, PKCE, CIMD instead of DCR, scopes and token lifetimes, shown with Claude Code on a live server.

The Standards team

In short

MCP authentication is how a client such as Claude Code proves to a remote MCP server who it acts for. Remote servers use OAuth 2.1: the server answers 401, the client discovers the authorization server, signs you in with PKCE, then sends a bearer token on every request. Local STDIO servers read credentials from the environment instead.

What is MCP authentication?

MCP authentication is the step where an MCP client, such as Claude Code, Codex or a ChatGPT connector, obtains a credential that a remote MCP server accepts before it runs any tool. The Model Context Protocol specifies it in its authorization chapter, built on OAuth 2.1. The current revision, dated 2026-07-28, opens with “Authorization is OPTIONAL for MCP implementations” (MCP authorization spec, read October 2026). It then sets the rule by transport: a server reached over HTTP should follow the OAuth flow, and a server launched over STDIO should “instead retrieve credentials from the environment”. In OAuth terms, the MCP server is a resource server, the client is an OAuth client, and a separate authorization server signs you in and issues tokens. That authorization server may run beside the MCP server or at an identity provider. Standards runs both on one host: its MCP server lives at https://api.standards.new/v1/mcp and its authorization server at https://api.standards.new/oauth.

API key or OAuth: which should an MCP server accept?

An MCP server should accept OAuth when people connect their own clients to it, and may also accept an API key for scripts and for clients without OAuth support. The two answer different questions: an API key says which integration is calling, an OAuth token says which person approved which client, for which server.

Method

Transport

Who holds the secret

How you revoke it

Fits

Environment variables

STDIO, a local process

Your machine

You change the variable

Local servers you launch yourself

API key in a bearer header

HTTP

The client's config file

You delete the key

Scripts, CI, clients without OAuth

OAuth 2.1 access token

HTTP

The client, after your consent

You revoke the connection

People connecting Claude Code, Codex or a ChatGPT connector

Standards takes both on the same endpoint: Authorization: Bearer stndrds_… for a key, or an OAuth access token issued for that workspace. Cursor connects with a key only (Standards MCP docs).

How the MCP OAuth flow works, step by step

The MCP OAuth flow takes a client from an anonymous request to an authorized one in five steps, following the 2026-07-28 revision of the specification. The client needs nothing in advance except the server URL: every endpoint it calls, it discovers from the answers it gets.

  1. The client sends an MCP request with no token. The server answers 401 Unauthorized with a WWW-Authenticate header whose resource_metadata points at its Protected Resource Metadata document (RFC 9728).

  2. The client reads that document to learn the authorization server, then fetches the authorization server's metadata (RFC 8414 or OpenID Connect Discovery). It refuses to go on when code_challenge_methods_supported is absent, since PKCE is mandatory.

  3. The client identifies itself: with a client ID it already holds for that server, otherwise with a Client ID Metadata Document, or through dynamic registration as a fallback. It opens your browser on the authorization endpoint with a PKCE code_challenge (method S256), the scopes it wants and a resource parameter naming the MCP server.

  4. You sign in and approve. The authorization server redirects back with a code and its iss; the client checks iss against the issuer it recorded, then exchanges the code, the code_verifier and the same resource for an access token.

  5. The client sends Authorization: Bearer <token> on every request, never in the query string. The server checks that the token was issued for it, and answers 401 when it is invalid or expired, 403 insufficient_scope when it lacks a scope.

The resource parameter asks the authorization server to bind the token to one server (RFC 8707). Each MCP server must refuse a token issued for another, so a token stolen from one MCP server cannot be replayed against a compliant one. The iss check, added in the 2026-07-28 revision, defends against mix-up attacks, where a malicious authorization server tries to make your client send it a code issued by an honest one.

What a live MCP server returns at each step

Standards' production endpoint answered an anonymous tools/list as below on 26 October 2026, then served the two discovery documents the client reads next. The headers and fields are copied from the responses, trimmed to the ones the flow uses.

POST /v1/mcp HTTP/2
Host: api.standards.new
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/list"}

HTTP/2 401
www-authenticate: Bearer resource_metadata="https://api.standards.new/.well-known/oauth-protected-resource/v1/mcp"
{
  "resource": "https://api.standards.new/v1/mcp",
  "authorization_servers": ["https://api.standards.new/oauth"],
  "scopes_supported": ["mcp:read", "mcp:write", "mcp:delete", "mcp:people:read"],
  "bearer_methods_supported": ["header"]
}

The client then reads https://api.standards.new/.well-known/oauth-authorization-server/oauth, the path the specification builds for an issuer with a path component:

{
  "issuer": "https://api.standards.new/oauth",
  "authorization_endpoint": "https://api.standards.new/oauth/auth",
  "token_endpoint": "https://api.standards.new/oauth/token",
  "revocation_endpoint": "https://api.standards.new/oauth/token/revocation",
  "code_challenge_methods_supported": ["S256"],
  "client_id_metadata_document_supported": true,
  "authorization_response_iss_parameter_supported": true,
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"]
}

Two fields decide the next step. client_id_metadata_document_supported tells the client it may use a URL as its client ID, and the absence of a registration_endpoint tells it that dynamic client registration is off. On Standards, a token carrying none of the four mcp:* scopes gets a 403 with WWW-Authenticate: Bearer resource_metadata="…", error="insufficient_scope", scope="mcp:read mcp:write mcp:delete mcp:people:read" (Standards MCP docs).

Is dynamic client registration still required for MCP?

Dynamic client registration is no longer required for MCP: the 2026-07-28 revision deprecates it in favor of Client ID Metadata Documents. The client hosts a JSON document at an HTTPS URL and uses that URL as its client_id, so it never registers with each server it meets.

Spec revision

Dynamic client registration

Client ID Metadata Documents

2025-06-18

SHOULD support

Not in the spec

2025-11-25

MAY support

SHOULD support

2026-07-28

Deprecated, kept for backwards compatibility

SHOULD support, tried before DCR

The specification's example document shows the shape, trimmed here to the required fields and the ones an authorization server checks. The authorization server fetches it, checks that client_id equals the URL, and checks every redirect URI against it:

{
  "client_id": "https://app.example.com/oauth/client-metadata.json",
  "client_name": "Example MCP Client",
  "redirect_uris": ["http://127.0.0.1:3000/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

The 2026-07-28 revision also recommends the order in which a client tries registration methods. A client ID pre-registered with the authorization server comes first, then a Client ID Metadata Document when the server advertises it, then dynamic registration. As a last resort, the client asks you for a client ID. An authorization server that offers neither CIMD nor DCR still works with clients given a client ID in advance.

OpenAI's page for ChatGPT apps names CIMD “the preferred client registration method when your authorization server supports it and the plugin builder chooses it”, and still supports DCR and predefined OAuth clients (OpenAI Apps SDK, read October 2026).

Standards has turned DCR off and admits a CIMD client under its own rules (Standards OAuth docs), and refuses a document that breaks one before sign-in starts. Among those rules, a client metadata document sent to Standards weighs at most 5120 bytes and carries no client secret (none, or private_key_jwt with an asymmetric key), and its redirects use HTTPS, or loopback http:// for a native app.

Scopes decide which tools the agent sees

Scopes decide what an agent may do once MCP authentication succeeds, and on Standards they also decide which tools appear in tools/list: a scope the connection lacks removes its tools. A Standards client signed in with read access never sees delete, so its model cannot plan a call it would only be refused. The specification leaves the meaning of scopes to each server. It only asks clients to request the scopes from the 401 challenge, or else every scope in scopes_supported, and to ask for more later through a step-up flow. On Standards, each scope opens its own tools, none implies another, and your role in the workspace still applies: the tools the client sees are the intersection of the connection's scopes and your permissions. At consent you pick one access level rather than ticking scopes, and you can change it later from Connected apps without reconnecting the client (Standards OAuth docs).

Level

Scopes

Tools the client sees

Read

mcp:read

search, query, read, propose_memory

Write, the default

mcp:read mcp:write

Read, plus write, edit_content, organize

Full

mcp:read mcp:write mcp:delete mcp:people:read

Write, plus delete, and search and read over members

On Standards, a level change reaches the connection on its next request, even with a token issued earlier. The scope field of a later Standards token response is therefore advisory: a client learns what it may do from tools/list, which the tools reference describes tool by tool.

How long do MCP access tokens last?

The MCP specification sets no token lifetime: each authorization server picks its own, and refresh tokens are optional. A client may add offline_access to its scopes when the authorization server's metadata lists it, and must not assume it will get a refresh token. On Standards, an access token lasts 3600 seconds (one hour), and every client whose metadata document lists the refresh_token grant gets a refresh token, with or without offline_access (client admission rules, token lifetimes, October 2026):

Token

Lifetime

Access token

3600 seconds, opaque

Refresh token

Rotated on every use, valid until 90 days after its last use

Grant

Slides forward on each refresh, ends 90 days after the last use

Authorization code

60 seconds

Refresh reuse grace

30 seconds

On Standards, a client that keeps working stays signed in, and one left unused for 90 days signs in again (token lifetimes). The grace window covers a dropped refresh response: the client may resend the same refresh token within 30 seconds and receives a new token pair. After that window, Standards treats a replayed refresh token as theft and revokes the connection.

Worked example: signing Claude Code and Codex in to Standards

Signing Claude Code or Codex in to an MCP server with OAuth takes two commands, one to add the server and one to sign in, because the client discovers everything else from the server URL. The example below connects both to a Standards workspace; any server that follows the 2026-07-28 revision behaves the same way.

# Claude Code: add the server, then sign in
claude mcp add --transport http standards https://api.standards.new/v1/mcp
claude mcp login standards

# Codex: add the server, then sign in
codex mcp add standards --url https://api.standards.new/v1/mcp
codex mcp login standards

When you sign in (claude mcp login or /mcp in Claude Code, codex mcp login in Codex), your browser opens on the Standards sign-in page, then on the consent screen. That screen shows the client's name from its metadata document and the workspace you sign in to, and asks for one access level. Once you approve, the client lists its tools and works with what that level and your role allow. You will find the connection under Connected apps in your settings. There you can change its level, up to what the client requested and your role allows, or revoke it; a revocation refuses the next request and the next refresh (Connected apps). The developers page covers the SDK and the CLI that call the same API, and the company brain guide shows what an agent reads once it is connected.

Questions

Before you switch tools.

There are three. A local server launched over STDIO reads credentials from its environment, as the MCP specification asks. A remote server over HTTP accepts an OAuth 2.1 access token, obtained through discovery, PKCE and your consent. A remote server may also accept an API key in the same Authorization: Bearer header, for scripts and for clients without OAuth, such as Cursor on Standards.

  1. The 2026-07-28 specification makes authorization optional. When a server over HTTP does protect its tools, the specification says it should follow the OAuth 2.1 flow, so that any compliant client can sign in without setup. A server can still accept API keys beside OAuth: Standards takes both on the same endpoint, and each credential sees only the tools it may use.

Authentication proves who is calling: your sign-in, and the token that carries it. Authorization decides what that caller may do: the scopes you granted and, on the server, your own permissions. The MCP specification puts both in one chapter called Authorization. On Standards, the token authenticates you, and the access level intersected with your role decides which tools appear.

It means the token is valid but lacks a scope the request needs. The MCP specification has the server answer 403 with error="insufficient_scope", and the client may ask for more through a step-up flow. On Standards, the header names the four mcp:* scopes, and a level raised in Connected apps applies on the connection's next request, without reconnecting.

Yes. OpenAI's Apps SDK documentation says ChatGPT runs the authorization code flow with PKCE using S256. It names a Client ID Metadata Document the preferred registration method when the authorization server supports it, with dynamic registration and predefined clients as the other options. Your server must publish Protected Resource Metadata. The Standards ChatGPT app is not published yet, so connect Standards through Claude Code, Codex or Cursor today.

Connect your MCP client to Standards

Point Claude Code or Codex at your workspace, sign in once, and the agent sees only the tools your access level and role allow.