Agent + multiple MCPs (Direct fanout)
At a glance. One agent talks to N MCPs directly. The user consents at each MCP separately. Each token’s
audis bound to its target MCP — cross-MCP token replay is rejected at audience validation. No infra between agent and MCPs. Simplest multi-resource shape.
Topology
flowchart TD
AS["AuthPlane"]
MCPA["MCP A<br/>read"]
MCPB["MCP B<br/>query"]
MCPC["MCP C<br/>write"]
Agent["Agent"]
AS --> MCPA
AS --> MCPB
AS --> MCPC
Agent -->|Bearer per-MCP| MCPA
Agent -->|Bearer per-MCP| MCPB
Agent -->|Bearer per-MCP| MCPC
Each of MCP A / B / C is registered as its own Mint Resource with its own scope catalog. The agent is one OAuth client that gets a separate audience-bound token per MCP.
Flow
Per-MCP flow is identical to single-mcp. The user gets N consent screens on first use — one per MCP resource.
sequenceDiagram
participant Agent
participant MCP
participant AS as AuthPlane
participant User
loop For each MCP in {A, B, C}
Agent->>MCP: GET /.well-known/oauth-protected-resource
MCP-->>Agent: PRM
Agent->>AS: /oauth/authorize?resource=mcp-X&scope=…
AS-->>User: consent screen for THIS mcp-X
User->>AS: approve
AS-->>Agent: 302 + code
Agent->>AS: /oauth/token
AS-->>Agent: token with aud=mcp-X
Agent->>MCP: call with Bearer(aud=mcp-X)
end
Token from MCP A replayed to MCP B is rejected — aud claim doesn’t match MCP B’s resource URI. This is the audience-binding guarantee of RFC 8707.
When to use
- Multiple independent MCP servers with distinct scope catalogs.
- User accepts per-MCP consent (the default; usually fine).
- No gateway, no infrastructure between agent and MCPs.
Don’t use when:
- User wants one consent screen for all MCPs → wait for RFC 8707 multi-resource authorization (on the roadmap), or use mcp-gateway-mint with one consented gateway.
- MCPs need to reach each other with an agent-attributed token → mcp-gateway-mint.
- Any MCP wraps an upstream IdP → mix in broker-mcp for that one.
How to configure
CLI:
# 1. Register every MCP
authserver admin resource create --slug mcp-a --backend-kind mint --uri https://mcp-a.example.com --scopes 'read||Read access'
authserver admin resource create --slug mcp-b --backend-kind mint --uri https://mcp-b.example.com --scopes 'query||Query access'
authserver admin resource create --slug mcp-c --backend-kind mint --uri https://mcp-c.example.com --scopes 'write||Write access'
# 2. Register the agent (one client across all MCPs)
authserver admin client create \
--name my-agent \
--grant-types authorization_code,refresh_token \
--auth-method none \
--scopes 'read||Read access' \
--scopes 'query||Query access' \
--scopes 'write||Write access'
YAML seed:
resources:
- { slug: mcp-a, backend_kind: mint, uri: https://mcp-a.example.com, scopes: [{name: read}] }
- { slug: mcp-b, backend_kind: mint, uri: https://mcp-b.example.com, scopes: [{name: query}] }
- { slug: mcp-c, backend_kind: mint, uri: https://mcp-c.example.com, scopes: [{name: write}] }
How AuthPlane handles it
Each MCP is a separate resource with its own audit trail. consent_grants has one row per (user, agent, mcp-X). One agent client, three separate consent states — revoking consent for MCP-A doesn’t affect MCP-B or MCP-C.
Every token is minted with the exact URI of its target as aud. The SDK on each MCP verifies aud matches its own URI — cross-MCP replay fails audience validation without any coordination between MCPs.
Verify
# List issuances per resource
authserver admin issuance list --resource mcp-a --client my-agent
# Grants per user — should show 3 rows if all three MCPs have been consented
authserver admin grant list-user-grants --user user-42
See also
- Choose your topology
- Concepts: Resource servers & PRM — audience binding under the hood
- Topologies: MCP gateway → hidden Mint — the alternative when you want one consent for many MCPs
- Reference: RFC 8707 — resource-indicator semantics