Folded resource
At a glance. Multiple internal services live behind one MCP boundary. AuthPlane sees one Resource (
mcp-a). The MCP internally calls other services using its own mesh auth (mTLS, service tokens, whatever) — AuthPlane doesn’t participate in the internal hop. Simplest way to compose internal microservices behind an MCP without exposing each as a separate resource.
Topology
flowchart TD
Agent["Agent"]
MCP["MCP-A<br/>(visible to AuthPlane)"]
Svc1["Svc1<br/>(int.)"]
Svc2["Svc2<br/>(..)"]
Svc3["Svc3<br/>(..)"]
Agent -->|"Bearer<br/>aud=mcp-a"| MCP
MCP -->|"mesh auth<br/>(mTLS, SA token, etc.)<br/>invisible to AuthPlane"| Svc1
MCP -->|"mesh auth<br/>(mTLS, SA token, etc.)<br/>invisible to AuthPlane"| Svc2
MCP -->|"mesh auth<br/>(mTLS, SA token, etc.)<br/>invisible to AuthPlane"| Svc3
Only MCP-A is a Resource. Svc1/2/3 are AuthPlane-invisible — their mesh auth is your own concern (Istio, Linkerd, SPIFFE, whatever your infra provides).
Flow
sequenceDiagram
participant Agent
participant MCP as MCP-A
participant Svc1
participant Svc2
participant Svc3
Agent->>MCP: POST /mcp Authorization: Bearer <token, aud=mcp-a>
MCP->>MCP: validates the token against AuthPlane's JWKS (cached)
MCP->>MCP: tool handler decides internally to fan out
MCP->>Svc1: mesh call (mTLS SAN=svc1, no AuthPlane involvement)
MCP->>Svc2: mesh call
MCP->>Svc3: mesh call
MCP-->>Agent: assembles response, returns to Agent
Steps 3a-3c are outside AuthPlane’s model — the internal hop is your service mesh’s concern.
When to use
- Backend services are internal implementation details of one MCP.
- You don’t want per-service consent / per-service audience — the boundary is the MCP.
- You have a service mesh with strong internal auth already.
- Fan-out scopes are decided by tool-handler logic, not by user consent.
Don’t use when:
- Users need visibility/consent into individual downstream services → each service is its own Resource, use direct-fanout.
- The internal hop wants an agent-attributed audit trail → use mcp-gateway-mint instead — the gateway becomes an OAuth client that hits real Mint resources with delegated tokens.
How to configure
Only the outer MCP needs registration — same as single-mcp:
authserver admin resource create \
--slug mcp-a \
--uri https://mcp-a.example.com/mcp \
--backend-kind mint \
--scopes 'tools/query||Query tools' \
--scopes 'tools/write||Write tools'
authserver admin client create \
--name my-agent \
--grant-types authorization_code,refresh_token \
--auth-method none \
--scopes 'tools/query||Query tools' \
--scopes 'tools/write||Write tools'
Internal services get no AuthPlane config. Register them with your mesh instead.
How AuthPlane handles it
Nothing special. The topology is single-mcp from AuthPlane’s perspective. The “folded” part is entirely internal to MCP-A’s process — an implementation detail of the tool handler.
If you later want to expose an internal service as its own Resource (per-service consent, per-service audit), promote it out of the fold and register it individually.
Verify
# Only one resource visible to AuthPlane
authserver admin resource list
# → mcp-a (Svc1/2/3 do NOT appear)
# Only one issuance per (agent, user)
authserver admin issuance list --client my-agent --resource mcp-a
Internal hop is not audited by AuthPlane — instrument it with your mesh’s observability (OTEL traces from MCP-A carry through to Svc1/2/3 with traceparent).
See also
- Choose your topology
- Topologies: MCP gateway → hidden Mint — the alternative when each internal call should be AuthPlane-audited
- Concepts: Architecture — the unified Resource model