⟨ INCOMING TRANSMISSION ⟩ 200,000 MCP instances exposed by April 2026 security disclosure (OX Security) · 97M monthly MCP SDK downloads, up from ~2M at launch (Anthropic, Mar 2026) · RSAC 2026: $392M raised in agentic security in one week · EU AI Act fully applicable August 2026 · Microsoft (Apr 2026): MCP tool execution needs a control plane · sources: sentnelops.com/research/mcp-landscape · ⟨ INCOMING TRANSMISSION ⟩ 200,000 MCP instances exposed by April 2026 security disclosure (OX Security) · 97M monthly MCP SDK downloads, up from ~2M at launch (Anthropic, Mar 2026) · RSAC 2026: $392M raised in agentic security in one week · EU AI Act fully applicable August 2026 · Microsoft (Apr 2026): MCP tool execution needs a control plane · sources: sentnelops.com/research/mcp-landscape ·

[ LEARN // MCP SECURITY ]

MCP authentication vs authorization

In MCP, authentication proves who is connecting — the spec standardizes this with an OAuth 2.1-based authorization framework between clients and servers. Authorization decides what an authenticated caller may do on each individual tool call — and the spec deliberately leaves most of that to implementers. Conflating the two is how deployments end up with authenticated agents that can do anything.

[ WHAT THE SPEC GIVES YOU ]

The Model Context Protocol does not ignore security. Its authorization specification defines a real, standards-based framework for the client-to-server boundary, built on OAuth 2.1: the MCP server acts as an OAuth resource server, the client obtains an access token from an authorization server, and the MCP server validates that token before serving the connection. This is a genuine achievement — before it, every remote MCP server invented its own credential scheme, and every client had to speak all of them.

What this buys you is precise: you know who connected. A validated token binds the session to a principal, servers can reject strangers outright, and token scopes let a server require that a connection was authorized for a broad purpose before any tool becomes reachable. For the question the framework was designed to answer — should this client be talking to this server at all? — it is a good answer, and deployments should use it.

A standard way to prove identity

OAuth 2.1 flows between MCP clients and servers, with the server acting as an OAuth resource server. Clients obtain tokens from an authorization server; MCP servers validate them. No bespoke auth scheme per server.

A known principal per connection

A validated token binds the connection to a principal. The server knows which client — and, through the authorization server, which user or workload — is on the other end. Strangers are rejected before any tool is reachable.

Scopes at the connection level

Tokens can carry scopes that bound what a connection is for, and servers can refuse connections whose tokens lack the scopes they require. Coarse-grained, but standard and interoperable.

[ WHAT IT DOESN'T ]

Connection-level scopes are not per-call decisions. Scopes are attached to a token when it is issued and checked when the connection is established; they answer what is this connection for, in broad strokes, once. After that, most MCP servers execute whatever tool call the connected client sends. The spec defines how to establish who is calling — it does not define a standard place to decide, call by call, whether the caller may do this.

Concretely: there is no standard way to express "deploy-bot may merge pull requests, but only outside main" or "this agent may query, but never against production". Those are decisions about a specific tool, with specific parameters, at a specific moment — and nothing in the OAuth exchange carries them. Each server either invents its own rules or executes everything its authenticated clients send.

This matters because authenticated ≠ safe. An agent steered by prompt injection is still perfectly authenticated while it calls delete_repository. Its token is valid, its scopes check out, its connection was legitimately established — and none of that stops the call, because none of it was ever asked about the call. The gap between "who are you" and "may you do this, now, with these parameters" is exactly where agent incidents live.

[ WHERE PER-CALL AUTHORIZATION CAN LIVE ]

If the spec leaves per-call decisions to implementers, an implementer has three realistic places to put them. They are not equivalent — and they are not mutually exclusive.

Inside each MCP server

Every server checks its own rules before executing a tool call. It works, and it is the only layer that sees the server's internal state. The cost: policy fragments across N codebases in N languages, with no way to review your authorization posture as a whole — auditing it means reading every server.

Fine for one server. Unreviewable at ten.

At a gateway

MCP gateways centralize routing, aggregation, and credential brokering — one endpoint in front of many servers. But gateways broker connections; most are not built to judge individual calls against per-agent policy, pause a call for approval, or record why a call was allowed.

Centralizes the plumbing, not the decision.

At an inline enforcement point

A proxy — an MCP firewall — between client and servers that intercepts every tool call, evaluates it against one policy, and permits, blocks, or pauses it before the server sees it. One policy covers every server, lives outside the agent process, and is reviewable as a single artifact.

One policy, every server, outside the agent.

The first and third options compose well: a server's own checks enforce invariants only it can know, while the inline layer enforces the organization-wide policy no single server can see. This layered posture is the core of AI agent runtime governance applied to MCP: the decision happens in the request path, before the action, against policy that exists outside the agent.

[ WHAT IT LOOKS LIKE IN PRACTICE ]

Suppose deploy-bot has authenticated cleanly through the spec's OAuth flow. Per-call authorization then picks up where the token check stops: it may create branches and open pull requests, a merge to main pauses for a human, and everything else is denied. In YAML:

# Authentication established who is calling. Authorization starts at deny.
- agent: "*"
  server: "*"
  tool: "*"
  action: block

# deploy-bot may create branches and open pull requests.
- agent: deploy-bot
  server: github
  tool: [create_branch, open_pull_request]
  action: permit

# Merging to main is a different decision — a human makes it.
- agent: deploy-bot
  server: github
  tool: merge_pull_request
  when: { branch: main }
  action: require_approval

Per-call authorization: the token said who; the policy says what, when, and with which parameters.

Note what makes the first line of every rule possible: the identity that authentication established is the subject of every authorization decision. "deploy-bot" is not a comment — it is the join point between the two layers. Per-agent identity is what lets a policy distinguish deploy-bot from changelog-bot, and what lets the audit trail attribute each decision to exactly one actor. Without it — with a shared credential — authorization collapses back into "whoever holds the token may do whatever the token allows", which is authentication wearing an authorization costume.

[ BEST PRACTICES ]

  • Treat spec-level OAuth as necessary, not sufficient. It answers "who is connecting"; deployment is not done until something answers "may this caller make this call, with these parameters, right now".
  • Give every agent its own identity — not a shared service account. Authorization needs a real subject; rules written against "whoever holds this token" govern nothing.
  • Default-deny per tool call. An authenticated connection should confer zero call rights until a rule grants them explicitly, per agent, per server, per tool.
  • Write conditions on parameters, not just tool names. "May call merge_pull_request" and "may merge to main" are different grants; the difference is where incidents live.
  • Gate irreversible operations behind human approval rather than trusting that an authenticated agent will only attempt reasonable things.
  • Log every decision with the agent identity attached — who called, what was attempted, what the policy decided, and which rule matched — so the trail shows both who and what-was-allowed.

[ HOW SENTNELOPS IMPLEMENTS THIS ]

SentnelOps supplies the per-call authorization layer that the MCP spec leaves to implementers: an MCP firewall deployed as a proxy in your own VPC between agents and MCP servers. Every agent gets its own identity with zero permissions by default, and every tool call is evaluated against YAML runtime policy before the server sees it — permit, block, or require_approval via Slack — in under 15ms p99, with each decision logged against the agent identity that made the call. Your spec-level authentication stays exactly as it is; SentnelOps adds the per-call decision on top.

[ FREQUENTLY ASKED QUESTIONS ]

What is the difference between MCP authentication and authorization in one sentence?

Authentication proves who is connecting to an MCP server — standardized by the spec's OAuth 2.1-based framework — while authorization decides what that authenticated caller may do on each individual tool call, which the spec largely leaves to implementers.

Does the MCP spec handle authorization?

Partially. The spec standardizes an OAuth 2.1-based authorization framework that governs who may connect: clients obtain tokens, servers validate them, and connections without valid credentials are rejected. Per-tool-call decisions — whether this specific agent may invoke this specific tool with these specific parameters — are deliberately left to implementers.

If my MCP server checks OAuth scopes, am I done?

No. Scopes are evaluated when the connection is established and are coarse by design. They cannot express per-call rules like "may open pull requests but not merge to main" or "may query, but never against production", and they do nothing once a legitimately authenticated agent is steered by prompt injection. You still need a layer that evaluates each call.

Where should per-call authorization live?

The realistic options are inside each MCP server, at a gateway, or at an inline enforcement point (an MCP firewall) between client and servers. In-server checks work but fragment policy across codebases; gateways centralize routing rather than per-call judgment; an inline proxy applies one reviewable policy to every server from outside the agent process. In-server checks and an inline proxy compose well as two layers.

How does agent identity tie authentication and authorization together?

The identity established by authentication becomes the subject of every authorization rule. If each agent authenticates as itself, policy can say what deploy-bot may do as opposed to what anyone holding a shared credential may do, and every logged decision names the agent that triggered it. With shared credentials, both layers degrade at once: authorization loses its subject and the audit trail loses attribution.

← All learn articles