Skip to content
All posts
a2aai-agentsprotocolsmcpplatform-engineering

The A2A Protocol, Sixteen Months In: What Agent-to-Agent Interop Actually Looks Like

A practitioner's reading of the Agent2Agent protocol as of August 2026: where it came from, what v1.0 changed, the implementation patterns that matter, and the trust problems nobody has solved yet.

Every few months someone asks me whether they should adopt A2A, and the honest answer keeps being: it depends on whether your agents need to talk to other people's agents. That is a narrower question than the press releases suggest, and a more interesting one than the skeptics admit.

This post is my attempt to write down the state of the Agent2Agent protocol as it actually stands in August 2026: where it came from, what the v1.0 specification changed, how the pieces fit together in code, and where the open problems sit. Everything versioned below was checked against primary sources this week; the links are at the end.

Where A2A came from

The timing only makes sense next to MCP. Anthropic open-sourced the Model Context Protocol in November 2024, and it spread through 2025 at a pace nobody planned for: by the time the Linux Foundation announced the Agentic AI Foundation in December 2025, MCP had over 10,000 published servers and first-party support from every major model vendor. MCP standardized how an agent reaches its tools and data. It said nothing about how two agents, built by different teams on different frameworks, talk to each other.

Google announced A2A on April 9, 2025, with more than 50 launch partners: Atlassian, Box, Cohere, Intuit, LangChain, MongoDB, PayPal, Salesforce, SAP, ServiceNow, UKG, and Workday on the technology side, plus the large consultancies (Accenture, Deloitte, McKinsey, TCS, and friends). The announcement was explicit that A2A complements MCP rather than competing with it, which was the right positioning and also the only survivable one.

Two months later, on June 23, 2025, Google donated the protocol to the Linux Foundation, by which point the supporter count had already passed 100. The technical steering committee today includes AWS, Cisco, Google, IBM Research, Microsoft, Salesforce, SAP, and ServiceNow. Whatever you think of foundation governance, the practical effect is that no single vendor can take the spec home.

The normative spec now lives at a2a-protocol.org, with a single a2a.proto file as the authoritative definition of every data object. That last choice matters more than it looks: pinning the data model to one proto file is what keeps the JSON-RPC, gRPC, and REST bindings from drifting apart.

Adoption, as of this week

Here is what I verified while writing this:

  • The current specification is 1.0.0, the first stable release. The v1.0 announcement landed in spring 2026, ahead of the protocol's first anniversary. Earlier 0.x drafts are superseded, though the SDKs keep a 0.3 compatibility mode.
  • The Python SDK, a2a-sdk on PyPI, is at 1.1.2, released July 22, 2026; 1.0.0 shipped on April 20, 2026, followed by monthly minor releases. Official SDKs also exist for JavaScript, Java, Go, .NET, and Rust.
  • All three major clouds embed it: Microsoft in Azure AI Foundry and Copilot Studio, AWS through Bedrock AgentCore Runtime, and Google in Vertex AI. Oracle went a step further in July: Autonomous AI Database now ships a managed A2A server that exposes in-database agent teams over the standard protocol.
  • The Linux Foundation's one-year milestone release (April 9, 2026) claims 150+ supporting organizations, 22,000+ GitHub stars, and production deployments in supply chain, financial services, insurance, and IT operations. The Agent Payments Protocol (AP2), a sibling spec for agent-initiated transactions, has its own specification site and 60+ backers.

One honest caveat. The supporter counts measure signatures, not deployments. A mid-2026 adoption analysis put it well: the tooling is production-ready, but evidence of production-proven use at scale, outside early enterprise adopters, is still thin. Teams that only need agent-to-tool connectivity keep choosing MCP because it works in minutes. A2A earns its keep in a specific situation: agents crossing framework, team, or organizational boundaries. If that is not your situation, the rest of this post is optional reading.

The problems A2A actually solves

The NxM integration problem. Without a shared protocol, N agents that need to delegate work to M other agents produce N times M bespoke integrations, each with its own auth handshake, payload shape, and failure semantics. This is the same argument that sold HTTP APIs and later MCP. A2A applies it one layer up: one task envelope, one lifecycle, one discovery document.

Opaque agents, not tools. MCP models the world as an agent calling a tool with a known schema. That breaks when the callee is itself an agent with its own planning loop, memory, and tools, owned by a team (or company) that will never show you its internals. A2A's core design principle is opacity: agents collaborate on declared capabilities and exchanged messages, without sharing internal state. Your vendor's support agent is a service you delegate to, not a function you call. This sounds like a philosophical distinction until you try to fit a 40-minute research agent into a synchronous tool-call contract.

Capability discovery. Every A2A server publishes an Agent Card, a JSON document describing identity, endpoint, skills, and authentication requirements. Public agents host it at /.well-known/agent-card.json per RFC 8615; enterprises typically use curated registries or direct configuration instead. The card is how a client agent decides whether a remote agent can do the job before sending anything.

Long-running work as a first-class object. The Task is the protocol's unit of work, with an explicit lifecycle: submitted, working, input-required, auth-required, and the terminal states completed, failed, canceled, rejected. A task can run for hours, pause to ask a human for input, and stream progress the whole time. If you have ever tried to stretch a REST request/response contract over a human-in-the-loop approval flow, you know why this exists.

A2A task lifecycle: submitted flows into working, with loops through input-required and auth-required, and terminal states completed, failed, canceled. Long-running agents stream status over SSE or push to a webhook.

Implementation patterns

Publishing an Agent Card

The card is the contract. A minimal v1.0 card looks like this:

{
  "name": "Invoice Agent",
  "description": "Accounts payable lookup for approved partners",
  "url": "https://agents.example.com/invoices",
  "version": "1.0.0",
  "capabilities": { "streaming": true, "pushNotifications": true },
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"],
  "skills": [
    {
      "id": "invoice_lookup",
      "name": "Invoice lookup",
      "description": "Answers questions about invoice status",
      "tags": ["finance", "invoices"],
      "examples": ["Has invoice 4417 been paid?"]
    }
  ]
}

Serve it from the well-known URI if the agent is meant to be discovered, and put real cache headers on it: the discovery guidance recommends Cache-Control plus an ETag derived from the card's version field, so clients can revalidate cheaply when the card changes. Cards evolved in a backward-compatible way across the 1.0 transition, which was a deliberate choice: the interaction protocol took breaking changes, the discovery document did not.

Sending a task

The JSON-RPC binding is the default. A message/send call creates a task and, by default, blocks until the task reaches a terminal or interrupted state:

{
  "jsonrpc": "2.0",
  "id": "req-7",
  "method": "message/send",
  "params": {
    "message": {
      "messageId": "m-1024",
      "role": "user",
      "contextId": "ctx-42",
      "parts": [{ "kind": "text", "text": "Has invoice 4417 been paid?" }]
    },
    "configuration": { "returnImmediately": false }
  }
}

Three delivery patterns matter in practice:

  1. Blocking request/response. Default behavior, right for tasks measured in seconds. Set returnImmediately: true to get the task handle back at once and poll tasks/get instead.
  2. SSE streaming. message/stream opens a Server-Sent Events stream of TaskStatusUpdateEvent and TaskArtifactUpdateEvent objects, closing when the task terminates. Right for interactive UIs and progress display.
  3. Push notifications. The client registers a webhook per task; the agent POSTs updates to it. Right for server-to-server work measured in minutes to days, where holding a connection open is operations debt. The client must be publicly reachable, which is the usual catch with webhooks.

contextId groups related tasks into one conversation; send follow-up messages with the same contextId (and taskId when continuing a specific task) and the remote agent maintains continuity. Multi-turn delegation falls out of this naturally, including the input-required round trip where the remote agent comes back with a clarifying question instead of an answer.

Messages and artifacts are deliberately separate. Messages are conversation: instructions, clarifications, status notes. Artifacts are outputs: the report, the file, the structured result. The spec is blunt about messages not being a reliable delivery mechanism for anything important; results belong in artifacts on the task. Build to that and reconnects stop being a data-loss scenario.

Exposing an existing agent

You rarely write an A2A agent from scratch. You wrap one. With a2a-sdk 1.1.x, the server side is an executor around your existing agent, a request handler, and a card:

from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.tasks import InMemoryTaskStore
from a2a.server.apps import A2AFastAPIApplication
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
from a2a.utils import new_agent_text_message

class InvoiceExecutor(AgentExecutor):
    async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
        # Call your existing agent with context.get_user_input(),
        # then publish results as they happen.
        await event_queue.enqueue_event(
            new_agent_text_message("Invoice 4417 was paid on 2026-07-30.")
        )

    async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
        raise NotImplementedError

card = AgentCard(
    name="Invoice Agent",
    description="Accounts payable lookup for approved partners",
    url="https://agents.example.com/invoices",
    version="1.0.0",
    default_input_modes=["text/plain"],
    default_output_modes=["text/plain"],
    capabilities=AgentCapabilities(streaming=True),
    skills=[AgentSkill(
        id="invoice_lookup",
        name="Invoice lookup",
        description="Answers questions about invoice status",
        tags=["finance", "invoices"],
        examples=["Has invoice 4417 been paid?"],
    )],
)

handler = DefaultRequestHandler(
    agent_executor=InvoiceExecutor(),
    task_store=InMemoryTaskStore(),  # swap for a durable store in production
)
app = A2AFastAPIApplication(agent_card=card, http_handler=handler).build()

Two production notes on that sketch. First, InMemoryTaskStore is for demos; a task that outlives the process needs a persistent store, which the SDK supports via SQL backends. Second, if the work inside the executor is itself long-running with retries and human pauses, you want durable execution underneath, and the mapping is direct: the A2A task maps to a workflow, tool calls map to activities. I wrote up that pattern in Temporal best practices.

Putting a gateway in front

Once more than a couple of agents are exposed, you want the same thing you wanted for REST a decade ago: a gateway that terminates auth, applies policy, and routes. Agent gateways treat A2A as a first-class protocol next to MCP and LLM traffic; the agentgateway docs show an AgentgatewayBackend of type a2a with path-prefix routing in front of the agent's own endpoint. I run exactly this pattern and wrote it up as a case study. The practical wins: one place for authentication policy, one place for rate limits, and a clean boundary between the agent's public card URL and where the workload actually runs.

Pitfalls and open problems

Trust is only half-built. v1.0 added signed Agent Cards, which let a client verify a card was issued by the domain it claims. That closes card forgery. It does not answer the harder question: whether you trust the agent behind a genuine card with your data, your money, or your users. Cross-organizational delegation policy, who is allowed to ask whom to do what, is still something you build yourself, in allowlists and contracts. AP2 is the community's bet for the payments slice of this; the general case is open.

Authn and authz across the boundary. A2A deliberately defers to standard web security (OAuth2, mTLS, bearer tokens, OpenAPI-style schemes declared in the card). That is the right call, and it means you inherit all the usual federation pain. The spec does require that task listings return only tasks visible to the authenticated client, and v1.0 added an explicit tenant field so one endpoint can host many agents safely. Use it. Multi-tenant agent endpoints without tenant scoping are the kind of mistake that ends in an incident review.

Webhooks are your problem. Push notifications invert the connection: the remote agent calls you. That means a publicly reachable endpoint, signature verification on inbound calls, replay protection, and retry handling, none of which the protocol can do for you.

Versioning needs discipline. Clients send an A2A-Version header (major.minor only; patch versions are not negotiated), and servers must honor the requested version or return VersionNotSupportedError. The 0.3 compatibility mode in the SDKs smooths the transition, but treat it as a migration bridge, not a target state.

Discovery is not standardized above the card. The well-known URI is specified. Registries are not: the spec explicitly leaves registry APIs to implementations. If you are betting on cross-company discovery, watch this space, because today it is direct configuration and private catalogs all the way down.

When A2A is overkill. A single agent with tools needs MCP, not A2A. A multi-agent system inside one trust boundary, built on one framework, usually wants the framework's native handoff primitives, which are lighter than a network protocol. A2A starts paying for itself when the other agent is someone else's: another team's, another vendor's, another company's. Below that threshold you are buying federation machinery you will never use.

Where this leaves us

Sixteen months in, A2A has the properties I look for in infrastructure: a stable 1.0 spec, boring transports (HTTP, JSON-RPC, SSE), SDKs in six languages, neutral governance, and a clear boundary against MCP, which it complements rather than replaces. What it does not yet have is a deep bench of public production war stories, and the trust layer between organizations is still mostly homework. If your architecture has agents crossing organizational boundaries, A2A is the only serious open standard for the job, and v1.0 is a reasonable foundation to build on. If it does not, file the protocol away and revisit when it does.

Sources