Articles

A2A Protocol Explained With a Python Example and a Traced Handoff

Learn the a2a protocol at v1.0: Agent Cards, task states, auth schemes, and a runnable Python handoff between two agents, traced end to end in one trace.

· 16 min read
a2a-protocol agent-card a2a-authentication a2a-python multi-agent-systems llm-tracing opentelemetry
Two AI agents handing off an order lookup over the A2A protocol, with both sides joined in one trace
Table of Contents

Your support agent takes a customer question about order A-1001 and hands the lookup to an Order Status Agent that another team runs. The reply comes back marked completed. A week later a customer gets the wrong status, and you have two services, two log streams and no single place that shows the request crossing between them.

The a2a protocol (Agent2Agent) exists to make that handoff predictable. It gives one agent a standard way to find another, send it a unit of work, and read back a result whose state both sides agree on.

That boundary shows up as soon as multi-agent systems in production are owned by more than one team.

This guide covers three things. First, what A2A is at v1.0, with the Agent Card, task model and authentication checked against the current spec. Second, a runnable Python handoff between two agents on the official SDK. Third, that same handoff captured as one OpenTelemetry trace, with real output from our run.

TL;DR

  • A2A is an open protocol for agent-to-agent work: a client agent reads a remote agent’s Agent Card, sends a Message, and tracks a Task until it reaches a terminal or interrupted state.
  • Version 1.0 uses one unified Part type, eight task states and three core bindings: JSON-RPC, gRPC and HTTP+JSON.
  • Authentication is declared in the Agent Card’s securitySchemes: API key, HTTP auth, OAuth 2.0, OpenID Connect or mutual TLS.
  • The spec reached v1.0.0 on March 12, 2026, has official SDKs in six languages, and joined the Agentic AI Foundation in August 2026.
  • The Python example runs two agents on a2a-sdk 1.3.0, and traceAI-a2a puts the client span and the server span in one trace.

What the A2A Protocol Does Between Two Agents

The A2A protocol is an open standard for one AI agent to discover another, send it work as a task, and get results back, without either side sharing its internal plans, thoughts or tool implementations. It defines the Agent Card for discovery and a shared task model that runs over JSON-RPC, gRPC or HTTP+JSON.

The A2A specification names the two sides the A2A Client and the A2A Server, also called the Remote Agent.

The client is any application or agent that starts a request on behalf of a user or another system. The remote agent exposes an A2A endpoint, processes tasks and returns responses. In our scenario, the support agent is the client and the Order Status Agent is the remote agent, each run by its own team.

In practice, the client sees only a card, a task, its state and its outputs. The two agents collaborate as black boxes, so either team can swap models, frameworks or tools behind its endpoint without breaking the contract the other side depends on.

Google launched the protocol in April 2025, and the Linux Foundation took it on as an open project two months later. The Linux Foundation launch announcement came on June 23, 2025, at Open Source Summit North America in Denver, with support from more than 100 technology companies at the time.

Where A2A Sits Next to MCP

MCP and A2A work at different layers. The Model Context Protocol connects an agent to the tools, data and prompts it calls. A2A connects one agent to another agent that does its own reasoning and returns a result. A support agent can use MCP to read the order database and A2A to hand the shipping question to another team’s agent.

For the full side-by-side, read our MCP vs A2A comparison.

Tasks, Messages, Parts and Artifacts in the A2A Protocol

A typical A2A exchange is built from five objects plus an optional context ID. A client sends a Message made of Parts, the remote agent tracks the work as a Task with a lifecycle state, and the result comes back as an Artifact. The Agent Card, covered in the next section, tells the client where to send it and how.

The table below paraphrases the core concepts in section 2.2 of the v1.0 specification. Keep it open when you read your first raw A2A response, because most fields you see there map back to one of these six terms.

ConceptOne-line definition (paraphrased from the spec)
Agent CardJSON metadata a remote agent publishes: identity, capabilities, skills, service endpoint and auth requirements
TaskThe unit of work, with a unique ID, that moves through a defined lifecycle
MessageOne communication turn with a role (“user” or “agent”) holding one or more Parts
PartThe smallest unit of content: text, a file as raw bytes or a URL, or structured data, with an optional media type
ArtifactAn output the agent produces for a task, made of Parts
ContextAn optional ID that groups related tasks and messages

The Eight A2A Task States

A task in v1.0 moves through eight states: submitted, working, input-required, auth-required, completed, failed, canceled and rejected. The protobuf definition gives each a TASK_STATE_ prefix, so completed arrives as TASK_STATE_COMPLETED. Four states are terminal (completed, failed, canceled and rejected), and a task in any of them accepts no further messages.

Two states pause the task instead of ending it. Input-required means the agent needs more from the user, and auth-required means it needs credentials. A blocking SendMessage call waits until the task reaches a terminal state or an interrupted one, so a client has to handle the two interrupted states as well as completed.

The life of a task guide walks through these transitions.

Artifacts Carry the Result

Section 3.7 of the spec draws a firm line between Messages and Artifacts. Messages start tasks, ask for clarification and report progress. Results should come back as Artifacts attached to the task, and the spec says messages must not be treated as a reliable delivery mechanism for critical information.

That rule shapes our example. The Order Status Agent writes its answer as an artifact on the task and then marks the task completed. The client reads the artifact, so the answer survives even if a status message sent along the way never reaches the client at all.

One Part Type in v1.0

Version 1.0 replaced the separate TextPart, FilePart and DataPart types with one Part that holds text, raw bytes, a URL or structured data, plus optional filename and media type fields. Samples written for 0.x versions of the SDKs can break on exactly this change, so check the SDK version before you copy one.

How an A2A Agent Card Advertises Skills, Endpoints and Auth

An Agent Card is a JSON document a remote agent serves at /.well-known/agent-card.json. It names the agent, lists its skills and its input and output modes, declares which protocol bindings it speaks at which URL, and states the authentication a client must present before it sends the agent a task.

The Order Status Agent card from the example, showing its eight required fields with notes on capabilities, supported interfaces, skills and the optional fields it leaves unset

The protobuf AgentCard marks eight fields as required: name, description, supported_interfaces, version, capabilities, default_input_modes, default_output_modes and skills. Optional fields include provider, documentation_url, security_schemes, security_requirements, signatures and icon_url. The JSON form uses camelCase, so these appear as supportedInterfaces, securitySchemes and so on.

Each supportedInterfaces entry carries a url, a protocolBinding and a protocolVersion, plus an optional tenant for servers that host several agents behind one endpoint. The core bindings are JSONRPC, GRPC and HTTP+JSON. The list is ordered, and the first entry is the interface the agent prefers clients to use.

The capabilities object holds four fields: streaming, pushNotifications, extensions and extendedAgentCard. The last one tells clients that an authenticated caller can fetch a richer card through the GetExtendedAgentCard operation, so a public card can stay minimal while signed-in partners see more of what the agent offers.

The spec also lets an agent sign its card. Signatures use JSON Web Signature, and the card is canonicalized with the JSON Canonicalization Scheme from RFC 8785 before signing. A client that checks the signature knows nobody altered the card between the agent that signed it and the client reading it.

Older guides often get the path wrong. Spec versions before 0.3.0 served the card at /.well-known/agent.json. From v0.3.0 onward, including v1.0, the path is /.well-known/agent-card.json, a permanent IANA well-known URI whose registration template sits in section 14 of the spec. Check which path your SDK and your peers expect.

The agent discovery guide covers well-known URIs, curated registries and direct configuration. Our example’s card appears in the remote agent code below.

A2A Authentication Through Agent Card securitySchemes

In A2A, the remote agent declares authentication up front. It lists accepted schemes in the Agent Card’s securitySchemes field, the client obtains credentials out of band, then sends them in headers or metadata on every request. The server must authenticate each request, as sections 7.3 and 7.4 of the spec require.

The scheme types come from the SecurityScheme message in the protobuf, which is based on the OpenAPI 3.2 Security Scheme Object. A card can list several schemes, and securityRequirements says which ones a client must satisfy. The third column below is our own guidance on where each scheme fits, not spec text.

Scheme typeWhat the card declaresTypical fit (our guidance)
API keyKey name and location (header, query or cookie)Internal or partner agents with a shared key
HTTP authHTTP scheme such as Bearer, with an optional bearer format such as JWTAgents behind an existing bearer-token setup
OAuth 2.0Flows (authorization code with optional pkceRequired, client credentials, device code), scopes, optional metadata URLDelegated access across org boundaries
OpenID ConnectOIDC discovery URLTeams already on an OIDC identity provider
Mutual TLSA description only; identity comes from client certificatesService-to-service links where both sides hold certs

Whatever scheme you pick, section 7.1 requires encrypted transport in production: HTTPS for the HTTP-based bindings and TLS for gRPC, with TLS 1.3 or later recommended. Clients should also validate the server’s TLS certificate, so the card your client trusts really came from the agent it names.

OAuth Changes in A2A v1.0

Version 1.0 added the OAuth 2.0 Device Code flow from RFC 8628, aimed at command-line tools and input-constrained devices, and a pkceRequired flag (pkce_required in the protobuf) on the Authorization Code flow. The Implicit and Password flows are now deprecated, and the spec points teams to Authorization Code with PKCE, or Device Code in place of Password.

The what changed in v1.0 page lists these OAuth changes, with the two old flows under removed flows, alongside the rest of the migration notes.

When a Task Needs Credentials Mid-Flight

Sometimes the remote agent only learns it needs credentials partway through a task, for example to call another API on the customer’s behalf. It can move the task to TASK_STATE_AUTH_REQUIRED and hand the request back to the client. If that client is itself an agent working on a task, it can pass the request up its own chain.

Section 7.6 says agents must arrange to receive these credentials out of band unless both sides negotiated an in-band mechanism, and should receive them over a secure channel such as HTTPS. In-band exchange through a chain of agents exposes the credentials to every agent in that chain, so negotiate it explicitly before you rely on it.

Is the A2A Protocol Production Ready in 2026?

Yes. The A2A protocol shipped a stable v1.0.0 specification on March 12, 2026, followed by v1.0.1 in May 2026, with official SDKs in six languages. It moved under the Agentic AI Foundation in August 2026 and keeps a regular SDK release cadence, with new Python and Go releases in late October 2026.

The A2A releases page shows v1.0.0 published on March 12, 2026 and v1.0.1, a bug-fix release, on May 28, 2026. The v1.0.1 changelog entry is dated May 26.

The official SDKs cover Python, Go, Java, JavaScript, C#/.NET and Rust.

Release activity is steady. As of 2026-11-01, the Python SDK (a2a-python) is at v1.3.0, released October 27, 2026, and the Go SDK is at v2.7.0, released October 21, 2026. Our example pins a2a-sdk 1.3.0, so the code below matches the newest Python release on PyPI.

On governance, A2A joined the Agentic AI Foundation as a Growth Stage project in August 2026.

The project’s announcement says the protocol is backed by over 150 organizations. That answers the recurring question of whether A2A is dead. Google’s April 2025 launch post promised a production-ready version later that year, and the stable spec, neutral governance and active SDKs are now all in place.

Why an A2A Handoff Needs One Trace

An A2A handoff crosses a network and often a team boundary, so by default the client agent and the remote agent write separate traces. Propagating W3C trace context in the request headers lets the remote agent’s server span join the caller’s trace, so one view shows the whole handoff from start to finish.

Without propagation you get two trace IDs and no parent link between them. The client’s trace shows a call that took some time and came back completed. The server’s trace shows a request arriving from somewhere. When the answer is wrong or slow, nothing ties the two together except timestamps and guesswork.

The fix is a standard header. The client injects a traceparent header defined by W3C Trace Context, and the server extracts it.

That header carries the client’s trace ID and its current span ID. The server starts its own span as a child of the client’s span, under the same trace ID. Both sides must take part: a client that injects without a server that extracts still produces two separate traces.

Our guide to tracing and debugging multi-agent systems covers span hierarchy across agents in more depth.

A2A Protocol Example in Python With Two Agents

This a2a protocol example runs two local Python agents with the official a2a-sdk 1.3.0. A remote Order Status Agent publishes an Agent Card and answers order lookups. A client agent fetches the card, sends one message, and prints the completed task with its artifact, so you can see the full round trip.

Both agents use the a2a-python SDK, the official Python implementation.

Setup

We ran this on Python 3.12.5 in a clean virtual environment on 2026-11-01. The remote agent answers from a small dictionary, so the example needs no model API key. Install the SDK with its HTTP server extra, the traceAI A2A package, and uvicorn to serve the remote agent locally.

pip install "a2a-sdk[http-server]==1.3.0" "traceAI-a2a==0.1.1" uvicorn

The resolved versions in our clean run were a2a-sdk 1.3.0, traceAI-a2a 0.1.1, fi-instrumentation-otel 1.2.2, opentelemetry-sdk 1.45.2, starlette 1.10.0, uvicorn 0.54.0 and httpx 0.28.1. Both scripts import helpers from tracing.py, which appears in the traceAI section below together with the tracing setup.

The Remote Agent

The remote agent has three parts. OrderStatusExecutor.execute creates or reuses the task, writes the answer as an artifact through TaskUpdater.add_artifact, then sets TASK_STATE_COMPLETED. The AgentCard declares one JSONRPC interface and one skill. Starlette then serves the card route and the JSON-RPC route on port 9999. Save it as remote_agent_clean.py.

"""Remote A2A agent (clean tracing variant): answers order-status lookups from a fixed dict (no LLM)."""
import uvicorn
from starlette.applications import Starlette

from a2a.helpers import get_message_text, new_task_from_user_message, new_text_part
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill, TaskState
from traceai_a2a import A2ATracingMiddleware

from fi_instrumentation import register
from fi_instrumentation.fi_types import ProjectType
from opentelemetry.sdk.trace.export import ConsoleSpanExporter

from tracing import fmt

ORDERS = {"A-1001": "shipped", "A-1002": "processing", "A-1003": "delivered"}


class OrderStatusExecutor(AgentExecutor):
    async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
        task = context.current_task or new_task_from_user_message(context.message)
        if not context.current_task:
            await event_queue.enqueue_event(task)
        updater = TaskUpdater(event_queue=event_queue, task_id=task.id, context_id=task.context_id)
        order_id = (get_message_text(context.message) or "").strip()
        answer = f"Order {order_id}: {ORDERS.get(order_id, 'not found')}"
        await updater.add_artifact(parts=[new_text_part(text=answer, media_type="text/plain")])
        await updater.update_status(state=TaskState.TASK_STATE_COMPLETED)

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


card = AgentCard(
    name="Order Status Agent",
    description="Looks up the shipping status of an order ID.",
    version="0.1.0",
    default_input_modes=["text/plain"],
    default_output_modes=["text/plain"],
    capabilities=AgentCapabilities(streaming=False),
    supported_interfaces=[AgentInterface(
        protocol_binding="JSONRPC", url="http://127.0.0.1:9999", protocol_version="1.0")],
    skills=[AgentSkill(
        id="order_status", name="Order status", description="Return status for an order ID",
        tags=["orders"], examples=["A-1001"])],
)

if __name__ == "__main__":
    # register() only: no A2AInstrumentor on the server, so only the middleware span is exported.
    provider = register(project_type=ProjectType.OBSERVE, project_name="order-lookup-handoff",
                        batch=False, span_exporter=ConsoleSpanExporter(formatter=fmt), verbose=False)
    handler = DefaultRequestHandler(
        agent_executor=OrderStatusExecutor(), task_store=InMemoryTaskStore(), agent_card=card)
    app = Starlette(routes=create_agent_card_routes(card) + create_jsonrpc_routes(handler, "/"))
    app = A2ATracingMiddleware(app, tracer_provider=provider, agent_url="http://127.0.0.1:9999")
    uvicorn.run(app, host="127.0.0.1", port=9999, log_level="warning")

The two tracing calls at the bottom, register() and the A2ATracingMiddleware wrapper, are the server half of the traced handoff. Without them the agent still works as a plain A2A server. The traceAI section below explains what they add and why the server skips the client instrumentor.

The Client Agent

The client discovers the remote agent before it sends anything. A2ACardResolver fetches the Agent Card from the well-known path, create_client picks a transport from the card’s interfaces, and send_message delivers one SendMessageRequest holding the order ID. Tracing is set up at import time, before any client exists, as the traceAI README asks. Save it as client_agent.py.

"""Client agent: discovers the remote Agent Card, hands off one task, prints the result."""
import asyncio

import httpx
from a2a.client import A2ACardResolver, ClientConfig, create_client
from a2a.helpers import new_text_message
from a2a.types import Role, SendMessageRequest

from tracing import setup

provider = setup("order-lookup-handoff")  # instrument before creating clients


async def main():
    async with httpx.AsyncClient() as http:
        card = await A2ACardResolver(httpx_client=http, base_url="http://127.0.0.1:9999").get_agent_card()
        print(f"Agent Card: {card.name} | {card.description}")
        print(f"  skills={[s.id for s in card.skills]} url={card.supported_interfaces[0].url}")

        client = await create_client(agent=card, client_config=ClientConfig(httpx_client=http, streaming=False))
        request = SendMessageRequest(message=new_text_message("A-1001", role=Role.ROLE_USER))
        try:
            async for event in client.send_message(request):
                print("Response event:", event)
        finally:
            await client.close()


try:
    asyncio.run(main())
finally:
    provider.force_flush()

Run Result

Start the remote agent in the background, wait two seconds for it to bind the port, run the client, then stop the server. The client prints the card it discovered and the task it got back. The excerpt below drops the span lines (shown in the next section), the IDs and the timestamp; every other line is as printed.

python remote_agent_clean.py > server_clean.log 2>&1 &
sleep 2
python client_agent.py > client_clean.log 2>&1
pkill -f remote_agent_clean.py
Agent Card: Order Status Agent | Looks up the shipping status of an order ID.
  skills=['order_status'] url=http://127.0.0.1:9999
Response event: task {
  status {
    state: TASK_STATE_COMPLETED
  }
  artifacts {
    parts {
      text: "Order A-1001: shipped"
      media_type: "text/plain"
    }
  }
  history {
    role: ROLE_USER
    parts {
      text: "A-1001"
    }
  }
}

The task reached TASK_STATE_COMPLETED, the answer arrived as an artifact with a text/plain part, and the history holds the user message that started the task. That is the whole A2A round trip in one run: discovery through the card, one message, one task and one artifact.

Tracing the A2A Handoff With Future AGI traceAI

Future AGI’s open-source traceAI-a2a package instruments the A2A client and wraps the receiving server, so the client agent’s send_message span and the remote agent’s request span share one OpenTelemetry trace ID, with the server span parented to the client span.

The traceAI-a2a package lives in traceAI, Future AGI’s open-source instrumentation layer built on OpenTelemetry.

Tracing Setup

The file tracing.py holds the shared setup. register() builds a tracer provider for a Future AGI Observe project, and A2AInstrumentor attaches it to the A2A SDK’s client calls. The fmt helper prints each finished span as two compact lines, so you can read the trace in a terminal.

"""Shared tracing setup: traceAI register() with a console exporter (no FI keys needed)."""
from fi_instrumentation import register
from fi_instrumentation.fi_types import ProjectType
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
from traceai_a2a import A2AInstrumentor


def fmt(span):
    ctx = span.context
    parent = format(span.parent.span_id, "016x") if span.parent else "-"
    attrs = {k: v for k, v in span.attributes.items()}
    return (f"[span] {span.name} kind={span.kind.name} trace={ctx.trace_id:032x} "
            f"span={ctx.span_id:016x} parent={parent} status={span.status.status_code.name}\n"
            f"       attrs={attrs}\n")


def setup(project_name):
    # span_exporter= replaces the Future AGI OTLP exporter; drop it once FI_API_KEY/FI_SECRET_KEY are set.
    provider = register(
        project_type=ProjectType.OBSERVE,
        project_name=project_name,
        batch=False,
        span_exporter=ConsoleSpanExporter(formatter=fmt),
        verbose=False,
    )
    A2AInstrumentor().instrument(tracer_provider=provider)
    return provider

With span_exporter set, spans print to the console and no Future AGI keys are needed. To send spans to Future AGI, set FI_API_KEY and FI_SECRET_KEY and drop the span_exporter argument. register() then uses its default exporter.

Client Instrumentor, Server Middleware

The two processes trace differently on purpose. The client runs A2AInstrumentor, which enriches the SDK’s native send_message span instead of adding a second one. The remote agent runs A2ATracingMiddleware, which extracts the incoming trace context, opens a server span per request, records the HTTP status code, and marks the span as an error on an exception or a 5xx response.

What the Joined Trace Shows

Here are the two spans that make up the handoff, copied from our run on 2026-11-01. We left out the client’s close span and one other server request span, each in its own trace. Trace and span IDs change on every run.

The two handoff spans from our run share one trace ID, and the server span A2AServer.request has the client send_message span as its parent

# client process
[span] a2a.client.transports.jsonrpc.JsonRpcTransport.send_message kind=CLIENT trace=ca8c8037eafc488d90a20290352c9f97 span=8cfaf0acfb9741ed parent=- status=OK
       attrs={'gen_ai.span.kind': 'A2A_CLIENT', 'rpc.system': 'a2a', 'rpc.method': 'send_message', 'gen_ai.a2a.streaming': False, 'gen_ai.a2a.message.role': 'user', 'gen_ai.a2a.message.parts.count': 1, 'gen_ai.a2a.agent.url': 'http://127.0.0.1:9999', 'gen_ai.a2a.propagated_trace_id': 'ca8c8037eafc488d90a20290352c9f97', 'gen_ai.a2a.task.id': '2a9c8973-dd37-4fc1-ab86-14130e082811', 'gen_ai.a2a.task.state': 'completed'}

# remote agent process
[span] A2AServer.request kind=SERVER trace=ca8c8037eafc488d90a20290352c9f97 span=1acf1ded50e847aa parent=8cfaf0acfb9741ed status=OK
       attrs={'gen_ai.span.kind': 'A2A_SERVER', 'gen_ai.a2a.agent.url': 'http://127.0.0.1:9999', 'http.response.status_code': 200}

Both spans carry trace ca8c8037eafc488d90a20290352c9f97. The server span’s parent is 8cfaf0acfb9741ed, the client span’s own ID, so the server request is a child of the client call. Both processes also register the same project name, order-lookup-handoff, so both spans carry the same project_name resource attribute.

What It Leaves Out on Purpose

The adapter records request metadata only. Per the README, message bodies, artifacts, authentication headers, URL credentials, paths and queries, and exception messages and stack traces are not captured. A2A makes no model call itself, so these spans carry no token usage or model cost.

Add the remote agent’s own framework instrumentation, as the README advises, to see its model calls beneath the server span.

Once both processes export to Future AGI, their spans go to the order-lookup-handoff project. The docs define a trace as a tree of spans sharing one trace ID, even across services, and explain what traceAI is.

Our post on traceAI and OpenTelemetry covers the wider instrumentation set.

Routing A2A Handoffs Through Agent Command Center

When more than two agents hand off work, Future AGI’s Agent Command Center can sit in the middle. Its docs say it “acts as an A2A node: it can receive tasks from other agents and delegate tasks to downstream A2A agents.” It lists the registered downstream A2A agents at GET /v1/agents.

The setup steps are in the docs for Agent Command Center as an A2A node.

Seeing the Order Lookup Handoff End to End

Go back to the order lookup that came back wrong. The support agent now reads the Order Status Agent’s card before calling it, so it knows the skill, the endpoint and, once the card declares securitySchemes, the auth scheme to present. The handoff is a task with a defined state, and the answer is an artifact the client can check.

When that answer is wrong again, you open one trace instead of two log streams. The client span shows the call leave the support agent with its task ID and final state, the server span shows it arrive under the same trace ID, and the task ID leads you to the remote agent’s own records.

Run the three files from this guide against your own agents. Once FI_API_KEY and FI_SECRET_KEY are set, drop span_exporter so both processes export to the shared Observe project, and add the remote agent’s framework instrumentation as soon as it starts calling a model.

Frequently Asked Questions

What exactly is the A2A protocol?

The A2A protocol is an open standard that lets AI agents built by different teams or vendors discover each other, hand off tasks, and return results without sharing internal code. Each agent publishes an Agent Card, and work moves as tasks over JSON-RPC, gRPC, or HTTP+JSON. Version 1.0.0 shipped on March 12, 2026.

Is the A2A protocol over HTTP?

Yes, the A2A protocol runs over HTTP, with JSON-RPC 2.0 and HTTP+JSON/REST bindings for plain HTTP calls plus a gRPC binding. Production deployments must use HTTPS or TLS. Streaming on the JSON-RPC binding uses Server-Sent Events, and each agent lists its bindings in its Agent Card.

Will A2A replace MCP?

No, A2A will not replace MCP, because MCP connects one agent to tools and data, while A2A connects separate agents that delegate work to each other. The two protocols solve different problems, and many agent systems run both side by side. Our MCP vs A2A guide compares them in detail.

How does A2A protocol authentication work?

A2A protocol authentication is declared in the Agent Card's securitySchemes field, which can list API key, HTTP auth, OAuth 2.0, OpenID Connect, or mutual TLS. The client obtains credentials out of band and sends them with every request. Version 1.0 adds the OAuth device code flow and a PKCE flag.

Is the A2A protocol production ready?

Yes, the A2A protocol reached a stable v1.0.0 specification on March 12, 2026, with official SDKs in Python, Go, Java, JavaScript, C#/.NET, and Rust. It joined the Agentic AI Foundation as a Growth Stage project in August 2026, and the project reports backing from over 150 organizations.
Related Articles
View all