Skip to main content
Version: 1.0

AI agents and MCP integration

The challenge

Traditional OAuth 2.0 was designed for humans delegating to applications. AI agents break that model in several ways:

  • No principle of least privilege by default — long-lived, broadly scoped tokens are the norm, not the exception.
  • No delegation chain visibility — when an agent calls a downstream agent, there is no standard record of who authorized what.
  • Privilege escalation risk — agents that can register new clients or request broad scopes can exceed their intended authorization.
  • No trust differentiation — agents from different providers, with different risk profiles, get treated identically.
  • Sparse audit trails — without structured token metadata, you cannot reconstruct what an agent did or why it was allowed.

How PBAC handles it

Trust tiers for agent clients

Agent identity is carried in a software statement — a signed JWT presented at Dynamic Client Registration (DCR). The statement can include agent-specific claims such as agent_provider and agent_model. Policy data defines which providers belong to which trust tier:

{
"agents": {
"untrusted_providers": ["untrusted-provider"],
"semi_trusted_providers": ["semi-trusted-provider"]
}
}

OPA evaluates these tiers at token issuance time. A semi-trusted agent requesting mcp:tools receives a 403 access_denied — no code change, no redeployment. Adding a new provider tier means updating a data row.

Scoped, time-bound tokens

Agent tokens are issued with reduced TTLs (e.g. expires_in=300) and constrained to the exact scopes and resources declared in the software statement. The jit_single_use flag in the OPA output causes the AS to delete the token after the first successful introspection — agents cannot reuse a token after the resource server has validated it.

MCP server protection

An MCP server protects its endpoints by calling the AS introspect endpoint on every request:

  1. The agent presents a Bearer token.
  2. The MCP server calls POST /introspect with its own RS credential.
  3. The AS re-evaluates the OPA token policy, merging any RS-supplied context (resource type, scopes).
  4. The AS returns active: true/false plus any obligations from the policy output.
  5. The MCP server allows or denies the request.

This means the policy fires on every tool call, not just at login. If policy data changes (e.g., a provider is downgraded), the change takes effect at the next introspection — without touching the agent or the MCP server.

The mcp-demo in this repository is a working end-to-end demonstration of this flow.

Delegation chains

note

Token exchange (subject token validation, scope narrowing, id_token exchange) is implemented. The actor_token parameter for full composite tokens with act.sub is planned — see RFC 8693 status.

When a primary agent needs to sub-delegate work to a specialized downstream agent, it uses the OAuth 2.0 Token Exchange grant. The downstream agent receives a composite token that carries:

  • sub — the original human whose data is being accessed
  • act.sub — the delegating agent
  • aud — the downstream agent
  • scp — scopes down-scoped to exactly what the downstream agent needs

OPA validates the full chain before issuing the composite token: the subject token must be valid, the actor must be authorized to delegate, the audience must be a registered client, and the requested scopes must be a subset of the subject grant.

See the Token exchange guide for the full API flow.


Architecture


Example: protecting an MCP server

The mcp-demo walks through the full lifecycle:

Step 0 — Discovery. The agent calls the MCP metadata endpoint (no auth required) to discover the AS token endpoint.

Step 1 — Get a token. The agent registers via DCR (or reuses a cached client_id) and requests a token with client credentials:

POST /token
grant_type=client_credentials
scope=mcp:tools mcp:resources
resource=http://localhost:5200/*
resource_types=urn:mcp:server

OPA evaluates the agent's agent_provider claim against the trust tier data. A trusted provider receives a token with expires_in=300. An untrusted provider receives 403 access_denied.

Step 2 — Call tools/list. The agent calls the MCP server with the Bearer token. The MCP server introspects, OPA approves, and the tool list is returned.

Step 3 — Call tools/call. The agent invokes a tool (e.g. get_time). The MCP server introspects again with the execute scope context — policy fires on every call.

Step 4 — No token. A call without a Bearer token receives 401 with a WWW-Authenticate header pointing to the discovery endpoint.

To run the demo locally:

./mcp-demo/run.sh

Then open http://localhost:5100. See mcp-demo/README.md for full configuration options.


Trust tier matrix

The agentic workflow test suite validates the following policy enforcement:

AgentProvidermcp:toolsmcp:resourcesCorporate APIs
TrustedidentosAllowedAllowedread
Semi-trustedsemi-trusted-providerDeniedAllowedread
Untrusteduntrusted-providerDeniedDeniedDenied

Policy denials are enforced by agents.rego at token issuance; no change to the MCP server or the agent is required to adjust a tier.


Next steps