A2A — Agent-to-Agent Protocol

Tools and Protocols7 min read

MCP is agent-to-tool. A2A (Agent2Agent) is agent-to-agent — an open protocol for letting opaque agents built on different frameworks collaborate. Released by Google in April 2025, donated to the Linux Foundation in June 2025, reaching v1.0 in April 2026 with 150+ supporters including AWS, Cisco, Microsoft, Salesforce, SAP, and ServiceNow. It absorbed IBM's ACP and added the AP2 payments extension. This lesson walks the Agent Card, Task lifecycle, and the three protocol bindings, using the A2A 1.0.1 wire names.

Type: Build Languages: Python (stdlib, Agent Card + Task harness) Prerequisites: Phase 13 · 06 (MCP fundamentals), Phase 13 · 08 (MCP client) Time: ~75 minutes

Learning Objectives

  • Distinguish agent-to-tool (MCP) from agent-to-agent (A2A) use cases.
  • Publish an Agent Card at /.well-known/agent-card.json with skills and supportedInterfaces metadata.
  • Walk the Task lifecycle: TASK_STATE_SUBMITTED, TASK_STATE_WORKING, TASK_STATE_INPUT_REQUIRED, and the terminal states TASK_STATE_COMPLETED, TASK_STATE_FAILED, TASK_STATE_CANCELED, TASK_STATE_REJECTED.
  • Use Messages whose Parts each hold one of text, raw, url, or data, and Artifacts as outputs.

The Problem

A customer-service agent needs to delegate report-writing to a specialized writer agent. Options pre-A2A:

  • Custom REST API. Works but every pairing is a one-off.
  • Shared codebase. Requires the two agents to run the same framework.
  • MCP. Doesn't fit: MCP is for calling tools, not for two agents collaborating while preserving each agent's opaque internal reasoning.

A2A fills the gap. It models the interaction as one agent sending a Task to another, with a lifecycle, messages, and artifacts. The called agent's internal state stays opaque — the caller sees only task state transitions and eventual outputs.

A2A is the "let agents across frameworks talk to each other" protocol. It does not replace MCP; the two are complementary.

The Concept

Agent Card

Every A2A-compliant agent publishes a card at /.well-known/agent-card.json:

{
  "name": "research-agent",
  "description": "Summarizes academic papers and drafts citations.",
  "version": "1.2.0",
  "supportedInterfaces": [
    {
      "url": "https://research.example.com/a2a",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    }
  ],
  "capabilities": {"streaming": true, "pushNotifications": true},
  "securitySchemes": {
    "bearer": {"httpAuthSecurityScheme": {"scheme": "Bearer"}}
  },
  "securityRequirements": [{"schemes": {"bearer": {"list": []}}}],
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/markdown"],
  "skills": [
    {
      "id": "summarize_paper",
      "name": "Summarize a paper",
      "description": "Read a paper PDF and produce a 3-paragraph summary.",
      "tags": ["research", "summarization"],
      "inputModes": ["text/plain", "application/pdf"],
      "outputModes": ["text/markdown"]
    }
  ]
}

Discovery is URL-based: fetch the card, pick the first supportedInterfaces entry whose protocolBinding your client speaks, and enumerate skills. Input and output modes are media types.

Signed Agent Cards

A card can carry a signatures array. Each entry is a JWS (RFC 7515) computed over the card's RFC 8785 canonical JSON, with the signatures field left out. Consumers canonicalize the card the same way and verify. Prevents impersonation.

Task lifecycle

TASK_STATE_SUBMITTED
  -> TASK_STATE_WORKING
  -> TASK_STATE_COMPLETED | TASK_STATE_FAILED | TASK_STATE_CANCELED | TASK_STATE_REJECTED

TASK_STATE_WORKING
  -> TASK_STATE_INPUT_REQUIRED
  -> TASK_STATE_WORKING (the client sends a message with the same taskId)

Clients initiate with SendMessage, and the server creates the Task. The called agent transitions through states; clients poll with GetTask or stream over SSE with SendStreamingMessage and SubscribeToTask. A stream carries statusUpdate and artifactUpdate events and closes when the task reaches a terminal state. There is no final flag.

Messages and Parts

A message has a messageId, a role (ROLE_USER or ROLE_AGENT), and one or more Parts. Each Part holds exactly one content field, and that field name is the type. There is no kind field.

  • text: plain content.
  • raw: file bytes, base64 in JSON, usually with filename and mediaType.
  • url: a link to file content.
  • data: structured JSON payload (structured input for the called agent).

Example:

{
  "messageId": "msg-001",
  "role": "ROLE_USER",
  "parts": [
    {"text": "Summarize this paper."},
    {"raw": "...", "filename": "paper.pdf", "mediaType": "application/pdf"},
    {"data": {"targetLength": "3 paragraphs"}, "mediaType": "application/json"}
  ]
}

Artifacts

Outputs are Artifacts, not raw strings. An Artifact is a named, typed output:

{
  "artifactId": "art-001",
  "name": "summary",
  "parts": [{"text": "...", "mediaType": "text/markdown"}]
}

Artifacts can be streamed as chunks. Each artifactUpdate event carries the artifact plus append and lastChunk. The caller accumulates.

Three protocol bindings

  1. JSON-RPC 2.0 over HTTP (JSONRPC). POST for requests, SSE for streaming. Methods are PascalCase: SendMessage, SendStreamingMessage, GetTask, ListTasks, CancelTask, SubscribeToTask, CreateTaskPushNotificationConfig, GetTaskPushNotificationConfig, ListTaskPushNotificationConfigs, DeleteTaskPushNotificationConfig, and GetExtendedAgentCard.
  2. gRPC (GRPC). For enterprise environments where gRPC is native. Same method names.
  3. HTTP+JSON/REST (HTTP+JSON). Resource URLs such as POST /message:send and GET /tasks/{id}.

All three bindings carry the same data model. Each supportedInterfaces entry names one binding and its protocolVersion. Clients send the header A2A-Version: 1.0 on every request, because a server reads a request without it as version 0.3.

POST /a2a HTTP/1.1
Host: research.example.com
Content-Type: application/json
A2A-Version: 1.0

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "msg-001",
      "role": "ROLE_USER",
      "parts": [{"text": "Summarize this paper."}]
    }
  }
}

Opacity preservation

A key design principle: the called agent's internal state is opaque. The caller sees task state and artifacts. The called agent's chain-of-thought, its tool calls, its sub-agent delegation — all invisible. This is different from MCP, where tool calls are transparent.

Rationale: A2A enables competitors to collaborate without revealing internals. A2A can be "call this customer-service agent" without the caller learning how that agent implements the service.

Timeline

  • 2025-04-09. Google announces A2A.
  • 2025-06-23. Donated to Linux Foundation.
  • 2025-08. Absorbs IBM's ACP.
  • 2025-09. AP2 extension (Agent Payments) ships.
  • 2026-04. v1.0 released with 150+ supporting organizations.

Relationship to MCP

DimensionMCPA2A
Use caseAgent-to-toolAgent-to-agent
OpacityTransparent tool callsOpaque inner reasoning
Typical callerAgent runtimeAnother agent
StateTool-call resultTask with lifecycle
AuthorizationOAuth 2.1 (Phase 13 · 16)Agent Card securitySchemes + securityRequirements
TransportStdio / Streamable HTTPJSON-RPC / gRPC / HTTP+JSON

Use MCP when you want to invoke a specific tool. Use A2A when you want to delegate a whole task to another agent. Many production systems use both: an agent uses MCP for its tool layer and A2A for its collaboration layer.

Figure a2a-task-lifecycle

Use It

code/main.py implements a minimal A2A harness: the writer agent publishes its card, the research agent sends it a SendMessage request with a PDF part and a text instruction, and the task moves through TASK_STATE_WORKING → TASK_STATE_INPUT_REQUIRED → TASK_STATE_WORKING → TASK_STATE_COMPLETED before returning a text artifact. All stdlib; uses an in-memory transport to focus on message shapes.

What to look at:

  • Agent Card JSON shape.
  • Server-side task id assignment and state transitions.
  • Parts typed by which content field is present.
  • TASK_STATE_INPUT_REQUIRED branch mid-task.
  • Artifact return on completion.

Ship It

This lesson produces outputs/skill-a2a-agent-spec.md. Given a new agent that should be callable by other agents, the skill produces the Agent Card JSON, skills schema, and endpoint blueprint.

Exercises

  1. Run code/main.py. Trace the full Task lifecycle, including the TASK_STATE_INPUT_REQUIRED pause where the called agent asks for a clarification.

  2. Add a signed Agent Card. Put one JWS entry in signatures with alg set to HS256, signing the card's canonical JSON without the signatures field. Write a verifier and confirm it fails on a mutated card.

  3. Implement task streaming with SendStreamingMessage: the writer agent emits the task, three artifactUpdate chunks, and a statusUpdate with TASK_STATE_COMPLETED, then closes the stream. The caller accumulates the chunks.

  4. Design an A2A agent that wraps an MCP server. Map each MCP tool to an A2A skill. Note the trade-offs — what opacity is lost?

  5. Read the A2A v1.0 announcement and identify the one feature that is not yet implemented by any framework as of April 2026. (Hint: it relates to multi-hop task delegation.)

Key Terms

TermWhat people sayWhat it actually means
A2A"Agent-to-Agent protocol"Open protocol for opaque agent collaboration
Agent Card"/.well-known/agent-card.json"Published metadata describing an agent's skills and supportedInterfaces
Skill"A callable unit"A named operation the agent supports (analog to MCP tool)
Task"Unit of delegation"A work item with a lifecycle and final artifact
Message"Task input"Carries Parts (text, raw, url, data)
Part"Typed chunk"Exactly one of text / raw / url / data, plus optional mediaType; no kind field
Artifact"Task output"Named, typed output returned on completion
AP2"Agent Payments Protocol"Payments extension built on A2A; card signing is core A2A (signatures)
Opacity"Black-box collaboration"Called agent's internals are hidden from caller
TASK_STATE_INPUT_REQUIRED"Task pause"Interrupted state when the agent needs more info

Further Reading

Reading free — progress needs a free account

Create an account to mark lessons done, save quiz attempts and unlock the AI tutor.

Start free