Off By Default
MCP shipped its largest revision since launch as a release candidate, and the comfortable read was that authorization finally got serious. Read the spec instead of the summary and the comfort thins out.
Authorization did get sharper. It did not get mandatory — the spec still says, in the opening lines of its authorization section, that authorization is optional. What changed is narrower and more interesting than “MCP is secure now.” Where the protocol used to leave the hard calls implied, it now writes down that they are yours. The capabilities that actually govern an agent ship as extensions that are off by default. The one change the redesign is loudest about — statelessness — the published draft still contradicts on its own transport page. Everything that keeps an agent honest is now something you turn on, wire correctly, and prove. That is the release. Off by default is the whole story.
I’ve been keeping a running file on the gap between what MCP lets you do and what its stack lets you verify, audit, and revoke. The MCP Trust Deficit named the gap — the protocol won, the trust layer never shipped. The Advisory List located the one surface a control can physically attach to. This is the protocol’s own answer to that gap, and the answer is: here are the hooks; you’re holding the lock.
Authorization got precise, not mandatory
The floor everyone described did not move up. It got drawn more exactly. A server can still be fully conformant with no authorization at all — the spec is explicit that the whole mechanism is optional. What the revision does is pin down, for the servers that do authenticate, exactly what the job is.
And the job is narrower than the changelog makes it look. A resource server that opts into authorization must publish its metadata, must confirm that a presented token was actually minted for it, and must never accept or pass along one that wasn’t. That is the deliverable. The parts that read like new homework — issuer validation, client-identity documents — are the client’s and the authorization server’s concerns, not the resource server’s. A server that runs off to implement them is polishing someone else’s window.
So what got commoditised is not having authorization. It is the correct way to do the authorization you do. “We handle OAuth thoughtfully” stops being a differentiator the moment the spec writes the thoughtful way down. If your edge was the handshake, your edge is now the baseline — for the servers that opt into it at all. The interesting work moved past the token the same week the token stopped being the differentiator — not the same week it stopped being hard, which it never will.
Stateless on paper, mid-migration in practice
The redesign is not subtle about where it is going. The lifecycle rules now say a server must not infer anything from a previous request; every request carries its own protocol version, identity, and capabilities; anything that needs to span calls has to travel as an explicit handle the client passes each time. Removing sessions is the headline change. As a design, statelessness is settled.
As a document, it is not. The published draft still contradicts itself. The transport page describes the session header and the initialization handshake as current, optional features — not tucked under its backwards-compatibility notice, which covers only the older SSE transport, but standing under their own headings — while the lifecycle page describes a world with neither. This is a release candidate, not a final spec, and the two halves of it have not finished shaking hands. The direction is not in doubt. The finish line is — and a specification that can’t yet agree with itself on whether sessions exist is a specification in the middle of handing something off.
The part that matters for you survives the ambiguity. With no session to anchor identity, you authenticate every request or you authenticate nothing — and where authorization is in play, the spec already requires the credential on every single call, not once at connect time. The new routing headers do not rescue you. They mirror the request’s own method and target, and a companion mechanism can fold client-supplied tool arguments into headers as well — but mirrored or folded, a header value is routing material the client controls, not identity you verified. Treat them as routing, never as proof of who is calling. Every request now arrives on its own, carrying its own claims, and what you trust about it is a decision you make fresh, every time. Statelessness did not remove that decision. It removed the connection you used to pretend had made it for you.
The governance layer is off by default
Here the through-line stops being a reading and becomes a rule you can quote. The capabilities you would reach for to keep an agent in check — intercepting a call before it runs (governance proper), controlling long-running work (the control plane), driving the inline UI where a human-in-the-loop step can live — did not land in the core protocol. They are different kinds of thing, but they share one property: they live in the extensions framework, and the framework’s defining rule is stated plainly: extensions are “always disabled by default and require explicit opt-in from the developer.”
Interception specifically — the hook layer where you would validate or rewrite a tool call before it executes — is still experimental, incubating inside a working group, not part of the core at all. Long-running work is an extension. Inline UI is an extension. None of it is running unless you decided it should be.
Read that as a philosophy, because it is one. The protocol ships the hooks and then declines to enable them for you. Off by default. Explicit opt-in. Whatever safe posture you want — validation before execution, integrity on the tools you expose, a human in the loop on the dangerous ones — is a thing you switch on and configure, not a thing you inherit by being conformant. The maintainers built the doorframe with real care. They also handed you the lock, and a lock nobody turns is decoration.
What the protocol can’t hand you
So if the spec got precise about its own surface, where is the rest? In the places a wire protocol cannot reach for you.
Per-tenant enforcement on the call path, in a world with no session to lean on. Enforcement below the protocol entirely — at the process and network layer, where an engine sees what a message-proxy structurally cannot. And forensic non-repudiation: the ability to prove, after the fact, what an agent actually did. That last one is a data-model problem, not a message-format one, which is exactly why no protocol revision can hand it to you — and why the record-keeping obligation I wrote about in Sixteen Months points at an architecture, not a feature flag.
None of these arrive as a conformant default, and most of them a wire protocol cannot deliver on its own. The redesign did not shrink the governance problem; it relocated it, out of the protocol and into your stack, and in the same move it made conformant and governed two different words.
Three questions for your next architecture review
-
With no session to anchor identity, where in your stack does a request get trusted because a previous one was — and what silently fails open the moment that anchor is gone?
-
If you do authorization, is the token checked for your audience on every call — or decoded once and believed thereafter? A token that carries a tenant claim is not a tenant until you’ve verified it belongs to you.
-
Name the governance capabilities you have explicitly turned on and configured to fail closed. Now separate them from the ones you assumed the protocol turns on for you. Everything in the second list is off.
The spec got more precise about the parts it covers and, in the same stroke, silent-by-default about the parts that keep you safe. That is not a protocol protecting you more. It is a protocol drawing a sharper line around what it will and won’t do for you — and standing everything that governs an agent on your side of it, switched off. Conformant and governed are two different words now. The boundary is yours. Turn it on.
Disclosure: I build MCP Hangar in this space — an MIT-licensed governance layer that sits on the MCP call path, where most of what this post argues for gets implemented. The entire project is at github.com/mcp-hangar/mcp-hangar. I’m not pitching it here — but it shapes what I notice, and you should know that.