A2A: When Agents Talk to Agents

Part of the free Generative AI course on LogicWiz, module: Nova Joins the Network.

Episode 37: A2A — When Agents Talk to Agents

"Nova, verify this company." It wasn't one lookup. It was: query the database, cross-check an external map, weigh the two, and decide. Not a tool. A whole job — one Nova wanted to hand to a specialist.


When a Tool Isn't Enough

Everything so far — MCP — is about reaching a tool: fetch a value, run a query, take one action. But some jobs aren't a single call. Consider verifying a company:

  1. Query a database for its registered name and address.
  2. Cross-check that address against an external map API.
  3. Reason over the two results and decide "pass" or "fail".

That's a multi-step workflow with reasoning in the middle — the sort of thing you'd wrap in its own agent. And when one agent needs to hand a job like that to another agent, tools aren't the right abstraction. You need agent-to-agent communication: A2A.

📌 The clean split you'll use for the rest of your career: MCP exposes a tool or data (single, often deterministic capability). A2A delegates a task to another agent (multi-step, reasoning-driven). One is "run this function"; the other is "handle this for me."

Before A2A: Hand-Rolled Contracts

Agents can talk without a standard — you just make it up. Agent A sends Agent B a data packet: a to-field, a from-field, some dates. Agent B has to know the exact shape to parse it.

That works until it doesn't. Both sides must agree on a contract — the precise structure of inputs and outputs — exactly like a UI team and a backend team agreeing on an API shape before the frontend can talk to the backend. Every new pair of agents is a new bespoke contract to design, document, and keep in sync. Sound familiar? It's the N×M coupling problem again, wearing a different hat.

So Google introduced A2A: a standard way for agents to find and talk to each other — no bespoke contract per pair. And like MCP, it comes in two phases: discovery and use.

Discover and Use

Watch one agent discover another and hand off a task:

{{visual:a2a}}

The pattern mirrors MCP's "list then call", one level up:

  • Discovery — a client agent finds which other agents exist and what they can do. ("Is there a flight agent? A legal agent?")
  • Use — once it's chosen one, it sends that agent an actual task and waits for the result.

How does discovery work? Every agent runs as a server (just like an MCP server), and to be found, each one publishes an agent card.

The Agent Card

An agent card is a business card for an agent — the metadata another agent reads to decide whether, and how, to work with it. Build one field by field:

{{visual:agent-card}}

The two fields that carry the most weight are description (what a calling agent reads to decide whether to delegate here) and skills — a list, because one agent can do several distinct things ("book a flight", "check a visa"). Publish this card, and any other agent can discover you and know how to call you. No bespoke contract required.

Picking a Teammate

Discovery, made real. An orchestrator has found three agents; it must pick the right one for a request — by reading their cards. Walk the code, then run it:

{{visual:agent-select-walkthrough}}

{{cell:l37-select}}

The model chooses visa_agent for a visa question over the flight and weather agents — reasoning over descriptions, not keywords. That selection is the discovery phase. Next comes the use phase.

Handing Over a Task

In the use phase, the calling agent (call it A0) sends the chosen agent (A3) a task — the A2A word for a unit of work. A3 immediately stamps it with a task ID, because it may be juggling many incoming tasks at once and needs to track which is which. Each task carries a status: not startedrunningcompleted.

A0 now has to wait for a result — and agent tasks can be slow (an agent might reason for a while, or even pause for a human). There are three ways to handle the wait:

  • Polling — A0 asks "done yet?" every few seconds. Simple; the usual default.
  • Streaming (SSE) — A0 holds an open connection and receives updates as they happen.
  • Registration / postback — A0 says "notify me when you're done," then goes away. When A3 finishes, it fires the notification and A0 comes back for the result. More efficient — no wasted checks.

⚠️ Because tasks can run long — even pausing for human-in-the-loop approval that blows past normal timeouts — A2A frameworks lean on state management. In-memory state keeps a task's progress so it can resume; persistent state (a database) lets it survive a crash. This is why A3 needs that task store.

Building an A2A Agent

So what does exposing an agent over A2A look like? Google's package makes it a template — and it rhymes with the fastmcp server from Episode 33: define the logic, define the metadata, bundle, serve.

{{visual:a2a-server-walkthrough}}

Two halves click together: the agent card (identity, for discovery) and the request handler + executor + task store (logic, for the use phase). A2AStarletteApplication bundles them, and uvicorn serves it. We read rather than run it — like an MCP server, an A2A agent is its own web process.

On authentication: A2A supports the usual options — none, bearer token, API key, or custom headers for secrets — and the check lives in the request handler, where incoming tasks arrive. Same instinct as MCP: guard the door where requests come in.

What Nova Learns Next

Nova can now reach tools through MCP and delegate whole tasks to other agents through A2A. The last question is the architect's: given a job, which one do you reach for — and how do you keep a fleet of both under control? Episode 38 brings it home.