AI EngineeringZero to ProductionHome·About·Contact
Interoperability & Agent Ops · Chapter I2

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.

⏱️ ~45 min🤝 Concepts🎯 Intermediate→Advanced

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.
Where this sitsYou've built multi-agent systems inside one process with CrewAI and AutoGen (M1/M2). A2A is the cross-boundary version: agents that don't share a codebase, framework, or company still collaborating. It complements MCP — MCP for tools/data (I1, C4), A2A for agent-to-agent. Protocols evolve fast; learn the roles and the flow, verify specifics against the current spec.

The problem: agents behind walls essential

Your agent is great at its job. But the task needs a capability owned by another agent — a different team's logistics agent, a vendor's specialist agent, a partner company's service. You can't import their code (it's not yours), and they're not a "tool" you call — they're an autonomous agent with their own reasoning, state, and long-running work. You need a standard way for agents to talk to agents across organizational and technical boundaries. That's A2A (introduced by Google in 2025, now under open governance).

MCP (I1, C4) your agent tools / data A2A (this) your agent otheragent peer collaboration across a boundary Two different arrows. MCP points down — your agent to the tools and data it uses. A2A points sideways — your agent to a peer agent it collaborates with. A tool is a function you call and own; a peer agent is an autonomous actor you delegate to and don't control. That difference is why A2A exists separately.
🗺️ How to read this diagram

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

MCPA2A
ConnectsAgent → tools / data / resourcesAgent → other agents (peers)
Other side is…A capability you invokeAn autonomous agent with its own reasoning
InteractionCall a tool, get a resultDelegate a task; it may run long, stream updates, ask back
BoundaryOften within your controlOften across teams / orgs / vendors
AnalogyAn API your program callsA colleague you hand a project to
Use both togetherThey're layers, not rivals. A common design: your agent uses MCP for its own tools and speaks A2A to delegate whole sub-tasks to specialist agents it can't absorb. The remote agent, in turn, uses its own MCP tools. MCP is how an agent acts; A2A is how agents cooperate.

Core concepts essential

A2A gives agents a shared vocabulary for discovery and delegation. Four pieces carry most of it.

ConceptWhat it is
Agent CardA 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
TaskA unit of work delegated to a remote agent; has a lifecycle (submitted → working → input-required → completed/failed) — it can run long
MessageA turn of communication between client-agent and remote-agent within a task (text and structured parts)
ArtifactAn output the remote agent produces for the task — the deliverable(s) handed back
The Agent Card is the discovery keyThe Agent Card is A2A's equivalent of a tool's description (C2) or an MCP server's tool list (I1): it's how a client agent finds out what a peer can do and how to reach it — without a human wiring them together in advance. Publish a good card and other agents can discover and delegate to yours automatically.

Lab I2.1 · A delegation flow intermediate

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.

client agent remote agent 1 · fetch Agent Card (discover skills) 2 · send task (delegate work) 3 · stream status + messages 4 · return artifact(s) when complete Discover → delegate → stream → deliver. The client fetches the remote's Agent Card to learn its skills, sends a task, receives streaming status/message updates (tasks can run long — streaming is first-class, like C2), and collects artifacts when done. If the remote needs more info, the task enters an input-required state and asks back.
🗺️ How to read this diagram

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-required state 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.

StepWhat happensFamiliar from…
DiscoverClient reads the remote's Agent Card (skills, endpoint, auth)Tool/server discovery (I1, C2)
DelegateClient submits a task with an initial messageSupervisor → worker handoff (L1, M1)
CollaborateTask streams status; may hit input-required and ask backStreaming (C2); human-in-the-loop shape (L5)
DeliverRemote returns artifacts; task reaches a terminal stateArtifacts / tool results (V3, C2)
Long-running tasks are the design centerA tool call returns in seconds; a delegated task to another agent might take minutes or hours (it's doing real work). A2A treats tasks as first-class, long-lived objects with a lifecycle and streaming updates — the same reason LangGraph made state durable and resumable (L5). Don't model a peer agent as a synchronous function call.

A2A vs in-process multi-agent (M1/M2) intermediate

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 codebaseAgents are built by different teams/companies
They share a framework & runtimeThey run on different frameworks/stacks
You control deployment of all of themYou call an agent you don't operate
Tight coupling is fineYou need a stable contract across a boundary
Same topologies, wider boundaryA2A doesn't replace the supervisor/network topologies of L1 — it lets them span organizations. Your CrewAI crew can have, as one "member," a remote A2A agent from a vendor. The coordination pattern is identical; A2A is the wire protocol that makes a member reachable across the boundary.

Trust, auth & reliability across the boundary intermediate

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.

ConcernWhy it's sharper across A2A
AuthenticationThe Agent Card declares auth requirements; you present credentials to a service you don't run — scope them tightly (I1, T1)
Trust of outputA remote agent's artifacts are untrusted input — they can carry prompt injection just like tool results (T1). Validate before acting
ReliabilityThe peer can be slow, down, or fail mid-task — treat it like any remote dependency: timeouts, retries, circuit breaker (A6, O3)
Data exposureDelegating a task sends data across a boundary — decide what's safe to share, mind residency & privacy (O4)
Cost & loopsAgents calling agents can fan out expensively or loop — bound it, as with any multi-agent system (M2)
A remote agent is an untrusted, autonomous dependencyEverything you learned about gating tools (L3), vetting MCP servers (I1), and treating retrieved content as hostile (T1) applies double to A2A: the other side reasons on its own, may be adversarial or compromised, and sits outside your control. Authenticate, scope what you delegate, validate what comes back, and keep your own safety gate (L5) in front of anything you do with its output.

Common pitfalls advanced

PitfallFix
Reaching for A2A when in-process would doOnly cross-boundary needs A2A; keep your own agents in-process
Modeling a peer agent as a sync functionTreat tasks as long-running, streamed, stateful
Trusting a remote agent's artifactsUntrusted input — validate; guard against injection (T1)
No timeout/retry on remote callsIt's a remote dependency — apply A6/O3 resiliency
Over-broad credentials to a peerLeast privilege; scope to the delegated task
Unbounded agent-to-agent fan-outCap 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.

Exercise 1 · Read an Agent CardBeginner

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.

Exercise 2 · The Task lifecycle as a state machineIntermediate

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.

Exercise 3 · Discovery: pick the right peer for a taskAdvanced

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.

Exercise 4 · Model a full delegate -> stream -> artifact flowExpert

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.

Exercise 5 · A2A vs MCP boundary: route each connectionProfessional

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.

Exercise 6 · Cross-org delegation with trust + reliability checksIndustry scenario

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.
🏗️ Toward the capstoneImagine the AI DevOps Engineer needing a specialist it doesn't own — a security-scanning agent run by another team. Rather than absorbing it, it could delegate via A2A: discover the scanner's Agent Card, hand it a task, stream results, and treat the findings as untrusted input behind its own safety gate (L5). A2A is how the capstone would reach beyond its own walls without swallowing everyone else's code. Revisit multi-agent orchestration →

Knowledge check check yourself

✓ Knowledge check

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
MCP connects your agent down to tools/data — a capability you invoke and own, returning a result in seconds. A2A connects your agent sideways to another autonomous agent (often across an org boundary) that has its own reasoning and state; you delegate a task that may run long, stream updates, and even ask back — so it's a long-lived, stateful object, not a synchronous function call.
✓ Knowledge check

What is an Agent Card and why is it central to A2A's discover→delegate→stream→deliver flow?

Show answer
The Agent Card is a published JSON descriptor at a well-known URL advertising an agent's identity, skills, endpoint, and auth requirements. It's A2A's discovery key — the client agent fetches it to learn what a peer can do and how to reach it, so agents can discover and delegate to each other automatically without a human wiring them together in advance.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in