MCP Authorization Models and Scope Design
How MCP's authorization model evolved from nothing to layered OAuth as the protocol scaled.

MCP's SDK downloads went from about 100,000 a month at launch in November 2024 to 97 million a month by March 2026. That's a 970-fold jump in eighteen months, and it happened while the protocol's authorization model was still being rebuilt, piece by piece, in public. This matters because MCP started with no authorization requirement at all, and every layer bolted on since then, OAuth, scopes, tool-level checks, has been a direct response to something that broke at scale.
OpenAI adopted MCP in March 2025. Microsoft followed in July. AWS came on in November, and by December the Linux Foundation had taken over governance. That sequence tells you MCP stopped being an experiment somewhere in 2025 and became infrastructure. Anthropic's December 2025 ecosystem update counted more than 10,000 active public MCP servers; a pull from the official MCP Registry API in May 2026 found 9,652 latest server records and nearly 29,000 server and version records once you count every version ever published. Each one of those servers is a door an agent can walk through. The protocol still treats authorization as optional, explicitly so for STDIO transport, and mandatory only once you move to HTTP. That's a sensible default for a tool running locally on someone's laptop. It becomes a real gap the moment a deployment goes remote, multi-tenant, and enterprise-facing, which is exactly where the growth numbers say MCP now lives. Scope choices that seemed harmless in a prototype don't stay harmless once they're replicated across a whole fleet of agents.
How the specification's authorization architecture evolved from no auth to a layered OAuth stack
Version 1.0, shipped in November 2024, had no mandatory authentication for remote servers. MCP was built as a local integration tool, so the spec matched that job: no login, no token, no problem, because nothing was leaving the machine.
That changed fast. By March 2025, OAuth 2.1 became the required framework for any HTTP-based flow, with Authorization Code plus PKCE mandatory and Dynamic Client Registration (RFC 7591) added as a should-have. It sounded like progress, but it created a new headache: every team that shipped an MCP server now owned token issuance, client registration, and key management. Most tool teams had never built that kind of security machinery and weren't equipped to get it right on the first try.
June 2025 fixed the actual design flaw. MCP servers got reclassified as OAuth resource servers, and token issuance moved to a separate authorization server. Resource Indicators (RFC 8707) came in alongside that change to stop a token minted for one server from being replayed against another. November 2025 tightened proof-of-possession checks and pushed Client ID Metadata Documents to the front of the registration line, with Dynamic Client Registration kept mostly for backward compatibility.
Then came July 2026, widely described as the biggest rewrite since launch. RFC 9207 issuer validation became mandatory. The spec formally moved away from Dynamic Client Registration toward metadata documents, folded in six SEP requirements, and gave older versions a twelve-month window to deprecate. Read the whole sequence back to back and a pattern shows up: each revision closed a hole that some real deployment had already fallen through. This isn't a design document written from a whiteboard. It's closer to an incident log.
The three-party separation that the current spec enforces and why each role is distinct
Three roles now sit at the center of MCP authorization. The MCP client is the OAuth client, making requests on behalf of whoever it's acting for. The MCP server is the OAuth resource server: it checks tokens and enforces scopes, and since June 2025, it does not issue tokens, full stop. The authorization server handles issuance and consent, and it can live on the same box as the resource server or somewhere else entirely.
Before that split, resource-server and authorization-server duties sat inside the same MCP server, which meant every team building a server had to implement OAuth correctly on its own. That's a lot of independent chances to get it wrong, and plenty of teams did. Separating the roles means authorization logic lives in one place, gets audited once, and behaves the same way no matter which server a client happens to be talking to.
Discovery works in two steps. An MCP server publishes a document at /.well-known/oauth-protected-resource under RFC 9728 and points to it via the WWW-Authenticate header on a 401 response. The client reads that, finds the authorization server, then fetches that server's own metadata from /.well-known/oauth-authorization-server under RFC 8414. No hardcoded configuration needed, which matters a lot once an organization is running dozens of servers instead of one.
PKCE with the S256 method became mandatory for every internet-facing MCP server as of November 2025, closing off the classic intercepted-authorization-code attack against public clients. And when a client needs to register, the spec now has a clear pecking order: use a pre-registered client first, then a Client ID Metadata Document, then Dynamic Client Registration, and only fall back to a human typing in details by hand as a last resort.
What OAuth scopes can and cannot express about what an agent is allowed to do
The spec asks servers to include a scope parameter in WWW-Authenticate challenges, to account for scope hierarchies when deciding whether a token clears the bar, and to bundle every required scope into a single challenge instead of making the client go back and forth. All reasonable. None of it solves the deeper problem with scopes themselves.
A scope string is a label, not a decision. read:tickets tells a server the agent is allowed to read tickets. It says nothing about which tickets, for which user, or under what conditions. There are two structural gaps here that matter a lot for agents specifically. First, the token belongs to the agent, not to the human standing behind it; OAuth has no standard field for "this agent is acting for this particular person," so agent scopes describe general permission, not permission tied to an individual. Second, scopes are coarse on purpose. They're broad categories, built for human-facing APIs where that granularity is fine. An agent running a multi-step workflow that touches dozens of resources needs something with a lot more resolution than that.
Resource Indicators help with one slice of this. Because RFC 8707 binds a token to a specific MCP server's audience, a token issued for one server can't be replayed against another, which matters once a deployment runs several servers side by side. But audience-binding only controls where a token works. It has nothing to say about what that token can do once it's inside the server, down at the level of an individual resource or record.
None of this makes scopes useless. It makes them a floor, not a ceiling. The patterns that follow, step-up authorization, Rich Authorization Requests, tool-level RBAC, exist because that floor isn't enough on its own.
Incremental authorization and step-up flows as a pattern for agents that discover permission needs at runtime
The lazy fix is to request every scope an agent might ever need, right at startup, so nothing interrupts the task later. It's also a bad fix. It produces tokens loaded with permissions the agent uses maybe once, which wrecks least-privilege and makes any audit trail close to meaningless, since a fat token tells you nothing about what actually got used.
SEP-835 gives MCP a better answer: incremental scope requests, built into the spec itself. A server can challenge for more scope mid-task instead of demanding everything up front. The mechanics are straightforward. If a token lacks what's needed for a given operation, the server returns a 403 with an insufficient_scope error. The client then computes the union of its current scopes and the new ones required, per SEP-2350, and kicks off a fresh authorization request that adds only what's missing, without losing anything it already had. The spec still wants every scope needed for an operation bundled into one challenge; forcing a client through several round-trips just to finish one task is bad design.
The payoff is real: an agent can start narrow and only pick up broader access when a task genuinely calls for it, with each step-up producing a visible consent event instead of a silent grant. Long sessions get handled through the standard offline_access scope for refresh tokens (SEP-2207), so an agent doesn't need to re-authenticate constantly just because it's been running a while.
None of this is free. Step-up flows add latency, and they require the orchestration layer running the agent to handle mid-task authorization redirects gracefully, which is harder than it sounds. Teams that skip this and just request broad scopes up front aren't wrong that it's simpler. They're trading security surface for smoother execution, and that trade tends to look fine right up until it doesn't.
Rich Authorization Requests as an alternative when flat scope strings are not expressive enough
RFC 9396, Rich Authorization Requests, swaps a flat scope string for a structured array called authorization_details. Each entry in that array carries a type, a set of allowed actions, locations, and specific resource identifiers. Instead of read:tickets, an RAR entry can say, in effect, read ticket 4471 in project Alpha, on behalf of user X. The token itself becomes a record of the actual decision, not just a category it fell into.
RAR earns its keep in a few specific situations. Agents working across a wide range of resource types, where scope strings would otherwise multiply into an unmanageable list. Multi-tenant setups where the same agent serves different users with different data boundaries. Regulated environments where the token has to double as auditable proof of exactly what was authorized, not a rough category of access.
It comes at a cost, though: RAR needs support from the authorization server and more careful parsing on both the client and server side. It's an emerging pattern in MCP deployments right now, not something every stack does by default. Together with tool-level RBAC, covered next, RAR represents one of two main ways teams are pushing authorization decisions down closer to the actual resource, rather than leaving them stuck at the coarse level scopes were built for.
Tool-level RBAC as the production pattern for enforcing least privilege at the operation boundary
Excessive agency is widely recognized as a critical risk in LLM deployments, and it breaks down into three causes that map onto MCP almost exactly: excessive functionality, meaning tools the agent doesn't need; excessive permissions, meaning more access than the task requires; and excessive autonomy, meaning high-stakes actions taken with no human checking in.
Tool-level role-based access control is how most production MCP deployments deal with this today. Every tool declares the scope or role it needs, and the server checks that before running anything. The decision about what an agent is allowed to do gets made when the tool is registered, not improvised at runtime based on whatever happens to be in the token.
A typical tool manifest pairs each tool name with a required scope: read_file needs file:read, write_file needs file:write, exec_shell needs shell:exec. Higher-risk tools get flagged confirmation_required: true, which forces a human into the loop before anything fires. Assigning scopes one by one to every user gets unmanageable fast once a deployment has any real size to it, so scopes get grouped into roles instead: a read-only role, a role that adds diagnostic access, a full-operator role, each one bundling the minimum set of permissions that job actually needs. Roles get assigned to agents, and to the humans or systems that authorize them, rather than handing out individual scopes piecemeal.
The real value here is that "what can this agent do" becomes a line in a manifest somebody wrote on purpose. It stops being an accident of whatever token happened to be floating around when the agent ran. RBAC handles enforcement at the moment of execution. What happens after execution, whether anyone can actually see what the agent did and hold it accountable, is a separate problem.
Why issuer validation and token-binding rules matter for multi-server deployments
Run enough MCP servers and mix-up attacks become a real threat: a malicious or just badly configured server tries to grab a token that was meant for a different server entirely. RFC 9207 issuer validation, made mandatory in the July 2026 revision through SEP-2468, closes that door. Clients now check the iss claim on authorization responses to confirm the token actually came from the authorization server they expected.
Credential binding, added under SEP-2352, ties registered credentials to the specific authorization server that issued them, so a credential from one can't get replayed against another. Resource Indicators do similar work at the token level, binding a token to one MCP server's URI, which means even a token that was issued correctly can't be used somewhere it wasn't meant to go.
For scope design, this has a practical consequence worth sitting with: scopes decided for one server don't carry over to another automatically. Each server is its own enforcement boundary, independent of the others, which means scope sprawl across a growing fleet of servers doesn't fix itself. It compounds, unless somebody's actively managing it. And that raises an organizational question that's easy to dodge: who actually owns the authorization server issuing tokens across the whole fleet, and does that person or team have real accountability for what agents end up touching?
How scope decisions compound across a deployment and what ungoverned accumulation looks like
Every new MCP server added to a deployment brings its own scope namespace, its own token audience, and its own RBAC surface. Without a central registry that tracks all of it, nobody responsible for the fleet actually knows what the aggregate permission footprint looks like. That's not a hypothetical risk; it's what happens by default once server count climbs into the dozens.
The usual pattern: an agent starts with a narrow set of scopes for a pilot project, picks up more as its use case grows, and those scopes almost never get revoked once the original use case ends or shrinks. A year in, a token's permission set tells you about the agent's history, not its current job.
Audit logs don't fix this on their own. A log of what an agent touched is genuinely useful after something's gone wrong, but it doesn't stop the access from happening in the first place. Catching a problem before it does damage requires real-time visibility into scope use and tool calls as they happen, not a record to sift through afterward.
A registry is the control that makes any of this manageable. Every MCP server that isn't in a registry is an identity the organization can't account for, and an identity nobody can account for is one whose scopes can't be reviewed, rotated, or pulled back when they should be. With more than 10,000 public servers already out there and enterprise fleets adding more every month, unregistered servers aren't a rare edge case anyone can afford to wave off. They're the default state unless someone builds the registry and actually keeps it current.


