MCP Multi-Agent Delegation Pattern: Designing Agent Chain Security with RFC 8693 Token Exchange and Audit Logs
The era of AI agents operating independently is over. In today's actual production environments, complex chains where agents call other agents, and those agents in turn run third-party tools, operate routinely. Model Context Protocol (MCP) is the fastest-growing agent-tool communication protocol, even amidst the coexistence of competing protocols such as Agent-to-Agent (A2A) and ACP; however, tracking "who authorized what" on this chain remains a new design challenge.
This article covers how to design Token Propagation and Responsibility Tracking, the core of the Multi-Agent Delegation pattern, based on modern standards such as RFC 8693 Token Exchange, OIDC-A, and A2A protocols. Going beyond a simple introduction to concepts, it examines practical implementation examples and strategies for addressing security risks. This article is primarily intended for backend and security developers who are familiar with the basic concepts of OAuth 2.0 (such as the Authorization Code Grant flow).
The MCP authorization specification underwent three major updates between 2025 and 2026. In particular, starting with the March 2026 specification (2026-03-15), validation of RFC 8707-compliant resource parameters became mandatory, potentially causing previous implementations to lose compatibility. CVE-2025-55241, discovered in Microsoft Entra ID, is a case confirming that 'Delegation Chain Splicing' attacks—which involve combining tokens from different delegation contexts to deceive the Security Token Service (STS) due to insufficient validation of the agent's actor token—are actually possible, accelerating standardization across the industry.
Key Concepts
Delegation vs. Presumptive: This distinction is the starting point of everything
In multi-agent systems, there is a conceptual distinction that must be clarified first.
| Classification | Delegation | Impersonation |
|---|---|---|
| Actor Distinction | Proxy Agent and Original User Separable and Identifiable | Actor Completely Replaces Original User |
| Audit Trail | Entire chain logged | Only original principal recorded, proxy actors opaque |
| Security Level | Applicability of the Principle of Least Privilege | Difficulty in Controlling Scope of Authority |
| MCP Recommendation | Recommended | Avoid |
Key Principle In a multi-agent environment, impersonation hides "who actually performed the action," making it impossible to trace the cause in the event of an incident. We recommend using a delegation approach to ensure traceability.
RFC 8693: Core Standard for Token Propagation
OAuth 2.0 Token Exchange (RFC 8693) is the standard method for when authentication and authorization information must flow along an agent chain. The Security Token Service (STS) issues a new delegation token at each hop (step of movement between agents), and the core of this specification is a nested delegation structure through the act(actor) claim.
According to RFC 8693 Section 4.1, in the act claim, the outer sub corresponds to the most direct (final) actor, and as it is nested inward, it represents actors at earlier stages. Taking a 3-stage chain where an orchestrator agent delegates to a sub-agent as an example, it is as follows:
{
"sub": "user-alice",
"act": {
"sub": "sub-agent",
"act": {
"sub": "orchestrator-agent"
}
}
}This structure implies within the token itself the fact that "the sub-agent acts on behalf of user-alice via the orchestrator-agent as the most direct final actor." The temporal flow of delegation is in the order of user-alice → orchestrator-agent → sub-agent, and the outermost act.sub is currently the final actor.
The basic form of a Token Exchange request is as follows.
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<user_access_token>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=<orchestrator_agent_token>
&actor_token_type=urn:ietf:params:oauth:token-type:access_token
&scope=read:orders
&resource=https://api.example.com/ordersThe resource parameter is an RFC 8707 resource indicator mandated in the March 2026 MCP specification update. It defends against mis-redemption attacks where a token issued for a specific resource is reused for another resource.
OIDC-A 1.0: Agent-Specific Identity Claims (Proposal for Standardization in Progress)
OIDC-A (OpenID Connect for Agents) is a specification proposed to extend the existing OIDC Core 1.0 to suit agent environments. As of April 2026, it is a draft proposal that has not yet been finalized as an official OIDC Foundation standard; however, since it is being experimentally adopted by numerous implementations, it is a direction with high potential for future standardization. The ID Token issued upon the start of an agent session may include the following claims.
{
"sub": "agent-instance-xyz",
"agent_type": "orchestrator",
"agent_model": "claude-sonnet-4-6",
"agent_instance_id": "sess-abc123",
"delegator_sub": "user-alice",
"delegation_chain": [
"user-alice",
"orchestrator-agent",
"sub-agent-b"
]
}Term Definition delegation_chain Claims are an array that records the delegation paths this agent has taken in chronological order. Audit systems can parse these values to track at which stage abnormal behavior occurred.
A2A Protocol: A Complementary Standard for Agent-to-Agent Cooperation
The Agent-to-Agent (A2A) protocol, led by Google, plays a complementary role to MCP. While MCP standardizes communication between agents and tools, A2A standardizes peer collaboration between agents, task negotiation, and the delegation flow itself. CrewAI added native support for A2A task delegation in 2026, and configurations using MCP and A2A together are becoming increasingly widespread in complex multi-agent pipelines.
Audit Log Structure for Accountability Tracking
Accountability is a chronological log of all actions, decisions, and delegation events performed by an agent. The log entry structure proposed in the draft submitted to the IETF (draft-sharif-agent-audit-trail-00) is as follows:
interface AuditLogEntry {
timestamp: string; // ISO 8601
trace_id: string; // 전체 요청 체인을 관통하는 ID
action_type:
| "tool_call"
| "tool_response"
| "decision"
| "delegation" // 에이전트 → 에이전트
| "escalation" // 에이전트 → 인간
| "error";
actor: {
sub: string; // 현재 행위 에이전트
delegation_chain: string[]; // 위임 계보 (시간순)
};
resource: string; // 접근 대상 리소스
policy_evaluation: {
decision: "allow" | "deny";
matched_policy: string;
};
payload_hash: string; // 불변성 검증용 해시
}In this structure, trace_id is a key field that binds the entire chain together from the start of the user request to the lowest-level agent response.
Practical Application
We examine how to implement core concepts in actual code through three scenarios. Each example covers the different layers of the Delegation pattern (validation, negotiation, and cancellation) to demonstrate the entire lifecycle.
Example 1: Triple Identity Verification via Identity-Aware Gateway
The most widely adopted pattern in actual enterprise environments is to force all MCP calls to pass through an API gateway. This is an example based on the Trusted On-Behalf-Of (OBO) pattern implemented in Gravitee 4.11.
# mcp_gateway_middleware.py
import jwt
import os
from typing import TypedDict
from dataclasses import dataclass
from cryptography.hazmat.primitives.serialization import load_pem_public_key
# 공개키는 환경 변수에서 로드합니다. 하드코딩은 지양해야 합니다.
PUBLIC_KEY = load_pem_public_key(os.environ["JWT_PUBLIC_KEY"].encode())
# 위임 깊이 제한 (일반적으로 3~5 수준을 권장합니다)
# 너무 낮으면 복잡한 위임 시나리오를 처리할 수 없고,
# 너무 높으면 체인 전체의 보안 검토가 어려워집니다.
MAX_DELEGATION_DEPTH = 5
@dataclass
class PolicyResult:
allowed: bool
matched_policy: str
reason: str | None = None
async def evaluate_policy(
principal: str,
resource: str,
action: str,
context: dict,
) -> PolicyResult:
"""
외부 정책 엔진(Cerbos, OPA 등)과 연동해 RBAC 결정을 수행합니다.
Cerbos 연동 예시:
from cerbos.sdk.client import CerbosClient
client = CerbosClient(host=os.environ["CERBOS_HOST"])
resp = client.check_resource(
principal=Principal(id=principal),
resource=Resource(id=resource, kind="mcp_tool"),
actions=[action],
context=context,
)
return PolicyResult(
allowed=resp.is_allowed(action),
matched_policy=resp.outputs[0].policy_version if resp.outputs else "",
)
"""
raise NotImplementedError("정책 엔진 연동 구현이 필요합니다")
class TripleIdentityContext(TypedDict):
user_sub: str
agent_sub: str
tool_id: str
delegation_chain: list[str]
scope: list[str]
async def verify_triple_identity(
user_token: str,
agent_token: str,
tool_id: str,
resource: str,
) -> TripleIdentityContext:
"""
사용자 → 에이전트 → 도구 세 신원을 동시에 검증합니다.
RFC 8693 act 클레임 체인을 통해 위임 맥락을 보존합니다.
"""
# 1. 사용자 토큰 검증 (공개키 필수 — key 없이는 DecodeError 발생)
user_claims = jwt.decode(
user_token,
key=PUBLIC_KEY,
algorithms=["RS256"],
)
# 2. 에이전트 토큰 검증 및 act 클레임에서 위임 체인 추출
agent_claims = jwt.decode(
agent_token,
key=PUBLIC_KEY,
algorithms=["RS256"],
)
delegation_chain = extract_delegation_chain(agent_claims)
# 3. 위임 깊이 제한 검증
if len(delegation_chain) > MAX_DELEGATION_DEPTH:
raise ValueError(
f"Delegation depth {len(delegation_chain)} exceeds limit {MAX_DELEGATION_DEPTH}"
)
# 4. RFC 8707 리소스 인디케이터로 토큰 오용 방지
if agent_claims.get("aud") != resource:
raise ValueError(
f"Token audience mismatch: expected {resource}, "
f"got {agent_claims.get('aud')}"
)
# 5. 정책 엔진(Cerbos 등)으로 RBAC 평가
policy_result = await evaluate_policy(
principal=user_claims["sub"],
resource=resource,
action="mcp_tool_call",
context={"tool_id": tool_id, "agent": agent_claims["sub"]},
)
if not policy_result.allowed:
raise PermissionError(
f"Policy denied: {policy_result.matched_policy}"
)
return TripleIdentityContext(
user_sub=user_claims["sub"],
agent_sub=agent_claims["sub"],
tool_id=tool_id,
delegation_chain=delegation_chain,
scope=agent_claims.get("scope", "").split(),
)
def extract_delegation_chain(claims: dict) -> list[str]:
"""
act 클레임을 순회해 위임 계보를 시간순 리스트로 변환합니다.
RFC 8693에서 바깥쪽 act.sub이 가장 직접적인(최종) 행위자이므로,
순회 후 역순 정렬해 [원래 주체, 첫 위임자, ..., 최종 행위자] 형태로 반환합니다.
"""
act_chain = []
act = claims.get("act")
while act:
act_chain.append(act["sub"]) # 바깥쪽(최근) → 안쪽(이전) 순으로 추가
act = act.get("act")
act_chain.reverse() # 이전 → 최근 순으로 정렬
return [claims["sub"]] + act_chain # [원래 주체, ..., 최종 행위자] 순Example 2: Delegation Request Negotiation Workflow
This is a negotiation pattern where, when Agent A delegates a task to Agent B, the receiving agent can accept, reject, or counter-propose the requested scope of authority.
// delegation-negotiation.ts
// 위임 깊이 제한 (일반적으로 3~5를 권장합니다)
const MAX_DELEGATION_DEPTH = 5;
interface DelegationRequest {
task_id: string;
scope: string[];
deadline: string; // ISO 8601
expected_artifact: string;
sensitivity_level: "public" | "internal" | "confidential" | "restricted";
delegation_chain: string[]; // 현재까지의 위임 경로
}
interface DelegationResponse {
status: "accepted" | "rejected" | "counter_proposed";
accepted_scope?: string[]; // counter_proposed 시 수락 가능한 축소 범위
reason?: string;
}
async function requestDelegation(
fromAgent: string,
toAgent: string,
request: DelegationRequest,
): Promise<DelegationResponse> {
// 무한 위임 방지: 체인 깊이가 한계에 도달하면 거절
if (request.delegation_chain.length >= MAX_DELEGATION_DEPTH) {
return { status: "rejected", reason: "delegation_depth_exceeded" };
}
// 수신 에이전트의 권한 범위 내에서 처리 가능한지 확인
const agentCapabilities = await getAgentCapabilities(toAgent);
const feasibleScope = request.scope.filter(
(s) => agentCapabilities.includes(s),
);
if (feasibleScope.length === 0) {
return { status: "rejected", reason: "no_matching_capabilities" };
}
if (feasibleScope.length < request.scope.length) {
// 일부 범위만 처리 가능한 경우 역제안
return {
status: "counter_proposed",
accepted_scope: feasibleScope,
reason: "partial_capability_match",
};
}
// 위임 이벤트를 감사 로그에 기록
await auditLog({
action_type: "delegation",
actor: { sub: fromAgent, delegation_chain: request.delegation_chain },
resource: request.task_id,
timestamp: new Date().toISOString(),
});
return { status: "accepted", accepted_scope: request.scope };
}Example 3: Cancellation Event Propagation Using OpenID Shared Signals
This is a pattern that invalidates even already issued tokens on sub-chains in real time when a user revokes top-level agent authority. The key to this pattern lies in which repository the find_delegation_descendants function looks up the delegation lineage from.
# revocation_propagation.py
import asyncio
from dataclasses import dataclass
# find_delegation_descendants 구현을 위해 필요한 데이터 구조:
# 각 Token Exchange 시점마다 위임 관계를 별도 저장소에 기록해야 합니다.
#
# Redis 예시:
# KEY: delegation_tree:{parent_agent_id}
# VALUE: SET of "{child_agent_id}:{issued_at}:{token_id}"
#
# PostgreSQL 예시:
# CREATE TABLE delegation_tree (
# parent_agent_id TEXT,
# child_agent_id TEXT,
# issued_at TIMESTAMPTZ,
# token_id TEXT PRIMARY KEY
# );
#
# 토큰 발급 시마다 이 테이블에 레코드를 삽입해야 취소 전파가 가능합니다.
@dataclass
class RevocationEvent:
subject: str # 취소된 주체 (사용자 또는 에이전트)
reason: str
trace_id: str
issued_before: str # 이 시각 이전 발급 토큰 모두 무효화 (ISO 8601)
async def propagate_revocation(event: RevocationEvent) -> None:
"""
OpenID Shared Signals Framework를 활용해
취소 이벤트를 체인 하위로 전파합니다.
"""
# 취소된 주체의 위임 체인에 포함된 모든 에이전트 조회
# (delegation_tree 저장소에서 BFS/DFS로 하위 노드를 순회합니다)
affected_agents = await find_delegation_descendants(
event.subject, event.issued_before
)
# 병렬로 모든 하위 에이전트 토큰 무효화
revocation_tasks = [
invalidate_agent_token(agent_id, event.trace_id)
for agent_id in affected_agents
]
results = await asyncio.gather(*revocation_tasks, return_exceptions=True)
# 실패한 취소 작업은 별도 큐에 저장해 재시도
failed = [
affected_agents[i]
for i, r in enumerate(results)
if isinstance(r, Exception)
]
if failed:
await queue_revocation_retry(failed, event)Pros and Cons Analysis
Advantages
| Item | Content |
|---|---|
| Principle of Least Privilege | Delegate only the minimum privileges required for the task to each agent, and the scope can be explicitly limited using the scope parameter |
| Complete Audit Tracking | act Track "who, what, on whom, and when" executed directly from the token itself via the claims chain |
| Selective Trust Degradation | Enables policy design that gradually reduces trust levels and scope of authority as hops increase |
| Revocation Propagation | When parent agent access is revoked, derived permissions can also be cascaded through the policy engine |
| Standard-based Interoperability | Integration between different vendor agents possible based on open standards such as RFC 8693 |
Disadvantages and Precautions
| Item | Content | Response Plan |
|---|---|---|
| Delegated Chain Joining Attack | A compromised man-in-the-middle agent deceives STS by combining tokens from different contexts (Identified as CVE-2025-55241) | RFC 8707 Mandatory Resource Indicator, Chain Binding Validation |
| Offline token cancellation failed | Already deployed offline tokens cannot be invalidated in real-time | Cancellation event propagated via OpenID Shared Signals, token TTL shortened |
| Confused Agent Problem | Executing attacker commands via prompt injection even if the agent possesses legitimate credentials | Delimiting trust boundaries for tool inputs, adding Human-in-the-Loop gates |
| Tooltip Contamination | Injection of hidden commands into MCP server tool metadata possible | Treat tooltips as untrusted external input, registry signature verification |
| Difficulties in Auditing | Agent Decision-Making Process is a Black Box Without Explicit Logging | Mandatory Structured Logs Under IETF Draft-Sharif-Agent-Audit-Trail |
Terminology Supplement Confused Deputy: A vulnerability where an agent performs unintended privileged actions through malicious prompts or tool input, despite possessing legitimate authority. The broader the agent's privileges, the greater the scope of damage.
The Most Common Mistakes in Practice
- Maintain the method of passing API keys directly to the chain: If third-party API keys are passed directly to agents, all agents are at risk if the key is exposed. It is recommended to use a token exchange service such as Auth0 Token Vault to prevent actual credentials from reaching the agent code.
- Allow recursive delegation without delegation depth limits: If circular delegation or infinite depth delegation is possible, such as Agent A → B → C → A, it can be exploited for system resource exhaustion and security bypasses. Generally, it is recommended to set
MAX_DELEGATION_DEPTHto a level of 3 to 5. - Assuming cancellation events are handled synchronously only: Synchronous cancellation alone is not sufficient when there are offline agents or cached tokens. It is safer to design an asynchronous propagation mechanism that logs cancellation failures to a separate queue and retries them.
In Conclusion
The core problem that the Multi-Agent Delegation pattern solves is one: making it traceable at every layer of the agent chain "who did what on behalf of whom." Security incidents occur not due to a lack of technology, but when this traceability is omitted during the design phase.
3 Steps to Start Right Now:
- Check the direct credential delivery method in your existing agent code. Use the
grep -r "api_key\|bearer_token" ./agentscommand to identify points in your current codebase where credentials are directly exposed to the agent, and prioritize replacing them with RFC 8693 Token Exchanges. - Try adding the
trace_idanddelegation_chainfields to the audit log. Even if you only add the two core fields of the previously presentedAuditLogEntrystructure to your current logging system, your ability to trace the cause of an incident will be significantly improved. - Check the resource indicator requirements of the MCP 2026-03-15 specification. It is recommended that you check the current specification in the official MCP authorization document and verify whether your MCP server is correctly validating the
resourceparameter as an OAuth 2.1 resource server.
Next Post: A Practical Security Hardening Guide to Defending MCP Agents from Prompt Injection and Tooltip Contamination Attacks
Reference Materials
Standard Document
- OAuth 2.0 Token Exchange RFC 8693 | IETF
- Understanding Authorization in MCP | Official MCP Document
- The New MCP Authorization Specification 2026-03-15 | dasroot.net
- What's New In The 2025-11-25 MCP Authorization Spec | Den Delimarsky
- draft-sharif-agent-audit-trail-00: Agent Audit Trail Standard | IETF Datatracker
- Delegation Chain Splicing Security Consideration | OAUTH-WG
- A Survey of Agent Interoperability Protocols: MCP, ACP, A2A, ANP | arXiv
Implementation Examples and Tools
- Trusted On-Behalf-Of: Agent Delegation in Gravitee 4.11 | Gravitee
- Securing MCP with OIDC & OIDC-A: Identity-Aware API Gateways | Subramanya N
- Token Delegation and MCP server orchestration for multi-user AI systems | DEV Community
- End-User Identity Propagation in Agents | Salesforce Agentforce
- OAuth for MCP - Emerging Enterprise Patterns for Agent Authorization | GitGuardian
- AI Agents, the Model Context Protocol, and the Future of Authorization Guardrails | Cerbos
- Delegation in a Multi-Actor World: It's Not Just OAuth Anymore | Spherical Cow Consulting