What is a catch-all scope in MCP, and why is it the default?

A catch-all scope is one OAuth scope that covers an entire MCP server. The agent authorizes once, gets back a token good for every tool the server exposes, and never asks again. It is the path of least resistance, and the MCP authorization spec makes it easy to take. The spec builds MCP servers as OAuth 2.1 resource servers on top of RFC 8414, RFC 7591, and RFC 9728, but it never mandates what a scope should mean. Descope's read of the spec is blunt about the gap: "the specification doesn't define scopes, implementors must decide, a fixed scope like access_mcp may suffice in some cases" (Descope, MCP Authorization Specification).

So teams ship the simplest thing that passes a login test. One scope, one token, one yes/no gate at the front door. It works in a demo. It fails the moment the server exposes more than one tool with more than one blast radius, which is every real deployment.

What goes wrong with a single catch-all scope

Two failures show up almost immediately, and they compound.

One scope means one blast radius for every tool. If a server bundles a read_customers tool and a read_billing tool under the same scope, an agent that only needed the first can reach the second. Nothing in the token says otherwise. WorkOS puts the fix as plainly as the problem: "scopes have to be per tool, not per server, access to the MCP server is not a useful permission" (WorkOS, MCP Authorization Patterns). An agent that can read one table can read all of them, because the token never distinguished between tables in the first place.

Prompt injection inherits the whole scope. An attacker does not need to steal a credential. They only need to get malicious text in front of the model, in a chat message, an email, or a document the agent reads. Stytch's own MCP threat writeup shows how thin the bar is: a crafted message was enough to make an agent leak a user's contact list, no custom server required (Stytch, How to Secure Model-Agent Interactions Against MCP Vulnerabilities). Once the model's next action is compromised, it acts with whatever the token already allows. A catch-all scope hands the attacker the whole server. A narrow, per-tool scope hands them one tool, and only the one the hijacked step was already using.

This is exactly why OWASP's Agentic Top 10 puts goal hijack at the top of its list: an attacker who seizes an agent's decision-making rides every permission that decision-making already carries. The scope is the ceiling on how bad that ride gets.

Per-tool scopes best practices

A short list, in the order it earns its keep.

  • Don't use catch-all scopes. One scope for an entire MCP server means one compromised tool call is a compromised server. Treat "access to the MCP server" as a permission that does not mean anything, because it doesn't.
  • Scope per tool, not per server. Give the read_customers tool a scope, give write_invoices a different one, and never let a token cover both unless the agent's job genuinely needs both. Descope frames this the same way: validate scopes "at the tool or function level," checking for something specific like a view scope rather than a blanket grant (Descope).
  • Make the ceiling non-negotiable. A scope is not advice the agent can talk its way past. It has to be enforced the same way regardless of who or what is asking, admin, service account, or delegated agent. If a role can talk its way to a wider scope than the token carries, the token was never a ceiling.
  • Revoke mid-session. Authorization is not a one-time check at login. An agent's access can and should change while it is still running, the moment a session looks wrong or a human pulls the plug. A token that stays valid for the life of a long agent run is a token that stays valid after you decide it shouldn't.
  • Log the decision, not just the call. A record that says "agent called read_customers" is thinner evidence than one that says "agent called read_customers, scope check passed, here is the token and the tier that allowed it." The second record is what tells you, after an incident, whether the system worked as designed or slipped.

The audit trail an agent can't quietly rewrite

Scoping stops most of the damage. It does not answer the next question a security review asks, which is: prove it. Prove the scope held, prove the revocation landed before the next call, prove no one edited the record afterward. A tidy log an admin (or a hijacked agent with admin-adjacent reach) can edit without detection is not proof of anything. It is a story someone can rewrite.

That is the harder half of MCP authorization, and it is the half most teams skip because scoping is the visible part and evidence is the part nobody asks for until an incident forces the question.

How DataShield Auth implements scope-as-ceiling tokens

DataShield Auth treats the scope not as a preference the agent can argue with, but as a ceiling enforced by construction. Every governed tool call runs the same dispatch pipeline: token scope ceiling, then declared authority tier, then a mid-session revocation re-check, then metered dispatch, then a sealed audit entry. A delegated key can never confer more authority than its owner intended, even for a global admin, because the ceiling sits in the dispatch path itself, not in a convention someone has to remember to follow.

Mid-session revocation is not a euphemism. Revoke or suspend an agent and the change is caught on its very next tool call, the session context is downgraded mid-flight rather than at the next login. And every one of those decisions, ceiling check, tier check, revocation re-check, gets sealed into a hash-chained audit log with signed checkpoints. Verification distinguishes tampering, insertion, deletion, and truncation as separate findings, so an attempt to quietly remove a record shows up as a deletion, not silence. That is the difference between a log you hope holds up and one you can independently check at /verify.

The Cedar policy engine handles the admin, platform, config, and token decisions on top of that pipeline, with policy_explain so a reviewer can see exactly why a call was allowed or denied, not just that it was. None of this claims to be a public MCP gateway or an inline content filter sitting in front of every request. It is authorization and evidence at the dispatch point: the scope ceiling holds regardless of role, the mid-session check catches a revoked agent before its next call lands, and the record of every decision is chained so it can't be quietly edited after the fact. More on how the pieces fit at /architecture.

What a per-tool review actually looks like

If you inherited an MCP server with one catch-all scope, the fix is not a rewrite. It is a pass through the tool list.

First, list every tool the server exposes and write down, honestly, what each one can reach: which tables, which write paths, which downstream systems. Second, group tools by blast radius, not by convenience. A read-only lookup and a payment write do not belong in the same scope just because they live in the same codebase. Third, cut the token down to the narrowest set of scopes the agent's actual job requires, and make the ceiling apply at dispatch, not in a comment or a wiki page. Fourth, wire revocation into the same path so a suspended agent loses reach on its next call, not its next login. Fifth, seal every authorization decision into a record nobody, including the people running the server, can quietly edit.

Do the first two steps on paper before touching any code. Most of the actual risk reduction happens in that grouping exercise, because it is where a team discovers how many unrelated capabilities got bundled under one scope out of convenience rather than design.

Related reading

Product

DataShield Auth

Scope-ceiling tokens, per-call authorization, mid-session revocation, and a hash-chained audit log.

MCP authorization

Databricks MCP Server Authorization

What Databricks' MCP server authorizes today, and where a scope ceiling closes the rest of the gap.

MCP authorization

Snowflake MCP Server Authorization

Snowflake's native MCP authorization model, and what per-tool scoping adds on top of it.

One scope per server is easy to ship and easy to regret. Scope per tool, cap it at dispatch, and prove it with a chain nobody can quietly edit.

See DataShield Auth

Frequently asked questions

What is a catch-all scope in MCP authorization?

A catch-all scope is a single OAuth scope that covers an entire MCP server rather than individual tools. One authorization grants access to every tool the server exposes, so a token meant for one low-risk tool also works for every higher-risk tool on the same server.

Why do most MCP servers default to one scope instead of per-tool scopes?

The MCP spec builds servers as OAuth 2.1 resource servers but leaves scope design to implementors. Descope's reading of the spec notes that a fixed scope like access_mcp can technically pass, so teams ship the simplest working setup, one scope, one token, and skip the harder work of scoping each tool separately.

How does prompt injection turn a catch-all scope into a bigger breach?

Prompt injection hijacks the agent's next decision, not its credentials. Once hijacked, the agent still acts with whatever its token already allows. Stytch has shown a single crafted chat message was enough to leak data with no custom malicious server involved. A catch-all scope means that hijacked step can reach every tool on the server instead of just the one it was using.

How do I revoke an MCP agent's access mid-session instead of waiting for it to log in again?

The revocation check has to run on the agent's next tool call, not only at token issuance. DataShield Auth's dispatch pipeline re-checks revocation on every governed call, so suspending an agent downgrades its session context mid-flight, on the very next call it makes, rather than after its next login.