Agent Interoperability — the A2A Protocol
MCP connects an agent to tools. A2A (Agent-to-Agent) connects an agent to other agents — built by different teams, on different frameworks, running behind different companies' walls. This chapter is the protocol that lets agents discover and delegate to each other as peers, and why that's a different problem from tool use.
Learning objectives
- Explain the problem A2A solves and how it differs from MCP.
- Describe the core concepts: Agent Card, tasks, messages, artifacts.
- Walk a discovery → delegate → stream-updates → result flow.
- Contrast A2A with in-process multi-agent frameworks (CrewAI/AutoGen).
- Name the trust, auth, and reliability concerns of cross-org agent calls.
30-second recap
- The MCP ecosystem: MCP is a standard protocol for connecting a model to tools and data: servers expose tools/resources, clients (like Claude) discover and call them.
- Agent & tool foundations: The model asks to call a tool, your code runs it and returns the result, and the loop repeats until the model answers — with step caps so it can't run forever.
The problem: agents behind walls essential
Picture the wall you keep running into. Your support agent needs a shipping estimate, so it needs the logistics team's agent. But that agent lives in another team's repo, on another framework, deployed behind another company's login. You can't pip install it, you can't read its code, and you certainly can't redeploy it when it changes. Every integration you've done so far assumed you owned both ends; this is the first time you don't. That is the wall A2A is built to knock a door through.
Here's why your usual move fails. Your instinct is to wrap the other agent as a "tool" and call it like a function — the pattern you learned for MCP. But a tool is a thing you invoke: you pass arguments, it computes, it returns in seconds, and you own it. The other agent is a thing you delegate to: it reasons on its own, keeps its own state, may take minutes to finish, may pause to ask you a clarifying question, and lives outside your control. Modeling that as a synchronous function call is the single most common mistake here, and it breaks the moment the work runs long or the peer talks back. The mismatch is the whole reason A2A is a separate protocol rather than "just another tool."
The plain idea: A2A (Agent-to-Agent) is a shared language for autonomous agents to find each other and hand off work across boundaries — different teams, different frameworks, different companies. Think of it less like an API and more like the etiquette two professionals from different firms use when they collaborate: they introduce themselves and their skills, one hands the other a project, the worker reports progress, and eventually delivers. Nobody has to have merged codebases first. Your agent is great at its job — A2A is how it borrows an ability it will never own, from a peer it will never operate. That's A2A (introduced by Google in 2025, now under open governance); the diagram below fixes the one distinction everything else builds on — down to a tool versus sideways to a peer.
This picture contrasts two protocols side by side by the direction their arrows point. The whole lesson hangs on that one difference, so read it as "down vs sideways".
- On the left (MCP): the top box is your agent, and the arrow points down to a box labelled
tools / data. Downward means "reaches into things it uses" — a tool is a function your agent calls and owns. - On the right (A2A): your agent sits next to an other agent, and there are two sideways arrows between them — one going each way. Sideways means "talks to a peer", and the two arrows show it's a back-and-forth conversation, not a one-shot call.
- The solid arrow is a request your agent sends; the faint/dashed arrow is the reply coming back. Same convention is reused in the second diagram, so it's worth noticing here.
- The caption line "peer collaboration across a boundary" is the takeaway: the other agent is autonomous — it has its own reasoning and state — so you delegate to it rather than call it.
In short: MCP = arrow pointing down to a tool you own; A2A = arrows pointing sideways to a peer agent you don't control. If you only remember the arrow directions, you've got the lesson.
A2A vs MCP — complementary, not competing essential
The fastest way to get A2A wrong is to think it competes with MCP. They sound similar — both are protocols, both let your agent reach outside itself — so people assume they'll pick one. Get this framing wrong and you'll either bolt A2A onto problems that only needed a tool, or try to squeeze a genuine peer agent into a tool schema and fight the protocol forever. So spend a moment here: it's the mental model the rest of the chapter leans on.
The distinction in one image: MCP points down, A2A points sideways. MCP connects your agent down to the tools, data, and resources it uses — things you invoke and own, like a database query or a file read. A2A connects your agent sideways to another agent — an autonomous peer, often across an org boundary, that you delegate to rather than call. Down is "reach into something I control"; sideways is "hand work to a colleague I don't." Once you feel that geometry, the table below is just the same idea broken into five rows.
The analogy that makes it stick: MCP is an API your program calls — you send a request, you get a response, you're in charge of the whole transaction. A2A is a colleague you hand a project to — you brief them, they go off and work autonomously, they check in, and they come back with a deliverable on their own schedule. You don't micromanage a colleague's internal steps the way you'd control an API call, and you can't brief an API the way you'd brief a colleague. The common mistake is treating the colleague like an API (expecting an instant, controlled return) — that's exactly what breaks. Read each row as the same "invoke vs delegate" split seen from a different angle: what's on the other side, how you interact, and where the boundary sits.
| MCP | A2A | |
|---|---|---|
| Connects | Agent → tools / data / resources | Agent → other agents (peers) |
| Other side is… | A capability you invoke | An autonomous agent with its own reasoning |
| Interaction | Call a tool, get a result | Delegate a task; it may run long, stream updates, ask back |
| Boundary | Often within your control | Often across teams / orgs / vendors |
| Analogy | An API your program calls | A colleague you hand a project to |
Core concepts essential
For two strangers to work together, they need a shared vocabulary — and so do agents. If your agent and a vendor's agent were built by teams who never met, how does yours even know the other exists, what it can do, or how to ask? It can't guess, and it can't read the other's mind. A2A solves this the way humans do when they collaborate across companies: with a small set of agreed-upon nouns that both sides understand. Learn these four and you can read almost any A2A interaction, because everything that happens is expressed in terms of them.
Why these four and not twenty: notice they map cleanly onto how a real handoff works. The Agent Card is the business card and résumé — who I am and what I can do. The Task is the project you hand over — a unit of work with a beginning, a middle, and an end. The Message is a turn in the conversation about that project — a question, an update, a clarification. The Artifact is the finished deliverable handed back. The common trap is collapsing Task and Message into one thing ("I sent it a request and got a reply") — but a single Task usually spans many Messages over time, because the work runs long. Keep the Task as the durable container and the Messages as the turns inside it, and the lifecycle in the next section will feel obvious. Here's the shared vocabulary, one row each.
A2A gives agents a shared vocabulary for discovery and delegation. Four pieces carry most of it.
| Concept | What it is |
|---|---|
| Agent Card | A published descriptor (JSON, at a well-known URL) advertising an agent's identity, skills, endpoint, and auth requirements — how others discover what it can do |
| Task | A unit of work delegated to a remote agent; has a lifecycle (submitted → working → input-required → completed/failed) — it can run long |
| Message | A turn of communication between client-agent and remote-agent within a task (text and structured parts) |
| Artifact | An output the remote agent produces for the task — the deliverable(s) handed back |
Lab I2.1 · A delegation flow intermediate
You now know the nouns; this is the verb. Concepts on their own are inert — the payoff is seeing them move. This section is the one you'll replay in your head every time you design an A2A integration, because it's the actual choreography of one agent getting real work out of another it doesn't own. If you can narrate this flow from memory, you understand A2A.
The shape to hold onto has four beats: discover → delegate → stream → deliver. First the client discovers the peer by fetching its Agent Card — the "what can you do and how do I reach you?" step. Then it delegates by submitting a task. Then — and this is the beat beginners skip — the peer streams progress back while it works, because the job takes real time. Finally it delivers the artifact and the task reaches a terminal state. The mistake to avoid is imagining beats two and four glued together with nothing in between (send request, get answer). That's a tool call, not a delegation; the streaming middle is exactly what makes this A2A and not MCP.
A human analogy keeps it concrete: it's a supervisor handing a job to a specialist in another department. You look up who does this kind of work (discover), you brief them and hand it over (delegate), they ping you with "30% done… 80% done…" and occasionally ask a clarifying question (stream, including the input-required pause where the task can turn around and ask you something), and then they drop the finished report on your desk (deliver). It's the supervisor topology from L1/M1 — but stretched across a network boundary, so the "handoff" is a wire protocol instead of a function call. Read the diagram top-to-bottom as time passing, and watch which direction each arrow points.
Walk the lifecycle: a client agent discovers a remote agent, delegates a task, receives streamed progress, and gets the result. This is the supervisor topology (L1/M1) stretched across a network boundary.
input-required state and asks back.
This is a time sequence, not a static map. Two agents face each other — client agent on the left, remote agent on the right — and the numbered arrows between them happen top to bottom, in order. Read it like a chat transcript flowing downward.
- Arrow 1 (fetch Agent Card) goes left→right: the client asks the remote "what can you do and how do I reach you?". The Agent Card is the published menu of the remote's skills — this is the discover step.
- Arrow 2 (send task) also goes left→right: the client hands over a unit of work — this is the delegate step, like a supervisor handing a job to a worker.
- Arrows 3 (stream status + messages) point back right→left and are dashed/faint: the remote keeps sending progress updates while it works. Multiple arrows = many updates over time, because a task can run for minutes, not milliseconds.
- Arrow 4 (return artifact), shown in green, is the final delivery: the finished output(s) come back and the task is done. Green marks the successful end state.
- Not drawn but noted in the caption: if the remote needs more info mid-task it enters an
input-requiredstate and asks back — the flow can pause and resume, which is why the updates go both ways.
In short: The four steps are discover → delegate → stream → deliver. Down-the-page = later in time; solid arrows = the client driving, faint/green arrows = the remote responding.
| Step | What happens | Familiar from… |
|---|---|---|
| Discover | Client reads the remote's Agent Card (skills, endpoint, auth) | Tool/server discovery (I1, C2) |
| Delegate | Client submits a task with an initial message | Supervisor → worker handoff (L1, M1) |
| Collaborate | Task streams status; may hit input-required and ask back | Streaming (C2); human-in-the-loop shape (L5) |
| Deliver | Remote returns artifacts; task reaches a terminal state | Artifacts / tool results (V3, C2) |
A2A vs in-process multi-agent (M1/M2) intermediate
Here's a real fork you'll hit on the job. You already build multi-agent systems with CrewAI and AutoGen — several agents cooperating inside one process. So a natural question, and a genuinely load-bearing one for your architecture, is: when a task needs another agent, do you add it to your crew, or reach for A2A? Choose wrong and you either drag a whole protocol into a problem that a function call would have solved, or you try to pull an agent you don't operate into your own runtime and discover you can't. This section is the decision rule.
The plain test is ownership, not capability. Both approaches coordinate multiple agents; the difference is who controls them. If every agent is yours — same codebase, same framework, same deployment you can push a fix to — keep them in-process: it's simpler, faster, and you can refactor freely. The moment an agent is built by another team, runs on another stack, or is operated by someone you can only reach over the network, you've crossed a boundary that in-process coordination can't span, and that's A2A's job. A useful gut-check: could I redeploy this agent myself if it broke? If yes, it's a crew member; if no, it's an A2A peer.
The reassuring part — and the thing most people miss: A2A doesn't replace the coordination patterns you already know, it stretches them across the boundary. The supervisor and network topologies from L1 work exactly the same; a remote A2A agent can be a "member" of your CrewAI crew, and your orchestration logic doesn't change. A2A is just the wire protocol that makes a member reachable when it lives in someone else's building. So this isn't a rival framework to learn — it's the adapter that lets your existing multi-agent design phone across an org line. Read the table as a boundary detector: left column = everything under your roof, right column = the first time something isn't.
You already coordinate multiple agents with CrewAI and AutoGen. When do you need A2A instead?
| Use in-process (CrewAI/AutoGen) when… | Use A2A when… |
|---|---|
| All agents are yours, in one codebase | Agents are built by different teams/companies |
| They share a framework & runtime | They run on different frameworks/stacks |
| You control deployment of all of them | You call an agent you don't operate |
| Tight coupling is fine | You need a stable contract across a boundary |
Trust, auth & reliability across the boundary intermediate
This is the section that keeps A2A systems out of the incident channel. The demo where two agents happily delegate to each other is the easy 80%; the hard 20% — the part that decides whether you can ship this to production — is that you're now taking instructions and data across a line you don't control, from an actor that thinks for itself. Skip this and you've built a beautiful pipe that leaks credentials, acts on poisoned output, and hangs forever when the peer goes down. Every concern here is one you've met before; the point is that crossing the boundary sharpens each one.
The reframe that makes the risks obvious: stop thinking of the remote agent as a helpful colleague and start thinking of it as an untrusted, autonomous remote dependency. "Remote" means it can be slow, down, or fail mid-task — so it needs the same timeouts, retries, and circuit breakers as any network call (A6, O3). "Untrusted" means its output is hostile input until proven otherwise — an artifact can carry prompt injection exactly like a tool result or a retrieved document (T1), so you validate before you act. And "autonomous" is the twist a plain API doesn't have: it reasons on its own, so it might be adversarial, compromised, or simply mistaken in ways a static endpoint never is. The common mistake is letting the friendly "peer" framing lull you into trusting it more than you'd trust a random URL — trust it less, because it's smarter.
Concretely, the boundary turns four familiar knobs up to eleven. Authentication: you're handing credentials to a service you don't run, so scope them to the single task and nothing more (least privilege). Data exposure: delegating sends your data across the line, so decide deliberately what's safe to share and mind residency and privacy (O4). Cost and loops: agents calling agents can fan out expensively or even loop, so bound the depth and spend (M2). And output trust: keep your own safety gate (L5) in front of anything you do with what comes back. Read the table below as a checklist you run before the first delegation ships, not a postmortem you write after — each row is a concern you already respect, now amplified because the other side is autonomous and outside your walls.
Calling an agent you don't control raises every concern that calling your own tools does — amplified, because the other side is autonomous and outside your walls.
| Concern | Why it's sharper across A2A |
|---|---|
| Authentication | The Agent Card declares auth requirements; you present credentials to a service you don't run — scope them tightly (I1, T1) |
| Trust of output | A remote agent's artifacts are untrusted input — they can carry prompt injection just like tool results (T1). Validate before acting |
| Reliability | The peer can be slow, down, or fail mid-task — treat it like any remote dependency: timeouts, retries, circuit breaker (A6, O3) |
| Data exposure | Delegating a task sends data across a boundary — decide what's safe to share, mind residency & privacy (O4) |
| Cost & loops | Agents calling agents can fan out expensively or loop — bound it, as with any multi-agent system (M2) |
Common pitfalls advanced
Every idea in this chapter has a matching way to get it wrong, and these are the six you'll actually meet. Think of this table as the negative image of everything above: each pitfall is what happens when one of the lessons doesn't land. Reading them together is worth more than the sum of the parts, because the failures share a spine — they all come from forgetting that a peer agent is autonomous, remote, and untrusted. Spot that root and every fix follows.
Watch how they cluster. The first two are identity mistakes — reaching for A2A when in-process would do (over-engineering a boundary that isn't there), and modeling a peer as a synchronous function (forgetting tasks run long and stream). The next three are trust mistakes — believing an artifact without validating it, calling a remote with no timeout or retry, and handing over credentials broader than the task needs. The last is a systems mistake — letting agent-to-agent calls fan out or loop without a cap. The honest truth is that none of these fixes are exotic; they're the resiliency (A6, O3), safety-gating (L5, T1), and cost-bounding (M2) habits you already have, simply applied across a wire. Read each row as "the lesson, and what it costs to skip it."
| Pitfall | Fix |
|---|---|
| Reaching for A2A when in-process would do | Only cross-boundary needs A2A; keep your own agents in-process |
| Modeling a peer agent as a sync function | Treat tasks as long-running, streamed, stateful |
| Trusting a remote agent's artifacts | Untrusted input — validate; guard against injection (T1) |
| No timeout/retry on remote calls | It's a remote dependency — apply A6/O3 resiliency |
| Over-broad credentials to a peer | Least privilege; scope to the delegated task |
| Unbounded agent-to-agent fan-out | Cap delegation depth & cost (M2) |
Exercises advanced
Exercise I2.1 — MCP or A2A?
Context: The core interop instinct is telling a capability you invoke (MCP) from an autonomous peer you delegate to (A2A), and four concrete cases sharpen it.
Your task: For each of four cases, decide MCP or A2A and justify: (a) query your Postgres, (b) a partner's fraud-scoring agent assesses a transaction, (c) read files, (d) hand a research sub-task to a vendor's autonomous research agent.
Requirements:
- Classify each of the four cases MCP or A2A
- Owned tools/data (Postgres, files) → MCP
- Autonomous peers across an org boundary (fraud-scoring, vendor research) → A2A
- Justify each with the invoke-vs-delegate rule
- State the rule: capability you invoke → MCP; autonomous peer you delegate to → A2A
💡 Hint: Whether you own and invoke the target or delegate to an autonomous peer decides it every time.
Show answers
(a) MCP — a tool/data source you own. (b) A2A — a peer agent across an org boundary. (c) MCP — a capability. (d) A2A — delegating a task to an autonomous external agent. Rule: capability you invoke → MCP; autonomous peer you delegate to → A2A.
Exercise I2.2 — Design an Agent Card
Context: To be delegated to safely, an agent has to publish its own Agent Card — and designing one for something you built surfaces exactly what a peer needs to know.
Your task: For an agent you've built in this course, sketch its Agent Card: identity, the skills it advertises, its endpoint, and what auth it would require.
Requirements:
- Give the agent an identity
- List the skills it advertises
- Specify its endpoint
- State the auth it would require
- Note what another team needs to know to safely delegate to it
💡 Hint: Write it from the caller's point of view — a peer should be able to discover, authenticate, and delegate from the card alone.
Exercise I2.3 — Threat-model a delegation
Context: Acting on an artifact returned by an external A2A agent opens real attack surface, so you threat-model it the way you would any untrusted input.
Your task: You delegate a task to an external A2A agent and act on its returned artifact: list three ways this could go wrong (injection, unreliability, data leak) and the specific mitigation for each.
Requirements:
- Name three failure modes: injection, unreliability, data leak
- Give a specific mitigation for each
- Draw on the injection (T1), guarded-delegation (A6), and vetting (L5) material
- Treat the returned artifact as untrusted input
💡 Hint: Each failure maps to a mitigation you've already seen — validate the artifact against injection, bound the call for reliability, and scope data to prevent leaks.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: An Agent Card is a JSON descriptor advertising an agent's skills, endpoint, and auth — A2A's discovery key that lets one agent learn a peer's capabilities without human wiring.
Your task: Given a card and a needed skill, write a function that says whether the agent can do it and where to reach it.
Requirements:
- Represent the card with name, endpoint, a skills list, and auth
- Check whether the needed skill is advertised
- Return the endpoint + auth scheme on a hit
- Return a clear “not advertised” miss otherwise
- Frame the card as the discovery key for delegation
💡 Hint: Membership-test the skill against the card's skills list, then hand back the endpoint and auth scheme to reach it.
Show solution
An Agent Card is just data you inspect before delegating. Runnable stdlib:
card = {
"name": "logistics-agent",
"endpoint": "https://vendor.example/a2a",
"skills": ["route-planning", "eta-estimate"],
"auth": "bearer",
}
def can_do(card, skill):
if skill in card["skills"]:
return f"yes -> POST {card['endpoint']} (auth: {card['auth']})"
return f"no -- {card['name']} does not advertise '{skill}'"
print(can_do(card, "eta-estimate")) # yes -> POST ...
print(can_do(card, "invoice-audit")) # no -- not advertised
The card is A2A's discovery key: you learn what a peer can do and how to reach it without a human wiring you together first.
Context: An A2A Task has a lifecycle — submitted → working → (input-required) → completed/failed — so a client must track state, not assume a one-shot reply.
Your task: Model the task lifecycle as a finite-state machine that rejects illegal transitions.
Requirements:
- Encode the allowed transitions (submitted, working, input-required, terminal completed/failed)
- Raise on an illegal transition
- Walk a valid path through the states
- Show a terminal-state transition being blocked
- Reinforce that a client tracks lifecycle, not a single reply
💡 Hint: A map from state to its allowed next states is the whole machine — anything not in the set raises, and completed/failed lead nowhere.
Show solution
Encode the allowed transitions and reject the rest. Runnable:
ALLOWED = {
"submitted": {"working", "failed"},
"working": {"input-required", "completed", "failed"},
"input-required": {"working", "failed"},
"completed": set(), # terminal
"failed": set(), # terminal
}
def transition(state, nxt):
if nxt in ALLOWED[state]:
return nxt
raise ValueError(f"illegal {state} -> {nxt}")
s = "submitted"
for nxt in ["working", "input-required", "working", "completed"]:
s = transition(s, nxt)
print("->", s)
try:
transition("completed", "working") # terminal, must raise
except ValueError as e:
print("blocked:", e)
Because a task can run long and pause for input, a client must track the lifecycle rather than assume a one-shot reply.
Context: Discovery in A2A means matching a peer that advertises the required skill AND an auth scheme the client can actually satisfy — skill-then-authenticable.
Your task: Given a registry of Agent Cards, write a matcher that finds a peer advertising a required skill and a supported auth scheme, returning the best endpoint or a clear miss.
Requirements:
- Filter the registry by the required skill
- Also require an auth scheme the client supports
- Return the first match's endpoint (or None)
- Note real discovery adds ranking (cost/latency/trust)
- The shape is match-skill-then-match-authenticable
💡 Hint: A card is only usable if you can both do the skill and authenticate to it — filter on both before returning.
Show solution
Discovery is a filter over cards, not a network call. Runnable:
REGISTRY = [
{"name": "a", "skills": ["translate"], "auth": "none"},
{"name": "b", "skills": ["route-planning"], "auth": "oauth2"},
{"name": "c", "skills": ["route-planning"], "auth": "bearer"},
]
def discover(registry, skill, supported_auth):
hits = [c for c in registry
if skill in c["skills"] and c["auth"] in supported_auth]
if not hits:
return None
return hits[0]["name"] # first match; could rank on latency/cost
print(discover(REGISTRY, "route-planning", {"bearer", "none"})) # c
print(discover(REGISTRY, "route-planning", {"none"})) # None
print(discover(REGISTRY, "translate", {"none"})) # a
Real discovery adds ranking (cost, latency, trust). The shape stays: match skill, then match what you can actually authenticate against.
Context: A2A isn't “call a tool, get a value” — a delegation streams status updates and ends with an Artifact, which you can model offline as a generator of events.
Your task: Simulate one A2A delegation offline: submit a task, receive streamed status updates, then collect the final Artifact, modelling the remote agent as an event generator.
Requirements:
- Model the remote agent as a generator yielding status events then an artifact
- Yield working-status updates (with progress) before completion
- Emit an artifact event, then a completed status
- Consumer folds the stream, printing progress and collecting artifacts
- Return the final state + artifacts
💡 Hint: A Python generator yielding status events and then an artifact captures the stream; the consumer just folds those events into progress + a final result.
Show solution
Model the remote as an event stream; the client folds it into a result. Runnable:
def remote_agent(task):
# a long-running peer that streams progress, then an artifact
yield {"type": "status", "state": "working", "pct": 30}
yield {"type": "status", "state": "working", "pct": 80}
yield {"type": "artifact", "name": "route.json",
"data": {"stops": task["stops"], "eta_min": 42}}
yield {"type": "status", "state": "completed"}
def delegate(task):
artifacts, final = [], None
for ev in remote_agent(task):
if ev["type"] == "status":
print("update:", ev.get("state"), ev.get("pct", ""))
final = ev["state"]
elif ev["type"] == "artifact":
artifacts.append(ev)
return final, artifacts
state, arts = delegate({"stops": ["A", "B", "C"]})
print("final:", state, "artifacts:", [a["name"] for a in arts])
Streaming updates + a final artifact are exactly why A2A is not "call a tool, get a value": the peer is autonomous and reports progress.
Context: In a mixed design, some connections go down to a tool you own (MCP) and some go sideways to an autonomous peer you delegate to (A2A) — they're layers, not rivals.
Your task: Write a classifier that, given a target's nature, picks the right protocol (MCP or A2A) and explains why.
Requirements:
- Return MCP for a function/resource (a capability you invoke and own)
- Return A2A for an autonomous agent (a peer you delegate a task to)
- Default to MCP
- Iterate a few sample targets to show the split
- Reinforce MCP=down/owned, A2A=sideways/autonomous
💡 Hint: Ask whether the target is a capability you own or an autonomous peer — that single distinction picks the protocol.
Show solution
The rule: own it and call it = MCP (down); autonomous peer you delegate to = A2A (sideways). Runnable:
def route(target):
# target: dict describing what we're connecting to
if target["kind"] == "function" or target["kind"] == "resource":
return "MCP", "a capability you invoke and own"
if target["kind"] == "agent" and target["autonomous"]:
return "A2A", "an autonomous peer you delegate a task to"
return "MCP", "default: treat as an owned capability"
for t in [
{"kind": "function", "name": "get_weather"},
{"kind": "agent", "autonomous": True, "name": "vendor-logistics"},
{"kind": "resource", "name": "s3-doc"},
]:
proto, why = route(t)
print(f"{t['name']:20} -> {proto:4} ({why})")
A mature agent uses MCP for its own tools and speaks A2A to hand whole sub-tasks to specialists it cannot absorb. They are layers, not rivals.
Context: Delegating to a partner-company agent adds trust and reliability concerns a tool call never has: you must check auth, confirm the skill was advertised, and bound the call with a timeout/retry policy before acting on its artifact.
Your task: Model a guarded cross-org delegation offline: enforce a valid auth token, an advertised skill, and a timeout/retry policy before trusting the returned artifact.
Requirements:
- Enforce trust first: skill must be advertised; bearer auth requires a token
- Then enforce reliability: a bounded retry loop over a flaky peer call
- Return ok/artifact after retry, or block on missing token / unadvertised skill
- Simulate transient failures in the peer call
- Note the real A2A SDK wires this over HTTP + an identity provider
💡 Hint: Check trust preconditions before you ever call, then wrap the call in a bounded retry — trust gates entry, retries handle transient failures.
Show solution
Crossing an org boundary means you cannot assume trust or liveness. Guard the call. Runnable:
import time
def guarded_delegate(card, skill, token, deadline_s=2, max_retries=2):
# 1) trust: skill must be advertised and auth must match
if skill not in card["skills"]:
return {"ok": False, "reason": "skill not advertised"}
if card["auth"] == "bearer" and not token:
return {"ok": False, "reason": "missing bearer token"}
# 2) reliability: bounded retries against a flaky peer
attempt = 0
while attempt <= max_retries:
attempt += 1
ok, artifact = _call_peer(card, skill, fail_first=1, attempt=attempt)
if ok:
return {"ok": True, "attempt": attempt, "artifact": artifact}
return {"ok": False, "reason": "peer unavailable after retries"}
def _call_peer(card, skill, fail_first, attempt):
time.sleep(0) # stand-in for network I/O
if attempt <= fail_first:
return False, None # simulate a transient failure
return True, {"skill": skill, "value": "OK"}
card = {"name": "partner", "skills": ["audit"], "auth": "bearer"}
print(guarded_delegate(card, "audit", token="t0k")) # ok after retry
print(guarded_delegate(card, "audit", token="")) # blocked: no token
print(guarded_delegate(card, "ship", token="t0k")) # blocked: not advertised
Across teams/vendors you own neither the code nor the uptime: verify the advertised skill, authenticate every call, and bound long-running/flaky work with timeouts and retries. The real A2A SDK wires this over HTTP + your identity provider (needs the SDK).
✓ Checkpoint — you can move on when you can…
- Explain why A2A exists and how it differs from MCP.
- Define Agent Card, task, message, and artifact.
- Walk the discover → delegate → stream → deliver flow.
- Decide A2A vs in-process multi-agent for a scenario.
- Name the trust/auth/reliability risks of cross-org agent calls and their mitigations.
Knowledge check check yourself
A2A and MCP are described as complementary, not competing. What does each connect, and why can't you model an A2A peer as a tool call?
Show answer
What is an Agent Card and why is it central to A2A's discover→delegate→stream→deliver flow?