The short version: The 2026-07-28 MCP spec went stateless, and that's the story everyone told. But the six authorization SEPs are what will actually break a working integration next Monday — and the breakage is almost entirely on the client. Three fixes cover it: validate the iss parameter, declare your application_type when you register, and discover the authorization server instead of hardcoding it. Here's the exact code, and why a client that skips the first one keeps working right up until it doesn't.
The auth changes add no new mechanism. That's the point of them: MCP now behaves like a plain OAuth 2.1 resource server so it drops into the identity providers companies already run. The risk is that "behaves like a boring resource server" means a strict server can now reject the shortcuts your client used to get away with.
Fix 1 — validate iss on the callback (this is the one that 401s you)#
The 2026-07-28 spec makes RFC 9207 a client requirement: when the authorization server redirects back with a code, it also returns an iss (issuer) parameter, and you must confirm it matches the server you started the flow with. This defends against an OAuth mix-up attack — a malicious server steering your client into sending its auth code to the wrong token endpoint.
It's one equality check, and it's the single most likely thing to be missing from a client written before the RC:
// OAuth redirect handler
function handleCallback(params: URLSearchParams, session: FlowState) {
// RFC 9207: the issuer that answered must be the one we asked.
if (params.get("iss") !== session.expectedIssuer) {
throw new Error("issuer mismatch — possible mix-up attack; abort");
}
if (params.get("state") !== session.state) {
throw new Error("state mismatch");
}
return exchangeCode(params.get("code"), session); // PKCE verifier here
}
A client that never read iss looks healthy today because a lenient server doesn't care. Against a hardened server it's a rejected flow — and it fails at login, not at a tool call, so it reads like "auth is broken" rather than "we skipped a check."
Fix 2 — declare application_type at registration#
If you use Dynamic Client Registration (RFC 7591) instead of a pre-issued client ID, the spec now asks OIDC-style clients to declare an application_type. It decides which redirect-URI and token rules the server enforces:
"web"— a server-side app with a confidential backend andhttpsredirect URIs."native"— a CLI, desktop, or mobile client: a public client using PKCE with loopback or custom-scheme redirects.
Get it wrong and the server applies the wrong policy — a native CLI declared as web will have its http://127.0.0.1 redirect rejected.
await fetch(registrationEndpoint, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
client_name: "my-agent",
application_type: "native", // CLI/desktop — public + PKCE
redirect_uris: ["http://127.0.0.1:8976/callback"],
token_endpoint_auth_method: "none",
grant_types: ["authorization_code", "refresh_token"],
}),
});
Fix 3 — discover the server, don't hardcode it#
The reason hardcoded endpoints break is that the spec now expects credentials to be bound to the issuer that minted them. If a server rotates its issuer or moves its authorization server, a client with a baked-in token URL silently talks to the wrong place. The fix is the two-hop .well-known discovery the spec standardizes:
// 1. Ask the MCP server which authorization servers protect it (RFC 9728).
const prm = await (await fetch(
new URL("/.well-known/oauth-protected-resource", mcpServerUrl))).json();
const issuer = prm.authorization_servers[0];
// 2. Ask that server for its real endpoints (RFC 8414).
const asm = await (await fetch(
new URL("/.well-known/oauth-authorization-server", issuer))).json();
// Use asm.authorization_endpoint / asm.token_endpoint / asm.registration_endpoint,
// and set expectedIssuer = asm.issuer → this is what Fix 1 checks against.
const expectedIssuer = asm.issuer;
That last line is the join: discovery gives you the issuer, and Fix 1 verifies the callback came from it. The three fixes are one chain, not three chores.
The stateless rewrite changes how your servers scale. The auth hardening changes whether your client can log in at all — and it fails quietly, at the one moment a user is least forgiving.
Test it before the 28th#
You don't have to wait for the final spec to shake this out. Point your client at a server running the release candidate, then deliberately break each guard: return a wrong iss and confirm your client aborts rather than proceeding; register a native client with an https-only server and confirm the redirect is rejected; kill your hardcoded token URL and confirm discovery still finds it. If all three fail loudly, you're ready. For the server side of this migration, our server checklist and the full 12-point list cover the rest.



