Agent identity
TL;DR — AuthPlane extends the standard OAuth token with two claims:
agent_id— the outermost agent’s client_id when it’s registered withis_agent: true, andagent_chain— an ordered list of agent client_ids extracted from theactchain (capped at 8). Your MCP server can gate per-agent quotas, filter by agent in audit queries, and enforce per-agent policy without walking the nested RFC 8693acttree. Not standardized — AuthPlane extension. Set an OAuth client’sis_agent: trueand (optional)agent_descriptionat registration to have it flagged as an agent.
The problem
RFC 8693 gives you an act chain for delegation (see Concepts: Delegation & act-chain). It’s rich — the full delegation tree is reconstructable — but walking it in every MCP server tool handler is annoying:
if token.act && token.act.sub == "agent-A" { ... }
else if token.act.act && token.act.act.sub == "agent-A" { ... }
AuthPlane emits flat claims that pre-compute the useful bits.
agent_id claim
Set to the outermost agent’s client_id when it’s registered with is_agent: true. In the vast majority of flows, that’s the client who initiated the current OAuth call.
Example — direct agent call (user auth-code flow with agent-A as the client):
{
"sub": "user-42",
"aud": "https://mcp.example.com/mcp",
"client_id": "agent-A",
"agent_id": "agent-A"
}
Example — delegated call (agent-A exchanged the user token, sub-agent B is now holding):
{
"sub": "user-42",
"aud": "https://downstream.example.com",
"client_id": "agent-B",
"act": { "sub": "agent-B", "actor_type": "agent", "act": { "sub": "agent-A" } },
"agent_id": "agent-B"
}
agent_id names the current actor — same as act.sub when there’s an act chain, same as client_id otherwise. Precomputed for convenience.
agent_chain claim
Ordered list of every client_id in the act chain, capped at 8 entries. First entry = originator (root of the chain), last entry = current actor. AuthPlane walks the nested act structure once and then reverses so the list reads in causal order.
Example — chained delegation A delegates to B, B delegates to C (C is now calling your MCP server):
{
"sub": "user-42",
"act": {
"sub": "agent-C",
"act": {
"sub": "agent-B",
"act": { "sub": "agent-A" }
}
},
"agent_id": "agent-C",
"agent_chain": ["agent-A", "agent-B", "agent-C"]
}
Your MCP server can filter/log/gate on the chain without recursion:
if token.agent_chain[0] == "agent-A":
# This request originated from agent-A (the root of the delegation chain)
Registering a client as an agent
At registration time, set is_agent: true:
authserver admin client create \
--name my-research-agent \
--agent \
--agent-description "Research assistant that reads GitHub + Notion" \
--grant-types authorization_code,refresh_token,urn:ietf:params:oauth:grant-type:token-exchange \
--scopes 'tools/read||Read tools'
REST:
POST /admin/clients
{
"client_name": "my-research-agent",
"is_agent": true,
"agent_description": "Research assistant that reads GitHub + Notion",
"grant_types": ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"],
"scope": "tools/read"
}
agent_description — max 255 chars — shown on consent screens (“Do you want to grant research assistant that reads GitHub + Notion access to your data?”). Optional but recommended for user-facing agents.
DCR clients can also self-register as agents by including "agent": true in the registration payload.
When agent_id and agent_chain are present
agent_idpresent when the outermost actor’s client hasis_agent: true.agent_chainpresent when the issuing (outermost) client is registered as an agent; in that case it contains every hop in the delegation chain, including any non-agent service clients. There is no filtering byis_agenton intermediate hops.
Absent when the flow is entirely non-agent — machine-to-machine service calls, admin operations, plain user auth-code with a non-agent client. Your MCP server should treat missing claims as “not an agent-mediated call”.
Optional: agents.enable_jwks_listing
When agents.enable_jwks_listing: true (config), AuthPlane publishes a JWKS document per agent at /.well-known/agents/{agent_id}.json — useful for third-party verification of agent-issued signatures if agents sign anything themselves. Off by default; enable only if agents actually publish their own JWKs.
AS metadata advertisement
/.well-known/oauth-authorization-server includes:
{
"authplane_agent_identity_supported": true,
...
}
Clients can key off this to know AuthPlane emits agent_id / agent_chain claims.
What downstream services should do with agent identity
- Per-agent rate limits — throttle at the MCP layer by
agent_id. - Per-agent quotas — count tool invocations per (user, agent_id) pair for billing / usage-based limits.
- Enhanced audit — log
agent_id+agent_chainon every request; enables “show me every action this agent has ever taken across our fleet” queries. - Agent-scoped policy — deny certain tools based on agent identity (e.g., “financial writeback tools not allowed for agents in
agent_chain”).
What NOT to do: don’t authorize solely on agent_chain inner entries. Same rule as RFC 8693 §4.1 ¶6 — inner-hop identities (originator + intermediaries) are informational, not authoritative. Only agent_id (the current actor, the last entry in the chain) is trustworthy for access-control decisions.
Multi-agent example
User asks orchestrator agent → orchestrator delegates to research agent → research agent hits summarizer:
Token at summarizer:
{
"sub": "user-42",
"client_id": "agent-summarizer",
"act": {
"sub": "agent-summarizer",
"act": {
"sub": "agent-research",
"act": { "sub": "agent-orchestrator" }
}
},
"agent_id": "agent-summarizer",
"agent_chain": ["agent-orchestrator", "agent-research", "agent-summarizer"],
"scope": "tools/summarize"
}
Summarizer knows:
- Currently holding:
agent-summarizer(authoritative for authorization). - Delegation chain: orchestrator started, delegated to research, who delegated to summarizer.
- Ultimate principal: user-42.
Everything reconstructable from the token alone. No external correlation needed.
Related
- Concepts: Delegation & act-chain — the RFC 8693
actsemantics thatagent_id/agent_chainderive from - Concepts: Cross-App Access (XAA) — enterprise-asserted agent identity via JWT Bearer
- Reference: RFC compliance → Agent identity claims
- Guides: Admin API → Register a client — how to register with
--agentflag - Full source —
authserver/docs/agents/agent-identity.md