Skip to main content
Version: Latest

AI Agents and MCP

The story in one paragraph

When your developers use Claude Code, OpenCode, or any MCP-capable agent, the agent acts with the developer's credentials — SSH keys, cloud tokens, database passwords. There is no agent identity, no per-agent audit, no policy at the tool-call boundary. PBAC fixes this with an MCP Gateway: one Policy Enforcement Point between every MCP client and every backend tool, enforcing policy on every call, logging every decision, usable with every OAuth-compliant MCP client. See The Agent Access Problem for the security framing, or Start Here for the dashboard onboarding.


What changes after adopting the Gateway

BeforeAfter
Claude Code runs as the developer, inheriting all their grantsClaude Code authenticates as a distinct OAuth client, scoped to a policy-defined subset
CI bot shares a service account with deploy jobsCI bot registers with a software statement; policy distinguishes "CI" from "deploy"
Audit log shows "human user read row X" — agent invisibleAudit log shows {agent, tool, scope, decision, timestamp} per call
Adding a new tool means writing new authorization codeAdding a tool means writing one scope; policy composition handles the rest
Revoking access requires the human to log out / rotate keysPolicy data change → next tool call reflects it, same token, no reissue

The architecture

Three properties make this safe:

  1. Opaque tokens + introspection. The token is not a JWT — it is an AS-issued opaque handle. Every call triggers a fresh OPA evaluation. Policy changes take effect on the next call.
  2. One PEP for every client. Claude Code, OpenCode, Cline, Continue, and custom MCP clients all hit the same gateway. One policy surface, all agents.
  3. Scope at the tool-call boundary. Your backends do not have to implement OAuth. The gateway enforces; backends stay simple.

Agent trust tiers

Agents register via Dynamic Client Registration (RFC 7591) with a signed software statement carrying claims like agent_provider and agent_model. Policy data maps providers to trust tiers:

{
"agents": {
"trust_tiers": {
"internal_trusted": ["claude-code-trusted", "our-internal-scanner"],
"developer": ["claude-code"],
"ci_agent": ["ci-bot"],
"read_only": ["security-scanner"]
}
}
}

OPA evaluates the provider claim on every /token and every /introspect. Scope is never wider than the tier allows — and the tier can change without the agent knowing.

Example trust-tier matrix

AgentProvider claimTierDriveJiraSlackDDL
Alice's Claude Codeclaude-codedeveloperr/wr/wr/wdenied
CI botci-botci_agentreadr/wdenieddenied
Security scannersecurity-scannerread_onlyreadreaddenieddenied
Autonomous plannerclaude-code-trustedinternal_trustedr/wr/wr/wdenied

Every row is a policy decision, enforced at the gateway, adjustable by changing one row in policy data.


Single-use tokens for high-risk agents

A software statement can carry single_use_access_tokens: true. When OPA returns jit_single_use: true in the introspect output, the AS deletes the token after the first successful introspection. A compromised scanner token is useless on its second use.


Delegation (agent → sub-agent)

When a primary agent needs to spawn a specialized sub-agent, it uses RFC 8693 Token Exchange. The sub-agent gets a composite token carrying:

  • sub — the original human
  • act.sub — the delegating agent
  • aud — the sub-agent
  • scp — scopes narrowed to what the sub-agent needs

OPA validates the whole chain: subject valid, actor authorized to delegate, audience a registered client, scopes a subset of the subject grant. See Token lifecycle for the full delegation model.


Live end-to-end demo

Every PBAC tenant ships with a ready-to-use MCP demo — five pre-seeded backends (Drive, Jira, Slack, Jenkins, GitLab), a pre-provisioned gateway, and three demo users (engineering, analytics, contractor) with distinct role tiers.

From the dashboard, pick Demo → MCP Gateway in your instance. You'll land on a narrative UI where you can run prompts as different users and watch tool calls hit 200s and 403s in real time.

To connect Claude Code to the demo gateway, copy the claude mcp add snippet from the demo page. It's pre-filled with your tenant's gateway URL and MCP client ID — one command, then start Claude Code and type a prompt.

Need offline / self-hosted evaluation?

Regulated or air-gapped environments can run PBAC in their own infrastructure. Contact us to discuss self-hosting.


Common questions

Q: Does the gateway work with stdio MCP transport? A: The canonical integration is HTTP. MCP's OAuth extension is designed for HTTP transports. If you are running Claude Code / OpenCode purely in stdio mode, the gateway still protects the backends — but you will be speaking HTTP between the client and the gateway.

Q: How does this compare to putting auth in each backend? A: One gateway is one PEP. Per-backend auth is N PEPs with N configurations. When policy changes, the gateway reflects it on the next call — N backends need N deploys.

Q: Does this add latency? A: One extra HTTP round-trip per tool call (client → gateway → backend). The AS /introspect is on the gateway's path. In our load tests, the p50 overhead is < 5ms.

Q: Can policy depend on tool arguments, not just scope? A: Yes. The gateway forwards the tool's arguments to OPA as part of the introspection context. Rego can inspect them. See the policy model for how request context flows into Rego.


Next steps