MCP authorization reference

MCP server authentication checklist

A pass or fail checklist for authenticating a remote MCP server: discovery, registration, PKCE, audience binding, scope enforcement, revocation, and the tests that prove each one.

Updated July 2026Implementation guidemcp server authentication
Built for

Teams shipping a remote MCP server that real users outside the team will connect to.

Decision supported

Whether an MCP server is ready to be reachable from a client you do not control.

The control gap

Local MCP servers over stdio inherit the trust of the machine they run on. A remote server has none of that. The specification supplies the pieces, but each one fails independently, and the common failure is a server that authenticates correctly in the happy path while accepting a token issued for something else, or one that never revokes.

What good looks like

Each item below either passes with a reproducible test or is written down as a known gap with an owner, rather than being assumed.

  • Discovery: an unauthenticated request returns 401 with a resource_metadata pointer, and that document resolves.
  • Registration: unknown clients can register as public PKCE clients, and confidential auth methods are refused.
  • Binding: tokens carry an audience matching this endpoint, and a token for a sibling endpoint is rejected.
  • Revocation: withdrawing consent stops the next tool call, including on an already open session.

A production workflow

  1. Run the unauthenticated request and capture the challenge header verbatim.
  2. Complete registration and the authorization code flow from a clean client with no cached state.
  3. Present a token from a different resource and confirm the rejection is 401 with invalid_token.
  4. Revoke the consent and confirm the next call fails without requiring a reconnect.

Copy this

Each line is a request and the response that counts as a pass. Anything that only passes by hand-editing a config file is not ready for an external client.

1  curl -i https://host/mcp/<slug>
   PASS: 401 with WWW-Authenticate carrying resource_metadata=

2  curl https://host/.well-known/oauth-protected-resource/mcp/<slug>
   PASS: JSON with resource, authorization_servers, scopes_supported

3  curl https://host/.well-known/oauth-authorization-server
   PASS: registration_endpoint present, code_challenge_methods_supported = ["S256"]

4  Register with token_endpoint_auth_method: "client_secret_post"
   PASS: rejected

5  Authorize without code_challenge
   PASS: rejected

6  Token request with a resource different from the authorization request
   PASS: rejected

7  Present a token minted for /mcp/<other-slug>
   PASS: 401 invalid_token

8  Present a tools:read token to a tools:call operation
   PASS: 403 with WWW-Authenticate insufficient_scope, scope="tools:call"

9  Revoke consent, then call a tool on the existing session
   PASS: fails immediately, no reconnect required

10 Delete the session, then replay the session header
   PASS: 404, client re-initializes

Items 6, 7 and 9 are the ones that most often fail on a server that otherwise looks correct. They are also the three whose failure is invisible until someone tests for it deliberately.

Evidence to require

  • The captured response for each numbered check, with the date it was run.
  • The scope set the server actually enforces, compared with the scope set it advertises.
  • Revocation latency measured on a live session rather than described in a design document.
  • A list of clients currently holding valid consents, with expiry dates.

Buyer checklist

  • Which of these ten checks currently fail, and who owns each gap?
  • Is the check suite run on every deploy or only before launch?
  • Does the staging environment issue tokens that would be rejected in production, and vice versa?
  • Who can revoke a consent, and how long does revocation take to bite?

Practical answers

Common implementation questions

Do local stdio servers need any of this?

Not the OAuth parts, but the trust assumption should be explicit. A stdio server runs with the user's own authority, so the control question moves to which tools it exposes and what those tools can reach.

Can we start with a static API key?

You can, and many teams do while a server is internal. Write down that clients cannot discover it, it does not identify a user, and it does not revoke per client, so the migration is planned rather than forced by an incident.

What does Endram provide here?

Endram implements the discovery, registration, PKCE, resource binding, and scope enforcement described above in front of an existing MCP server, so the server itself keeps its original transport and tools.

Continue the evaluation

Related controls