Skip to main content
AI Security

Securing MCP: OAuth Is the Floor, Not the Ceiling

A working security model for MCP clients and servers: audience-bound tokens, least privilege, consent separated from tool calls, server-side argument validation, and audit trails you can actually read.

Jordan Reyes
Jordan Reyes · 9 min read
Securing MCP: OAuth Is the Floor, Not the Ceiling

MCP made tools dramatically easier to discover and connect. That is the point, and it is genuinely useful. It also means a lot of teams are now wiring authorization between a client, a resource server, and an authorization server for the first time — and taking shortcuts between them that produce exactly the token leakage and confused-deputy problems the standards exist to prevent.

Bind every token to the resource it was issued for

A token minted for one service must not be accepted by another. Validate issuer, audience, expiry, scopes, and intended resource on every single request — not at session start, not at connection setup, every request. And never forward a client’s token to an unrelated downstream API because it happened to be in hand.

That last one is the failure that keeps recurring. A tool server receives a token, needs to call a third-party API, and passes the token straight through. Now a service the user authorized for one narrow purpose is holding a credential that works somewhere else entirely. Exchange tokens properly or hold separate credentials — do not relay.

  • Use the OAuth 2.1 flow appropriate to the client type, including PKCE for public clients
  • Request the narrowest scopes the current job needs, not the widest the user would approve
  • Store tokens outside model-visible context and redact them from every trace and log
  • Validate tool arguments server-side even when — especially when — the model produced them
  • Keep user consent separate from the agent’s decision to invoke a tool

Authorization is not tool policy

This distinction is where most MCP deployments are thin. A valid token proves the caller is permitted to reach a capability. It says nothing about whether this particular invocation, with these arguments, at this moment, is appropriate.

A token scoped to send email does not mean every email is fine to send. A token scoped to read records does not mean exporting all of them is fine. You need a second layer: application policy covering dangerous parameter values, rate limits, data classification, approval thresholds, and hard tenant boundaries. That layer lives on the server, and it is the one that saves you.

Treat model-generated arguments as user input

The schema you publish is a hint to the model, not a guarantee about what arrives. Validate types, ranges, identifiers, and cross-field consistency on the server exactly as you would for a public HTTP endpoint. A tool that accepts an arbitrary path, an arbitrary query, or an arbitrary destination is an injection surface — the fact that a model composed the string is not a defense, it is an amplifier.

Make the audit trail legible to a human

When something goes wrong, someone will need to reconstruct it under time pressure. Record who authorized access, which scopes were granted, which tool ran, what validated arguments reached the server, and what changed as a result. Correlate it all under one task ID so the story reads in order.

Logs should support an investigation without becoming a second copy of your secrets and personal data. If your audit trail is itself a breach risk, it is not finished.

Primary sources

First-party documentation and announcements used to ground this field note.

MCPOAuth 2.1Agent SecurityTool Use
Jordan Reyes
Jordan ReyesFull-Stack Engineer · Zehnai