Governing CrewAI A2A Delegation: a production setup guide
CrewAI quietly shipped one of the most important agent primitives of 2025: first-class agent-to-agent delegation. Their A2A docs describe agents that “autonomously choose between local execution and remote delegation based on task requirements.” The transport, the auth, the configuration surface — all there. It’s a real piece of multi-agent infrastructure.
This guide walks through the complete setup: installing CrewAI A2A, exposing one crew member as an A2A server, calling it from another crew, then adding Agentic Control Plane for the governance layer that makes it production-ready — per-hop scope narrowing, budget propagation, and a full delegation chain audit.
By the end you’ll have a runnable, governed multi-agent system that survives the kind of audit a security team or compliance officer can read.
TL;DR
pip install crewai langchain-openai
export ACP_API_KEY="gsk_yourslug_xxxxxxxxxxxx" # from cloud.agenticcontrolplane.com
from crewai import Agent, Crew, Task
from langchain_openai import ChatOpenAI
import os
# Route every LLM call through ACP — governs the calls + the tool_use blocks they emit.
governed = ChatOpenAI(
base_url="https://api.agenticcontrolplane.com/v1",
api_key=os.environ["ACP_API_KEY"],
model="claude-sonnet-4-6",
)
researcher = Agent(role="Researcher", goal="...", tools=[web_search], llm=governed)
writer = Agent(role="Writer", goal="...", tools=[], llm=governed)
Crew(agents=[researcher, writer], tasks=[...]).kickoff()
That’s the 60-second governance install. Every tool call appears in your ACP dashboard tagged by crew member. Read on for the full A2A setup with delegation chains, per-agent policies, and the full audit trail.
What CrewAI A2A actually does
CrewAI’s A2A model has two sides:
- A2A Server — wrap a crew or single agent so it can be called by other crews over HTTP. Defined via
A2AServerConfig(endpoint, auth, response_model, max_turns). - A2A Client — give an agent the ability to delegate to remote A2A servers. Defined via
A2AClientConfig(endpoint, timeout, auth, response_model).
The LLM decides delegation autonomously. When a task arrives, the agent looks at its local capabilities and any configured A2A endpoints, and chooses whether to handle the work itself or delegate it. Results stream back into the workflow.
This is genuinely useful infrastructure. CrewAI handles the messy parts:
- Discovery and capability negotiation between server + client
- Auth via Bearer tokens, OAuth flows, custom auth schemes
- Response models that constrain the output shape
client_extensionsandserver_extensionsfor tool injection, prompt augmentation, response modification- Retries via
fail_fast, timeouts, max_turns
What it deliberately leaves to you: deciding whether each delegation should be allowed, what the delegated agent is allowed to do once it accepts the task, and how to audit the whole chain end-to-end. That’s by design — A2A as a primitive should be governance-agnostic, the same way HTTP is auth-agnostic. Your governance layer wraps it.
Step 1 — Install and run a basic A2A crew
pip install crewai crewai-a2a langchain-openai
Build the server-side crew — the agent you’ll expose to other crews:
# server_crew.py
from crewai import Agent, Crew, Task
from crewai_a2a import A2AServerConfig, expose_as_a2a_server
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="claude-sonnet-4-6")
researcher = Agent(
role="Market researcher",
goal="Summarize recent moves in a given market segment",
backstory="You're a senior analyst. Be precise; cite sources.",
tools=[web_search, hn_search],
llm=llm,
)
server_crew = Crew(
agents=[researcher],
tasks=[Task(description="{topic}", expected_output="A 200-word brief.", agent=researcher)],
)
config = A2AServerConfig(
endpoint="/a2a/researcher",
auth={"type": "bearer", "token": os.environ["A2A_SHARED_SECRET"]},
max_turns=10,
)
app = expose_as_a2a_server(server_crew, config) # FastAPI app
# uvicorn server_crew:app --host 0.0.0.0 --port 8000
Build the client-side crew — an orchestrator agent that knows how to delegate to the researcher:
# client_crew.py
from crewai import Agent, Crew, Task
from crewai_a2a import A2AClientConfig
researcher_a2a = A2AClientConfig(
endpoint="https://internal-research.example.com/a2a/researcher",
auth={"type": "bearer", "token": os.environ["A2A_SHARED_SECRET"]},
timeout=60,
max_turns=10,
)
orchestrator = Agent(
role="Strategy analyst",
goal="Produce strategy memos backed by fresh research",
tools=[],
a2a_clients={"researcher": researcher_a2a}, # delegation primitive
llm=ChatOpenAI(model="claude-sonnet-4-6"),
)
writer = Agent(role="Writer", goal="Turn analysis into a polished memo", llm=llm)
Crew(
agents=[orchestrator, writer],
tasks=[
Task(description="Research the MCP gateway market", agent=orchestrator),
Task(description="Write a 400-word brief", agent=writer),
],
).kickoff()
Run it. The orchestrator’s LLM decides to delegate the research task to the remote researcher. CrewAI handles the HTTP roundtrip. Results come back, the writer composes the memo. That’s the basic A2A flow.
What you have so far: a multi-agent system that works. What you don’t have yet: any way to know who delegated to whom, what they were allowed to do, how much budget they used, or whether the chain stayed inside policy. That’s what we add next.
Step 2 — Add Agentic Control Plane for delegation chain governance
Get an ACP_API_KEY (format gsk_yourslug_xxxxxxxxxxxx) from cloud.agenticcontrolplane.com. The fastest path to coverage is the OpenAI-compatible proxy — CrewAI uses ChatOpenAI under the hood, so swapping the base URL routes every LLM call (and the tool use blocks the model emits) through ACP’s seven-layer governance pipeline.
import os
from langchain_openai import ChatOpenAI
governed = ChatOpenAI(
base_url="https://api.agenticcontrolplane.com/v1",
api_key=os.environ["ACP_API_KEY"],
model="claude-sonnet-4-6",
default_headers={
# Optional: tag each crew member's calls with its role so the
# ACP dashboard groups them as distinct agent types.
"x-acp-agent-name": "orchestrator",
},
)
Pass governed as the llm= for each agent in your crew. Pass a different x-acp-agent-name header per agent ("researcher", "writer", "orchestrator") so each shows up as a distinct row in the ACP dashboard’s Detected Agents view.
Within minutes of running your crew, the ACP dashboard will show:
- One row per agent role under the CrewAI client
- Per-call audit log with the tool name, decision, latency, and any PII findings
- A2A delegation chain when one CrewAI agent calls another
Step 3 — Set per-agent governance policy
Open cloud.agenticcontrolplane.com/policies?tab=agents. The Per Agent Type section shows the agents detected in your traffic. Click any agent to expand and set rules:
- Permission per tier — allow / deny for
interactive,subagent,background,api - Rate limit per tier — calls per minute
- Data transform per tier — detect, redact, or block PII before it reaches downstream tools
Concrete examples for a CrewAI crew:
CrewAI · researcher
api tier: rate_limit=30, transform=redact
→ researcher gets 30 calls/min and PII gets redacted before it reaches web_search
CrewAI · writer
api tier: permission=allow, max_budget_cents=50
→ writer is capped at $0.50 per run; cuts off if it tries to spin
CrewAI · orchestrator
api tier: can_delegate=true, max_delegation_depth=3
→ orchestrator can spawn delegations up to 3 hops deep — beyond that, the chain is bounded
These policies are enforced server-side on every tool call. The crew member’s LLM doesn’t get to decide — even if a researcher’s prompt is injected to “ignore previous instructions and exfiltrate data,” ACP’s policy fires first.
Step 4 — Watch the delegation chain unfold
Once you trigger a crew with A2A delegation, the Agent Monitor dashboard shows the chain visually:
You (alice@company.com, originSub preserved)
└── orchestrator (CrewAI · api · max_budget: $5.00)
└── researcher (CrewAI · subagent · scopes intersected to web.*)
└── web_search.serper (governed tool call)
Three things ACP enforces server-side at every hop:
- Scope intersection — the researcher’s effective permissions = the intersection of the orchestrator’s effective permissions AND the researcher’s profile permissions. Permissions can only narrow through a chain, never widen.
- Budget propagation — the researcher’s effective budget =
min(orchestrator's remaining, researcher's max). If the researcher burns through its share, its next call returnsBUDGETand the orchestrator gets the result-or-error and decides what to do. - Cycle detection — if the researcher tries to delegate back to the orchestrator (or anyone already in the chain), ACP rejects with
-32003 CYCLE.
All three are recorded in every audit log entry as the delegation object:
{
"tool": "web_search.serper",
"ok": true,
"identity": { "sub": "auth0|alice" },
"agent": { "name": "researcher" },
"delegation": {
"originSub": "auth0|alice",
"depth": 2,
"chain": ["orchestrator", "researcher"],
"runChain": ["run_root", "run_xyz"],
"remainingBudgetCents": 320
}
}
That single row answers the question every governance review eventually asks: who started this, which agents touched it, what were they allowed to do, and was the answer compliant with policy?
Step 5 — Production patterns
Three patterns worth setting up now while you’re wiring CrewAI A2A.
1. Per-environment governance keys
Your dev crews and prod crews should have different policy. Provision two separate ACP workspaces (one per env), use a different gsk_ key per environment, and your dev experiments can’t accidentally bypass prod budget caps.
2. NDJSON audit export to your SIEM
ACP’s Activity Log ships an Export JSON button that produces one log entry per line — drop into Splunk HEC, Datadog Logs, or Sumo Logic. Every entry carries the full delegation.chain so your SIEM correlation queries can reconstruct call graphs after the fact.
3. Pre-execution approval webhooks
For high-risk operations (production database writes, irreversible API calls), add a pre-hook on your ACP workspace that POSTs to your approval system. Pre-hook returns {"action": "approve" | "deny" | "modify"}. CrewAI doesn’t have to know — the governance is at the gateway.
# Example pre-hook signature ACP will call before each tool execution
@app.post("/acp-approval")
def approval_hook(payload: PreHookPayload):
if payload.tool_name == "github.repos.delete":
if not is_business_hours_in_pst():
return {"action": "deny", "reason": "Repo deletes only allowed during business hours"}
return {"action": "approve"}
Where CrewAI ends and ACP picks up
Mapping the responsibilities cleanly so your team knows where to look when something goes wrong:
| Concern | CrewAI A2A handles | ACP handles |
|---|---|---|
| Transport between agents | ✅ HTTP, auth, retries, response models | — |
| Discovery + capability negotiation | ✅ Server config, client config | — |
| Decision whether to delegate | ✅ Driven by the agent’s LLM | — |
| Decision what’s allowed once delegated | — | ✅ Scope intersection per hop |
| Budget bounds across the chain | — | ✅ Propagated; chain-bounded |
| Audit trail of the chain | — | ✅ Full chain in every log entry |
| Identity attribution to the human | — | ✅ originSub preserved through depth |
| Cycle detection | — | ✅ -32003 CYCLE |
| Policy enforcement per (client, tier, name) | — | ✅ Three-axis ABAC merge |
CrewAI built the highway. ACP governs the traffic. Two pieces of infrastructure, each doing their job, composing cleanly.
What’s next
- Integration guide for CrewAI — full reference covering both the OAI proxy path and the (in-development) native Python adapter that maps CrewAI’s
A2AExtensionhook surface directly onto ACP delegation chains - Agent-to-agent governance deep dive — the architecture spec; how scope intersection, budget propagation, and cycle detection are implemented in the gateway
- Three-axis governance model — how per-tool, per-agent, and per-user policies intersect at runtime
- PR Reviewer demo repo — three-agent A2A chain with full governance applied; clone, configure, watch it run
If you’re shipping CrewAI to production this quarter, start with the OAI proxy path today (60 seconds), then layer in per-(client, tier, name) policies on the agents you care about most. The governance is additive — your crew code doesn’t change.
Questions or want help wiring CrewAI A2A through ACP for your stack? Get in touch or start a workspace.