Skip to main content
Guide

How to authorize AI agent MCP tool calls

Short answer: MCP's built-in authorization tells an MCP server who is calling. It does not decide which tool, with which arguments, on whose behalf is allowed. To authorize each tool call, put a policy check in front of every tools/call: send the agent, the human it acts for, the tool name, the arguments and the current context to a policy engine such as Open Policy Agent (OPA), deny by default, fail closed on errors, and keep a record of every decision.

What the MCP spec covers, and what it leaves to you

  • Authorization is optional in MCP. When it is used over HTTP transports, a protected MCP server acts as an OAuth 2.1 resource server and the MCP client acts as an OAuth 2.1 client. (MCP spec, Authorization)
  • MCP servers must validate that access tokens were issued for them as the intended audience (RFC 8707), and return HTTP 401 for invalid or expired tokens. (same source)
  • STDIO transports should not follow the HTTP flow and instead get credentials from the environment. (same source)

That gets you an authenticated, correctly scoped token. It does not answer questions like: may this agent call refund_payment for $9,000 right now, on behalf of this support rep, outside business hours? That is a per-action authorization decision, and it belongs in policy.

Step by step

  1. Add a policy enforcement point (PEP) in your MCP server middleware or MCP gateway, before the tool handler runs.
  2. Build the decision input for every tools/call: agent identity, the human or service it acts for, tool name, arguments, the token's validated claims, and context (time, environment, approval state).
  3. Ask the policy engine locally. Run OPA as a sidecar or library next to the server so the check doesn't add a network hop.
  4. Deny by default and fail closed. If the engine errors or times out, the tool call doesn't run.
  5. Require human approval for high-impact tools. Mark tools like payments, deletes and permission changes as high impact and require a recorded approval from someone other than the requester.
  6. Record every decision with its input and result, so you can show later what each agent was allowed to do and why.
  7. Re-check when context changes, for example a revoked role or an expired approval, instead of trusting one earlier allow.

Example Rego policy

rego
package mcp.authz
import rego.v1

default allow := false

# Analysts' agents may read inventory, never write.
allow if {
  input.method == "tools/call"
  input.tool.name == "get_inventory"
  "analyst" in data.principals[input.on_behalf_of].roles
}

# High-impact tools need an approval from a different person.
allow if {
  input.method == "tools/call"
  input.tool.name in data.high_impact_tools
  input.context.approval.status == "approved"
  input.context.approval.approver != input.on_behalf_of
}

OAuth scopes vs. policy checks

ComparisonOAuth scopes (MCP spec)Per-call policy check
DecidedWhen the token is issuedAt each tool call
Sees tool argumentsNoYes
Knows who the agent acts forOnly if encoded in the tokenYes, as decision input
Can require human approvalNoYes
Decision recordToken issuance logsOne record per tool call

Use both: scopes to authenticate and bound the client, policy to decide each action.

Where EnforceAuth fits

EnforceAuth is an OPA control plane for apps, APIs, people and AI agents. It extends OPA; it doesn't replace the OPA runtime. Writ governs Rego policy, bundles and audit. Verdict requires human approval for high-impact agent actions. Evidence IQ keeps a durable, searchable record of authorization decisions; it records decisions and never makes or changes them. Herald is EnforceAuth's authorization MCP server, so agents and IDEs can query and operate the same policy control plane as humans. Free tier: 1 million authorization decisions a month. (MCP authorization server, AI agent authorization, Pricing)

Common questions

Direct answers

No. It defines how clients get and present OAuth 2.1 access tokens to MCP servers over HTTP. Deciding whether a specific tool call with specific arguments is allowed is up to the server, typically through a policy engine.

Close the gap with policy-as-code

Free tier includes 1M authorization decisions / month. No card required.