MCP mandates PKCE — what must a client check before starting an MCP authorization flow?
answer
- Check something before starting the flow
- The authorization server advertises it
- A metadata field lists the methods
- S256 must be listed, or abort
- No downgrade to a challengeless flow
basics
~20 sBefore starting the flow, an MCP client must read the authorization server's metadata and confirm that code_challenge_methods_supported advertises S256. If that field is absent or lacks S256, the client must not proceed — there is no legal fallback to a flow without PKCE.
solid answer
~50 sMCP's authorization profile in revision 2026-07-28 makes PKCE mandatory rather than recommended, and adds a precondition the client owns: it must fetch the authorization server's metadata and verify that `code_challenge_methods_supported` includes `S256` **before** it initiates the authorization request. If the field is missing, or lists no acceptable method, the conformant behaviour is to abort and surface an error to the user — not to fall back to an authorization code flow without a challenge, and not to hope the server enforces it anyway. The reason for the explicit check is that a metadata document that stays silent about PKCE gives the client no evidence the server will enforce the verifier at the token endpoint, and an unenforced challenge protects nothing. Treat "the library does PKCE for us" as an incomplete answer: the profile requires the capability check as well as the challenge.
go deeper
Know that MCP requires PKCE on the authorization flow and that the client checks the authorization server advertises support before starting, rather than assuming it.
Name the field — code_challenge_methods_supported in the authorization server's metadata — say that S256 must be listed, and state that the client aborts when it is not instead of downgrading.
Explain why the advertisement check exists at all: a client can always send a challenge but cannot observe enforcement at the token endpoint, so the metadata is the only pre-flight signal. Tie it to MCP clients being public clients with interceptable local redirects.
Frame it as a fleet policy question: your host connects to servers users add, so decide what a client does when a deployment fails the check — hard fail with a clear operator-facing error, and whether any allowlisted exception can ever exist.
## What the profile actually requires OAuth 2.1 already folds PKCE into the authorization code flow for public clients. MCP revision 2026-07-28 goes one step further and puts a *verification duty* on the client: before it sends a user anywhere, it must obtain the authorization server's metadata and confirm that the server advertises support for a code challenge method it can use — specifically `S256`, advertised in the metadata field `code_challenge_methods_supported`. If that advertisement is not there, the client must not start the flow. ## Why an advertisement check, and not just "send a challenge" The protection PKCE offers comes from the token endpoint refusing to exchange an authorization code unless the presented verifier matches the challenge recorded at authorization time. A client can always *send* a `code_challenge`; what it cannot see is whether the authorization server stored it and will check it. An authorization server that ignores the parameter will happily exchange a stolen code, and the client's request looks identical either way. The metadata advertisement is the only signal available before the flow begins. Making the check mandatory converts a silent downgrade into a visible failure — the client stops with a clear error instead of running a flow that looks correct and protects nothing. ## Why MCP cares more than a typical web app MCP clients are overwhelmingly public clients: desktop hosts, CLIs, editors and local agents that cannot keep a client secret. They also handle redirects through mechanisms that are easier to intercept than a server-side callback — loopback listeners, custom URI schemes, sometimes a local HTTP port that any process on the machine could race for. In that environment the authorization code is the credential most exposed to theft between the redirect and the token exchange, and the verifier is what makes a stolen code useless. On top of that, MCP clients connect to servers a *user* added at runtime, which means the authorization server on the other end was not vetted by the client's authors. A precondition that must hold before any user interaction is the only leverage the client has over an unknown deployment. ## What "must not proceed" means in practice The wrong reactions are all tempting: - Falling back to an authorization code flow with no challenge, on the reasoning that some access is better than none. This is exactly the downgrade the requirement exists to prevent. - Falling back to a weaker challenge method. `S256` is the method MCP's profile expects; a plain challenge transmits the verifier itself in the authorization request, which defeats the purpose against an attacker who can observe that request. - Sending the challenge anyway and "seeing what happens". The exchange succeeding proves nothing about enforcement. The right reaction is to stop and tell the user that this server's authorization server does not meet MCP's requirements, so the connection cannot be established securely. That is a deployment bug on the server operator's side, and surfacing it is how it gets fixed. ## Where this sits in the overall sequence The check has a fixed place in the sequence a client runs when connecting to a remote MCP server: 1. Discover the resource's protected-resource metadata and read the issuer it trusts. 2. Fetch that issuer's authorization-server metadata. 3. **Verify `code_challenge_methods_supported` contains `S256`. Abort if not.** 4. Run the authorization code flow with the challenge, requesting a token bound to the MCP server as its audience. 5. Attach the resulting token to every MCP request, since 2026-07-28 has no session to hold it. Step 3 is cheap — the metadata was already fetched for step 2 — which is part of why the specification can make it a hard requirement rather than a recommendation. ## The interview signal An answer that says only "PKCE is required" describes OAuth 2.1, not MCP's profile. The MCP-specific content is the client-side precondition: check the advertisement, and refuse to run the flow when it is absent. Candidates who have actually written an MCP client tend to volunteer it, because it is the one part their OAuth library did not do for them.
- The metadata omits code_challenge_methods_supported entirely. What should the client do?Abort and report the failure. Omission is not permission: it gives the client no evidence the authorization server will enforce a verifier at the token endpoint, and MCP's profile requires the client to verify support before proceeding. Starting the flow anyway would run a flow that looks correct while protecting nothing against code interception.
- Why is verifying the advertisement not redundant with simply sending a code_challenge?Because sending a challenge is unilateral. PKCE's protection lives in the token endpoint rejecting an exchange whose verifier does not match the recorded challenge; a server that ignores the parameter accepts a stolen code and returns an ordinary-looking token. The client cannot detect that after the fact, so the pre-flight advertisement is the only available signal.
- Does mandatory PKCE make client secrets unnecessary for MCP clients?For public clients there was never a usable secret to begin with — a desktop host or CLI cannot keep one — and PKCE is what makes the authorization code flow safe for them. A confidential deployment may still authenticate at the token endpoint; MCP's profile requires PKCE regardless, so the two are complementary rather than alternatives.
saying these in an interview costs you the question
- Falls back to a flow without PKCE when unsupported
- Says sending a code_challenge is sufficient on its own
- Treats PKCE as optional for confidential clients
- Assumes the OAuth library performs the capability check
- Accepts a plain challenge method as equivalent to S256