Agent-to-Agent Protocol (A2A)

Section 27.3

"Talking to yourself is fine. Talking to another agent requires a protocol."

PipPip, Diplomatically Networked AI Agent
Big Picture

If MCP connects agents to tools, A2A connects agents to each other. Complex enterprise workflows span multiple systems, each managed by a different agent built by a different team. Without a standard protocol, integrating these agents requires brittle point-to-point connections. Google's Agent-to-Agent Protocol (A2A) provides a common language for agent discovery (Agent Cards), task delegation (Tasks), and structured communication (Messages), all built on standard HTTP and JSON-RPC. This section covers the protocol design, implementation patterns, and the relationship between A2A and MCP as complementary standards in the agentic ecosystem.

Prerequisites

This section builds on agent foundations from Chapter 26 and LLM API basics from Chapter 11.

Three friendly robots of different colors sitting around a round table, each holding a business card showing their specialty, passing structured message envelopes to each other with a shared task board floating above
Figure 27.3.1: The A2A protocol enables independent agents to discover each other, advertise their capabilities, and coordinate on shared tasks through structured message exchange.

27.3.1 The Agent-to-Agent Protocol

Fun Fact

A2A is the protocol that Anthropic and Google co-published in 2024 to let agents from different vendors talk to each other. The spec is roughly the length of HTTP/1.1 and contains exactly one diagram showing a rabbit hole, an early acknowledgment that nobody actually knows how far multi-agent federations can scale before the bills do.

While MCP connects agents to tools and data sources, the Agent-to-Agent Protocol (A2A) connects agents to each other. Developed by Google and announced in April 2025, A2A defines a standard for how independently built agents discover each other's capabilities, negotiate task assignments, exchange intermediate results, and coordinate complex workflows. If MCP is USB for AI tools, A2A is the internet protocol for AI agent.

The need for A2A arises from the reality that complex enterprise workflows span multiple systems, each potentially managed by a different agent built by a different team using a different framework. A customer onboarding workflow might involve a document processing agent (built with LangGraph), a compliance checking agent (built with the OpenAI Agents SDK), and an account provisioning agent (a custom Python service). Without a standard protocol, integrating these agents requires brittle point-to-point connections. A2A provides the common language.

A2A is built on three core concepts: Agent Cards (JSON metadata files that describe an agent's capabilities, endpoint, and authentication requirements), Tasks (the unit of work exchanged between agents, with a defined lifecycle), and Messages (the communication medium within a task, supporting text, files, and structured data). The protocol uses standard HTTP/JSON-RPC, making it compatible with existing web infrastructure.

Key Insight

A2A and MCP are complementary, not competing, protocols. MCP connects agents to tools (agent-to-tool communication). A2A connects agents to other agents (agent-to-agent communication). A well-architected system uses both: individual agents use MCP to access their tools and data sources, and A2A to coordinate with other agents in multi-agent workflows. Think of MCP as the arms (how an agent interacts with the world) and A2A as the voice (how agents talk to each other).

Agent Cards

This snippet defines an A2A (Agent-to-Agent) agent card that advertises capabilities and endpoint information.

{
 "name": "compliance-checker",
 "description": "Validates documents against regulatory requirements for financial services. Supports KYC, AML, and SOX compliance checks.",
 "url": "https://agents.example.com/compliance",
 "version": "2.1.0",
 "capabilities": {
 "streaming": true,
 "pushNotifications": false,
 "stateTransitionHistory": true
 },
 "skills": [
 {
 "id": "kyc_check",
 "name": "KYC Compliance Check",
 "description": "Validates customer identity documents against Know Your Customer regulations. Accepts passport, drivers license, or national ID.",
 "inputModes": ["text", "file"],
 "outputModes": ["text"]
 },
 {
 "id": "aml_screening",
 "name": "AML Screening",
 "description": "Screens individuals and entities against sanctions lists and PEP databases.",
 "inputModes": ["text"],
 "outputModes": ["text"]
 }
 ],
 "authentication": {
 "type": "bearer",
 "tokenUrl": "https://auth.example.com/token"
 }
}
Code Fragment 27.3.1a: This JSON defines an A2A Agent Card with name, description, capabilities (including streaming and pushNotifications), and a skills array listing supported task types. The endpoint URL and authentication requirements tell client agents how to connect and what operations the agent supports.

27.3.2 Task Lifecycle

Every A2A interaction revolves around a Task. The client agent creates a task by sending a request to the server agent's endpoint. The task progresses through well-defined states: submitted, working, input-required (when the server agent needs more information), completed, failed, or canceled. This state machine provides clear visibility into where work stands and enables proper error handling at each transition.

The input-required state is particularly important for enterprise workflows. A compliance checking agent might need additional documents, or a clarification about which jurisdiction's regulations apply. Rather than failing or guessing, the agent signals that it needs input, the requesting agent (or a human) provides it, and processing resumes. This turn-based interaction model supports the kind of back-and-forth that complex real-world tasks require.

A2A supports both synchronous and streaming task execution. For quick operations (a lookup, a simple check), the client sends a request and receives a complete response. For long-running operations (document analysis, multi-step processing), the server streams progress updates and partial results. The client can monitor task state, cancel tasks that are taking too long, and handle timeouts gracefully. This flexibility accommodates the wide range of latencies inherent in agentic systems.

The six states form a finite state machine, and treating it as one (rather than as a loose set of status strings) is what makes A2A clients robust. Three of the states are terminal (completed, failed, canceled): once a task enters them no further transition is legal, so a client can safely release any resources it was holding. Two states are active (working, input-required): the task is alive and the client must keep listening. The one initial state (submitted) exists only briefly between the client posting the task and the server picking it up. Algorithm 27.3.1 below makes the legal transitions and their triggering events explicit; the client and server each maintain their own copy of this machine and the protocol's status-update messages are how they keep the two copies in sync.

Algorithm 27.3.1

The A2A task lifecycle as a state machine. The left column is the current state, the event is what the server observes or the client sends, and the right column is the resulting state. Only transitions listed here are legal; a status-update message announcing any other pair indicates a non-conforming peer and should be rejected. Terminal states (completed, failed, canceled) absorb: no event moves the task out of them.

Input: a task created by the client via tasks/send
Output: a task that ends in exactly one terminal state
States: submitted (initial), working, input-required (active),
        completed, failed, canceled (terminal)

1. start in submitted
   // client has posted the task; server has not yet begun work

2. transitions (current_state --[event]--> next_state):
   submitted      --[server dequeues and begins]--> working
   working        --[server needs a clarification/file]--> input-required
   input-required --[client supplies the requested input]--> working
   working        --[server produces the final artifact]--> completed
   working        --[unrecoverable error]--> failed
   input-required --[input deadline exceeded]--> failed
   any-active     --[client sends tasks/cancel]--> canceled

3. on entering a terminal state:
   a. server emits a final TaskStatusUpdateEvent with final=true
   b. client stops polling/streaming and releases the task handle

Two pieces of structure in Algorithm 27.3.1 are worth dwelling on. First, the only way back into working from input-required is a client-supplied input: the server cannot un-stick itself, which is precisely the property that lets a human or a calling agent stay in the loop on consequential decisions. Second, canceled is reachable from any active state but never from a terminal one, so a late tasks/cancel arriving after completed is a no-op rather than an error. Encoding these rules in the client (rejecting illegal transitions, ignoring post-terminal events) is what separates a robust A2A integration from one that corrupts its own task table when a peer misbehaves.

A Worked JSON-RPC Delegation Trace

To see the lifecycle in motion, consider an Intake Agent (the client) delegating a KYC check to the compliance agent whose Agent Card appears in Code Fragment 27.3.1a. Assumptions: the transport is HTTPS POST to the agent's url with each body a single JSON-RPC 2.0 object; authentication is a bearer token (from the card's authentication.tokenUrl) carried in the Authorization header on every request, so it is omitted from the bodies below for brevity; because the card advertises "streaming": true, the client opens the task with tasks/sendSubscribe and the server pushes status updates over Server-Sent Events rather than making the client poll. The following sequence of JSON-RPC messages walks the task through submitted to working to input-required to working to completed.

The client opens the task. The method is tasks/sendSubscribe; the params carry a client-chosen task id and the first message (role user, a text part naming the document to check):

// 1. Client -> Server: open the task and subscribe to updates
{
 "jsonrpc": "2.0",
 "id": 1,
 "method": "tasks/sendSubscribe",
 "params": {
  "id": "task-kyc-7f3a",
  "message": {
   "role": "user",
   "parts": [{"type": "text", "text": "Run a KYC check on applicant 88412."}]
  }
 }
}
Code Fragment 27.3.2a: The opening request. The client-assigned params.id (task-kyc-7f3a) names the task for the lifetime of the exchange, and tasks/sendSubscribe (rather than plain tasks/send) is what asks the server to stream subsequent status changes.

The server acknowledges by returning the task in submitted state, then immediately streams a working update as it dequeues the job. Each streamed event is a JSON-RPC result on the original request id, carrying a status.state field:

// 2. Server -> Client (initial result): task accepted
{"jsonrpc": "2.0", "id": 1,
 "result": {"id": "task-kyc-7f3a", "status": {"state": "submitted"}}}

// 3. Server -> Client (streamed): work has begun
{"jsonrpc": "2.0", "id": 1,
 "result": {"id": "task-kyc-7f3a",
  "status": {"state": "working"}, "final": false}}
Code Fragment 27.3.2b: The submitted to working transition observed by the client. Both messages are JSON-RPC results sharing id: 1; the final: false flag tells the client more updates are coming, matching step 1 of Algorithm 27.3.1.

Mid-task the server discovers the passport scan is missing, so it transitions to input-required and streams an agent message describing exactly what it needs. The client (or the human behind it) answers by sending a follow-up tasks/send on the same task id, which drives the machine back to working:

// 4. Server -> Client (streamed): more input is needed
{"jsonrpc": "2.0", "id": 1,
 "result": {"id": "task-kyc-7f3a",
  "status": {"state": "input-required",
   "message": {"role": "agent",
    "parts": [{"type": "text", "text": "Provide a passport scan for applicant 88412."}]}},
  "final": false}}

// 5. Client -> Server: supply the requested input on the same task id
{"jsonrpc": "2.0", "id": 2, "method": "tasks/send",
 "params": {"id": "task-kyc-7f3a",
  "message": {"role": "user",
   "parts": [{"type": "file", "file": {"name": "passport.png", "uri": "https://files.example.com/88412/passport.png"}}]}}}
Code Fragment 27.3.2c: The input-required round trip. The server's update carries an agent-role message stating precisely what is missing; the client's reply reuses params.id: task-kyc-7f3a so the server resumes the existing task rather than starting a new one, executing the input-required to working edge.

With the passport in hand the server finishes, transitioning working to completed and attaching the result as an artifact. The final event sets final: true, which (per step 3 of Algorithm 27.3.1) tells the client to stop listening and release the handle:

// 6. Server -> Client (streamed, terminal): task done, artifact attached
{"jsonrpc": "2.0", "id": 1,
 "result": {"id": "task-kyc-7f3a",
  "status": {"state": "completed"},
  "artifacts": [{"name": "kyc_result",
    "parts": [{"type": "text", "text": "KYC PASS: identity verified, no sanctions match."}]}],
  "final": true}}
Code Fragment 27.3.2d: The terminal completed result. The verdict travels in artifacts (named, reusable outputs) rather than in a transient status message, and final: true closes the SSE stream, completing the submitted -> working -> input-required -> working -> completed path traced in Algorithm 27.3.1.
Warning
Two failure modes the state machine does not fix for free

The lifecycle is clean on paper but two failure modes survive into production. The lost update: A2A status changes stream over SSE, and SSE has no built-in redelivery, so a dropped connection during the working to completed transition leaves the client believing the task is still working while the server considers it done. The fix is to treat the stream as a hint and reconcile against authoritative state with a tasks/get poll on reconnect, using the client-assigned task id; never let a missed event be the only record of a terminal transition. Stuck in input-required: if the client agent that asked for the passport crashes before answering, the task sits in input-required forever, holding server resources, because nothing in the core machine forces progress. Production servers add a timeout edge (the input-required to failed transition shown in Algorithm 27.3.1) so an unanswered request eventually fails cleanly rather than leaking the task.

Real-World Scenario: Multi-Agent Loan Processing with A2A

Who: An engineering director at a regional bank modernizing their mortgage application pipeline.

Situation: The bank had four separate teams responsible for intake, credit scoring, regulatory compliance, and underwriting. Each team had built its own AI agent using different frameworks (LangChain, PydanticAI, a custom Python service, and a Semantic Kernel agent on Azure).

Problem: Connecting these agents required point-to-point integrations with custom message formats. Every time one team updated their agent's API, the other teams' integrations broke. A single loan application touched all four agents, and failures were difficult to trace across the boundaries.

Decision: The team adopted A2A as the coordination protocol. Each agent published an Agent Card describing its capabilities. The Intake Agent created tasks for Credit and Compliance agents in parallel; when both completed, it forwarded combined results to the Underwriting Agent. The Compliance Agent could enter an input-required state when additional documents were needed, pausing the flow gracefully.

Result: Cross-team integration incidents dropped from 6 per month to fewer than 1. Average loan processing time fell from 48 hours to 14 hours because parallel agent execution replaced sequential handoffs.

Lesson: Standardized agent-to-agent protocols let independently maintained agents collaborate without shared code, turning organizational boundaries from friction points into clean interfaces.

27.3.3 Federation and Discovery

A2A includes mechanisms for agent discovery. Agent Cards can be published to registries where other agents can search for capabilities. A supervisory agent that needs "document processing" capabilities can query a registry, discover agents that offer those skills, evaluate their descriptions and authentication requirements, and dynamically route tasks to the most appropriate agent. This federation model enables ecosystems of agents that can collaborate without being pre-configured to know about each other.

Security in A2A follows standard web patterns: OAuth 2.0 for authentication, TLS for transport security, and capability-based access control defined in the Agent Card. The protocol also supports audit logging of all inter-agent communication, which is essential for compliance-sensitive industries where every decision and data exchange must be traceable.

Warning

A2A is still a relatively new protocol (announced April 2025) and the ecosystem is less mature than MCP's. While the specification is stable, production tooling, client libraries, and debugging tools are still evolving. For teams adopting A2A today, plan for additional integration testing and expect to contribute fixes and improvements back to the ecosystem. Start with internal agent-to-agent communication before exposing A2A endpoints externally.

Tip: Implement Idempotent Tool Calls

Design tools so that calling them twice with the same parameters produces the same result without side effects. This lets you safely retry failed agent steps without worrying about duplicate actions (double-booking, double-charging, duplicate writes).

Key Takeaways
Self-Check
Q1: What is the Agent-to-Agent (A2A) protocol, and how does it complement MCP?
Show Answer

A2A is a protocol for agents to communicate with each other as peers, enabling task delegation, status updates, and result exchange. While MCP connects agents to tools and data sources, A2A connects agents to other agents, enabling multi-agent collaboration without a centralized orchestrator.

Q2: What is an Agent Card in the A2A protocol, and what purpose does it serve?
Show Answer

An Agent Card is a JSON metadata document that describes an agent's capabilities, supported task types, authentication requirements, and endpoint URL. It serves as a discovery mechanism, allowing other agents to find and understand what a peer agent can do before delegating tasks.

Exercises

Exercise 21.3.1: A2A Protocol Basics Conceptual

Describe the key components of Google's Agent-to-Agent (A2A) protocol. How does an A2A Agent Card enable discovery, and what information does it contain?

Answer Sketch

An Agent Card is a JSON document (served at /.well-known/agent.json) that describes the agent's capabilities, supported input/output types, and endpoint URL. It enables discovery by allowing other agents to fetch the card, understand what the agent can do, and decide whether to delegate tasks to it. It contains fields like name, description, skills, and authentication requirements.

Exercise 21.3.2: Task Lifecycle States Conceptual

Draw (or describe) the state transitions in an A2A task lifecycle. What states can a task be in, and what events trigger transitions between them?

Answer Sketch

States: submitted, working, input-required, completed, failed, canceled. Transitions: submitted to working (agent starts processing), working to input-required (agent needs more info from the caller), input-required to working (caller provides additional input), working to completed (task done), working to failed (unrecoverable error), any state to canceled (caller cancels).

Exercise 21.3.3: A2A vs. MCP Conceptual

Compare A2A and MCP. What problem does each protocol solve? Can they be used together, and if so, how?

Answer Sketch

MCP connects LLM applications to tools and data sources (model-to-tool). A2A connects agents to other agents (agent-to-agent). They are complementary: an agent might use MCP to access tools locally and A2A to delegate subtasks to specialized remote agents. An A2A agent could internally use MCP servers for its tool access.

Exercise 21.3.4: Federation Design Coding

Design a simple agent registry service in Python that stores Agent Cards and supports discovery queries. Implement register(card), search(skill_query), and get(agent_id) methods.

Answer Sketch

Use a dictionary keyed by agent_id. For search, iterate over stored cards and match the skill_query against each card's skills list using substring matching or embedding similarity. Return a ranked list of matching agents. In production, this would use a database with full-text search or vector similarity.

Exercise 21.3.5: A2A Error Handling Conceptual

A task delegated via A2A fails because the remote agent encounters an error. Describe how the calling agent should handle this failure, considering the task lifecycle states.

Answer Sketch

The remote agent transitions the task to 'failed' state with an error message. The calling agent receives this status update and should: (1) log the failure with the remote agent's error details, (2) decide whether to retry with the same agent, (3) search for an alternative agent with the same skill, or (4) escalate to a human. The A2A protocol's task history preserves the full interaction for debugging.

What Comes Next

In the next section, Custom Tool Design, we cover how to build robust, production-quality tools with proper validation, error handling, and security boundaries.

Further Reading
Google (2025). "Agent-to-Agent Protocol (A2A) Specification." google.github.io/A2A. The official A2A specification defining how AI agents discover capabilities, negotiate tasks, and collaborate across organizational boundaries using Agent Cards and task lifecycle management.
Google (2025). "A2A: A New Era of Agent Interoperability." Google Developers Blog. The launch announcement explaining how A2A complements MCP by enabling agent-to-agent collaboration rather than agent-to-tool connections.
Li, G., Hammoud, H., Itani, H., et al. (2023). "CAMEL: Communicative Agents for 'Mind' Exploration of Large Language Model Society." NeurIPS 2023. Early work on role-playing communication between AI agents, demonstrating how structured protocols enable cooperative task completion between autonomous agents.
Hong, S., Zhuge, M., Chen, J., et al. (2024). "MetaGPT: Meta Programming for a Multi-Agent Collaborative Framework." ICLR 2024. Introduces structured communication protocols between software engineering agents using standardized operating procedures, demonstrating how message formats shape multi-agent collaboration quality.
FIPA (2002). "FIPA ACL Message Structure Specification." Foundation for Intelligent Physical Agents. The classic agent communication language specification that influenced modern protocols like A2A, establishing foundational concepts of performatives, message structure, and interaction protocols.