Phase 13 · A2A Standard Protocol (Google Agent-to-Agent)
Why
Phase 12 used a native LangGraph multi-agent (supervisor + worker) to demonstrate "agent calls agent" — but that happens inside one process, orchestrated by a shared state graph.
In production, agents often run in different processes, machines, or even companies: a "researcher agent" hosted by company A, a "writer agent" hosted by company B. To call each other they cannot share memory — they need a standard protocol. That is Google's A2A (Agent-to-Agent) protocol: let arbitrary agents "discover each other, submit tasks, and get results back", independent of framework.
One-liner: MCP = agent ↔ tool; Phase 12 = agent ↔ agent (in-process); Phase 13 = agent ↔ agent (cross-process, standard protocol).
This Phase implements a minimal, spec-faithful A2A server/client from scratch (zero extra dependencies) so you can see exactly what travels on the wire; for production, swap in the official a2a-sdk.
What
The A2A protocol boils down to three things:
- Agent Card: each agent exposes a JSON card over HTTP describing "who I am, what I can do (skills), and how to reach me (url, capabilities, input/output modes)". The card is served at
/.well-known/agent-card.jsonby default. - JSON-RPC 2.0 transport: requests
{jsonrpc:"2.0", id, method, params}, responses{jsonrpc:"2.0", id, result}or{..., error}. The most common method ismessage/send(non-streaming). - Message data model:
{role:"user"|"agent", parts:[{type:"text", text:"..."}]}— a message is made of "parts" (text / file / data).
A typical exchange:
Client A2A Server (writer agent)
│ GET /.well-known/agent-card.json │
│ ─────────────────────────────────> │ returns AgentCard (name/skills/capabilities)
│ <───────────────────────────────── │
│ POST / {jsonrpc, method:"message/send", params:{message:{...}}} │
│ ─────────────────────────────────> │ runs the real LLM
│ <───────────────────────────────── │ returns result:{role:"agent", parts:[...]}How
1. Agent Card (a2a_server.py)
def make_agent_card() -> dict:
return {
"name": "LangGraph Writing Assistant",
"description": "Expands points/questions into fluent prose.",
"version": "1.0.0",
"url": "http://127.0.0.1:9999",
"capabilities": {"streaming": False, "pushNotifications": False},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{"id": "expand", "name": "Expand into prose",
"examples": ["Turn these points into a paragraph: ..."]}
],
}2. Server handles message/send (JSON-RPC 2.0)
class A2AHandler(BaseHTTPRequestHandler):
def do_POST(self):
req = json.loads(self.rfile.read(...))
method = req.get("method")
if method == "message/send":
user_text = _extract_text(req["params"]["message"])
answer = run_agent(user_text) # call the real LLM
result = {"role": "agent",
"parts": [{"type": "text", "text": answer}],
"messageId": uuid.uuid4().hex}
self._send_json(200, {"jsonrpc": "2.0", "id": req["id"], "result": result})3. Client: discover → send → receive (hello_chain.py)
card = http_json(f"{base}/.well-known/agent-card.json") # 1. discover
request = {"jsonrpc": "2.0", "id": "p13-demo-1",
"method": "message/send",
"params": {"message": {"role": "user",
"parts": [{"type": "text", "text": question}]}}}
response = http_json(base, method="POST", payload=request) # 2. send task
# 3. read the agent's answer from response["result"]["parts"]The client does not care whether the server is LangChain, LangGraph, or another framework — that decoupling is exactly the value of A2A.
Run
One-shot (the script starts the server in a background thread, then talks to it as a client):
make run p=13
# or python -m examples.p13.hello_chainOr run manually in two terminals (closer to real cross-process):
# terminal 1: start the server
python -m examples.p13.a2a_server
# terminal 2: call it as a client, pointing A2A_BASE_URL at the remote server
A2A_BASE_URL=http://127.0.0.1:9999 python -m examples.p13.hello_chainTypical output:
===== Agent Card discovered =====
Name: LangGraph Writing Assistant
Description: Expands points/questions into fluent prose.
Skills: ['Expand into prose']
Version: 1.0.0
===== Sending A2A request (message/send) =====
User: explain what an Agent is to a non-technical person in three sentences
===== Agent answer =====
An agent is like a digital assistant that understands you and figures out how to finish the task...Summary
- A2A = Agent Card (discovery) + JSON-RPC 2.0 (transport) + Message (data model), framework-agnostic, built for "cross-process / cross-org agent interconnection".
- This Phase implements its minimal equivalent from scratch and runs via
make run; the data shapes are aligned with the official spec, and production can swap ina2a-sdk. - By now the full agent-interconnection picture is assembled: P11 MCP (agent↔tool) → P12 native multi-agent (agent↔agent, in-process) → P13 A2A (agent↔agent, cross-process standard protocol).
- Next steps:
message/stream(SSE streaming),tasks/get/tasks/cancel(long-task state machine), auth (theauthenticationfield in Agent Card + OAuth/mTLS), and the officiala2a-sdk's gRPC/REST transports.