Перейти к основному содержимому

A2A: The Agent-to-Agent Protocol

Продвинутый

By mid-2026, most non-trivial agent work is done by several agents cooperating — a research agent hands off to a writer, a billing agent talks to a support agent from a different vendor, an orchestrator delegates to a specialist inside another org's cloud. A2A (Agent-to-Agent) is the open protocol that lets those agents find each other, describe what they can do, and hand tasks back and forth — even when they were built on different frameworks by different teams. Where MCP connects an agent to its tools and data, A2A connects an agent to another agent. They compose; they don't compete.

What you'll learn
  • State exactly what A2A is for — and where the MCP boundary sits
  • Read an Agent Card and know what a receiving agent will do with it
  • Walk a task through all eight lifecycle states, including the two that are interrupted rather than terminal
  • Choose between SSE streaming and webhook push for long-running tasks
  • Recognize the non-obvious features: signed cards, multi-tenant endpoints, and structured data parts

Why A2A exists at all

Every agent framework — LangGraph, CrewAI, the Claude Agent SDK, the OpenAI Agents SDK, Microsoft's Agent Framework, custom in-house loops — solved tool use by leaning on the model's native tool-calling and, increasingly, on MCP as the universal tool-and-data connector. That gave us agent ↔ tool.

What none of them solved on their own was agent ↔ agent across a boundary. Two problems kept coming up:

  • Discovery. How does agent A know that agent B exists, what it can do, how to authenticate against it, and whether it supports streaming? A hand-rolled README is not a protocol.
  • Task hand-off. Once A wants B to do something, how do they exchange the work (not the model's chat) — including files, structured data, progress updates, and the fact that B might need to pause and ask A for input?

Framework-specific answers (LangGraph subgraphs, CrewAI crews, subagents inside one runtime) work great inside one process. They stop at the org boundary. A2A is the piece that lets an agent in your product delegate to an agent in someone else's product without either side leaking its internals — Google's Todd Segal calls it "the secure foundation for personal, team, and domain-specific agents to work together seamlessly across any platform." Governance sits with the Linux Foundation, and by April 2026 the project reported 150+ supporting organizations including AWS, Microsoft, Salesforce, SAP, ServiceNow, and IBM.

MCP vs. A2A — the mental model

Both are open protocols. Both use JSON over HTTP. They solve different problems and are designed to compose:

  • MCP = agent-to-tool. An MCP server exposes tools (search, read_file, run_query) and resources (docs, prompts) to a model-driven client. The client is an agent; the server is a passive capability provider. See MCP & connecting to tools for how Claude uses it.
  • A2A = agent-to-agent. An A2A endpoint exposes an agent — an entity with its own reasoning loop that will decide how to solve a delegated task. Both sides are agents; either can call the other.

The concrete difference shows up in the wire format. An MCP tools/call returns a result and closes. An A2A SendMessage opens a task — a stateful thing with its own lifecycle that can stream updates, ask for clarification, or run for hours in the background. If your endpoint's answer is always "here's the result of one function," it's a tool — publish it via MCP. If it's "let me think about that and get back to you, and I might need to ask a follow-up question," it's an agent — publish it via A2A.

Most serious systems will run both: an agent whose inside uses MCP to reach tools, and whose outside speaks A2A so peers can hand it work.

The Agent Card — the piece of the spec that matters most

An Agent Card is a JSON document served at a well-known URL that tells any potential caller everything they need to talk to your agent. Think OpenAPI + robots.txt, for agents. Every A2A interaction starts by fetching one.

The card includes:

  • Identityid, name, description, provider (org details).
  • Endpoints — the service URLs and which protocol bindings (JSON-RPC, gRPC, REST) are available.
  • Capabilities — feature flags: streaming, pushNotifications, extendedAgentCard.
  • Skills — declared agent functions with input/output schemas (this is what B says it can do).
  • Security schemes — one or more of APIKey, HTTPAuth (Basic/Bearer), OAuth2 (Authorization Code, Client Credentials, Device Code), OpenIdConnect, MutualTLS. The caller reads this to know what credentials to bring before the first call.
  • Signature — an optional cryptographic signature over the card.

That last field is the one worth pausing on. A Signed Agent Card lets a receiving agent verify that the card was actually issued by the domain owner — the DNS/PKI equivalent of "yes, this really is agents.acme.com, not something an attacker planted." Combined with mTLS auth, it closes the door on rogue Agent Cards being served from spoofed hosts, which is how you'd otherwise get a prompt injection at the discovery layer: an attacker's Agent Card lying about what tools it exposes and what data it wants.

The eight task lifecycle states

Once you SendMessage to an A2A agent, the work becomes a Task — a first-class object with an ID you can poll, subscribe to, cancel, or list. The task moves through eight states, and it's worth noticing that only five are terminal:

  • SUBMITTED — the server acknowledged the task.
  • WORKING — active processing.
  • INPUT_REQUIREDinterrupted: the agent needs more information from the caller. Not an error; expected in real workflows.
  • AUTH_REQUIREDinterrupted: the agent needs the caller to authenticate (or re-authenticate) before it can continue.
  • COMPLETED — success. Terminal.
  • FAILED — error. Terminal.
  • CANCELED — caller-initiated cancellation. Terminal.
  • REJECTED — the agent declined the task (policy, capability, quota). Terminal.

The interrupted states are the point. Legacy RPC assumes one call → one result. Real agent work looks like "start the analysis, come back an hour later, notice I need to ask a clarifying question, wait for the answer, resume, finish." A2A models that natively. Your calling code has to be a small state machine, not a synchronous await.

Streaming vs. push notifications

Long-running tasks need a way to deliver updates. A2A gives you two async models — pick per task, not per agent:

  • Streaming (SendStreamingMessage / SubscribeToTask). Server-Sent Events over the open HTTP connection. Client stays connected, receives TaskStatusUpdateEvent and TaskArtifactUpdateEvent in order. Great for interactive UIs and short-to-medium tasks. Falls over if the client can't hold a connection (mobile, serverless, browser tabs going to sleep).
  • Push notifications (webhook config). The client registers a webhook via CreateTaskPushNotificationConfig. The server POSTs updates to that URL as they happen. The client can be totally offline in between. This is the mode for tasks that outlive an HTTP connection — think "run this overnight" or "call me back when the batch job finishes."

Both require the agent to advertise support in its Agent Card (capabilities.streaming: true and/or capabilities.pushNotifications: true). A conforming caller checks first and downgrades cleanly.

Message parts — not just chat

An A2A Message is composed of one or more Parts. Each Part can be one of:

  • text — string content (the chat-y part).
  • raw — binary file, base64-encoded in JSON.
  • url — a reference to an external file (avoids inlining huge blobs).
  • data — a structured JSON object or array, with an optional metadata map.

The data part is what makes A2A useful for machine-to-machine work. Two agents can exchange typed payloads — an order, a purchase spec, a JSON diff — without pretending to have a natural-language conversation about it. This is also what makes protocols like Agent Payments (AP2) layer cleanly on top of A2A: the payment intent rides in a data part with a known schema.

Multi-tenant endpoints — one URL, many agents

This one is easy to miss. A single A2A endpoint can host many agents, served through the GetExtendedAgentCard method after authentication. A SaaS vendor can offer one URL and return different, tenant-specific Agent Cards depending on which API key or OAuth scope the caller presents. From the caller's side it looks like one endpoint per agent; from the vendor's side it's one deployment. If you're building agent-serving infrastructure, this is the pattern that lets you scale without one hostname per tenant.

A minimal end-to-end interaction

The full protocol has a lot of surface, but the happy-path flow is short:

Guided walkthrough1 of 5
  1. Fetch the Agent Card from a well-known URL. Verify its signature if one is present. Read `capabilities`, `skills`, and `securitySchemes` to decide whether this agent can do what you need and what auth you'll bring.

Fetching an Agent Card (concept)

Discovery is just an HTTP GET. This is the shape of a request a client would make to inspect a candidate agent before ever sending it work:

Fetch and inspect an Agent Card

GET https://agents.example.com/.well-known/agent.json
Accept: application/json

# Response (abbreviated):
# {
#   "id": "acme/support-router",
#   "name": "Acme Support Router",
#   "provider": {"organization": "Acme, Inc."},
#   "endpoints": [
#     {"url": "https://agents.example.com/a2a", "protocol": "jsonrpc"}
#   ],
#   "capabilities": {
#     "streaming": true,
#     "pushNotifications": true,
#     "extendedAgentCard": true
#   },
#   "skills": [
#     {"id": "route_ticket", "inputSchema": {...}, "outputSchema": {...}}
#   ],
#   "securitySchemes": {
#     "primary": {"type": "oauth2", "flows": {...}}
#   },
#   "signature": {"alg": "EdDSA", "value": "..."}
# }

Note what's not in the card: nothing about the model behind the agent, its prompt, its tools, or its private state. That opacity is deliberate — A2A treats a peer agent as a black box you delegate work to, not a system you introspect.

Common gotchas

  • Treating INPUT_REQUIRED as an error. It isn't; it's the protocol asking your code to respond. If your caller code only branches on "did it succeed or fail," you'll break every interactive workflow.
  • Ignoring the Agent Card's capabilities. Sending a streaming request to an agent that didn't advertise streaming: true gets you an UnsupportedOperationError. Read the card, downgrade cleanly.
  • Assuming the streaming connection is durable. Mobile clients, serverless runtimes, browser tabs — they all drop the SSE connection. For anything longer than "a chat turn," prefer push notifications.
  • Skipping signature verification because it's optional. If you're consuming Agent Cards from outside your org, verify the signature — otherwise you've created a discovery-layer prompt injection vector.
  • Confusing A2A with MCP. Publishing an A2A endpoint for a stateless single-shot function is overkill; that's a tool, and MCP is the right protocol. Publishing an MCP tool for something that takes minutes and needs to ask clarifying questions is under-engineering; that's an agent, and A2A is the right protocol.

Check yourself

0/4
  1. Which of these is A2A's job — the thing that MCP is *not* designed for?
  2. A2A defines eight task states. Which two are *interrupted* rather than terminal?
  3. You need to run an A2A task that will take an hour. The client is a serverless function that can't hold an open HTTP connection. What's the right delivery model?
  4. What does a *Signed* Agent Card protect you from?

Where A2A sits in your stack

Put together with the rest of the agent picture:

  • Inside an agent, MCP is how you reach tools and data — including remote MCP servers you didn't write.
  • Between agents in one framework, you use whatever the framework gives you — subagents in Claude Code, LangGraph subgraphs, CrewAI crews, etc. This is fastest but locks you to one runtime.
  • Between agents across frameworks, teams, or vendors, A2A is what you reach for. Read Open-Source AI Agent Frameworks for the runtime side; A2A is the wire between those runtimes.

The rule of thumb: if the boundary is a process boundary, framework-native handoffs are fine. If it's an org, cloud, or trust boundary, use A2A.

Sources & further reading