A2A Protocol Implementation Guide: Build an Agent in a Day

Emily Winks profile picture
Data Governance Expert
Updated:06/22/2026
|
Published:06/22/2026
18 min read

Key takeaways

  • Implementing A2A with the official a2a-sdk follows eight steps and reaches a working hello-world server in about a day.
  • The Agent Card is served at /.well-known/agent-card.json; transport is JSON-RPC 2.0 with SSE for streaming.
  • A task moves through submitted to working to a terminal state; always emit a terminal event or the caller waits forever.
  • A2A carries messages, not meaning. Coordinating agents still need a governed context layer to agree on what data means.

How do you implement the A2A protocol?

Implementing Google's A2A (Agent2Agent) protocol takes roughly a day to a working hello-world server using the official a2a-sdk, and follows eight steps: install the SDK, define an Agent Card, implement an Agent Executor, wire the request handler and server, handle the task lifecycle, enable discovery, secure the endpoint, and test interop. A2A was donated to the Linux Foundation in 2025 and crossed 150 supporting organizations in its first year. The verified spine: the Agent Card is served at /.well-known/agent-card.json, transport is JSON-RPC 2.0 with Server-Sent Events for streaming, and a task moves through submitted to working to completed.

The verified spine:

  • SDK: pip install a2a-sdk. Official SDKs in Python, JavaScript, Java, C#/.NET, Go, and Rust.
  • Discovery: Agent Card served at /.well-known/agent-card.json (renamed from agent.json in v0.3).
  • Transport: JSON-RPC 2.0 over HTTP(S); Server-Sent Events for streaming; webhook push notifications.

Is your AI context-ready?

Get the AI Context Stack

The steps below use only real a2a-sdk classes and verified spec method strings. If you also need the tool-access side of agent infrastructure, the MCP server implementation guide covers the complementary protocol.

Field Value
Protocol Agent2Agent (A2A), a Linux Foundation project
Latest spec v1.0 / v0.3.0 line (a2a-protocol.org)
Transport JSON-RPC 2.0 over HTTP(S); SSE for streaming; webhook push notifications
Install pip install a2a-sdk
Discovery path https://{domain}/.well-known/agent-card.json (RFC 8615)
Official SDKs Python, JavaScript, Java, C#/.NET, Go, Rust
Difficulty Intermediate (Python plus async web server)
Time to hello-world ~1 day


Why implement A2A and what does it actually solve?

Permalink to “Why implement A2A and what does it actually solve?”

A2A standardizes how agents built on different frameworks coordinate with each other over a common wire protocol. Without it, every pair of agents needs a bespoke integration, and integration count grows with the square of the agent population. Teams building multi-agent systems, where a coordinator agent delegates to specialist agents, are the ones who should build on it.

The core problem is fragmentation. An agent built on one framework cannot call an agent built on another without a shared language for discovery, delegation, and secure collaboration. A2A gives all of them that language. According to the Linux Foundation (2026), the protocol surpassed 150 supporting organizations in its first year, with a Technical Steering Committee that includes AWS, Cisco, Google, IBM Research, Microsoft, Salesforce, SAP, and ServiceNow.

A2A pairs naturally with the Model Context Protocol. MCP connects an agent to its tools and data. A2A connects an agent to other agents. If you already run MCP for tool access, A2A is the delegation layer on top. The two are complementary protocols, not competing choices, and the MCP vs A2A comparison walks through where each one fits. This guide assumes you already understand what A2A is; the definitional overview of Google’s A2A protocol covers the concepts if you need them first.

A2A solves coordination. It does not, on its own, make the coordinating agents agree on what their data means, which is the gap this guide returns to at the end.


Prerequisites for building an A2A agent

Permalink to “Prerequisites for building an A2A agent”

Before you write any code, confirm you have the runtime, the server, a reachable endpoint, and an agent backend in place so you can self-qualify for this build.

Technical prerequisites:

  • [ ] Python 3.10+ environment with pip install a2a-sdk
  • [ ] An ASGI server such as uvicorn; the A2A Python server is Starlette-based via A2AStarletteApplication
  • [ ] A reachable HTTPS domain, required for /.well-known/agent-card.json discovery
  • [ ] An agent or LLM backend that your executor will call

Knowledge prerequisites:

  • [ ] Familiarity with async Python and JSON-RPC 2.0
  • [ ] Understanding of the Agent Card concept

Quick overview: Time ~1 day. Difficulty intermediate. Tools: a2a-sdk, uvicorn, Python 3.10+. Prereqs: an HTTPS domain and an agent backend.


Step 1: Install the a2a-sdk and set up your environment

Permalink to “Step 1: Install the a2a-sdk and set up your environment”

The first step installs the official SDK and confirms the package imports cleanly, which takes a few minutes and rules out the most common version mistake before you write logic.

Create a virtual environment and install the SDK:

python -m venv .venv
source .venv/bin/activate
pip install a2a-sdk uvicorn

Validate the install:

python -c "import a2a; print('a2a-sdk ready')"

You should see a2a-sdk ready. A common mistake here is copying a stale 0.2.x sample that serves the Agent Card at /.well-known/agent.json. The current a2a-sdk uses /.well-known/agent-card.json, and shipping the old path silently breaks discovery for every client that tries to reach you.


Step 2: Define your AgentSkill and Agent Card

Permalink to “Step 2: Define your AgentSkill and Agent Card”

This step declares what your agent can do and how peers reach it, which matters because the Agent Card is the public contract every other agent reads before delegating to you.

Construct one or more AgentSkill objects, wrap your capabilities in AgentCapabilities, and assemble the AgentCard:

from a2a.types import AgentSkill, AgentCapabilities, AgentCard

skill = AgentSkill(
    id="summarize_report",
    name="Summarize report",
    description="Summarizes a financial report into key points.",
    tags=["finance", "summarization"],
    examples=["Summarize the Q3 revenue report"],
)

card = AgentCard(
    protocolVersion="0.3.0",
    name="Report Summarizer",
    description="An agent that summarizes financial reports.",
    url="https://your-domain.example/",
    version="1.0.0",
    preferredTransport="JSONRPC",
    capabilities=AgentCapabilities(streaming=True, pushNotifications=False),
    defaultInputModes=["text/plain"],
    defaultOutputModes=["text/plain"],
    skills=[skill],
)

The card is served at /.well-known/agent-card.json. Version 0.3 renamed this path from the older /.well-known/agent.json, so do not hardcode the legacy path anywhere in your client or your reverse proxy.

Field Type Purpose
protocolVersion string Declares which A2A spec version the card follows
name / description string Human-readable identity of the agent
url string Base endpoint where the JSON-RPC service lives
version string Your agent’s own release version
preferredTransport string Default "JSONRPC"; also gRPC and HTTP+JSON bindings
capabilities object Flags such as streaming and pushNotifications
defaultInputModes / defaultOutputModes array Default media types the agent accepts and returns
skills array The AgentSkill objects the agent exposes
securitySchemes / security object Declared auth schemes (covered in the discovery and security step)
supportsAuthenticatedExtendedCard boolean Whether an authenticated extended card is available
signatures array Cryptographic signatures for Signed Agent Cards

A common mistake is embedding plaintext API keys directly in the card. The card is publicly fetchable, so declare securitySchemes instead and supply credentials out of band.


Step 3: Implement the Agent Executor

Permalink to “Step 3: Implement the Agent Executor”

The executor is where your agent does its work, and you implement it by subclassing the abstract AgentExecutor and filling in two async methods, execute() and cancel().

from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.utils import new_agent_text_message

class ReportSummarizerExecutor(AgentExecutor):
    async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
        # Read the incoming message, run your agent/LLM, enqueue results.
        result_text = "Summary: revenue up, costs flat."
        await event_queue.enqueue_event(new_agent_text_message(result_text))

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

The executor receives a RequestContext (the incoming request and task metadata) and an EventQueue (where you push results). You enqueue a Message for a direct reply, or a Task plus TaskStatusUpdateEvent and TaskArtifactUpdateEvent for longer-running work.

A2A models content as parts. A Message carries a role of either "user" or "agent" and a parts array. Each Part is one of three types: TextPart for text, FilePart for files (using file_with_uri or file_with_bytes plus mimeType and name), and DataPart for structured JSON. An Artifact wraps output parts as the result of a task.

The most damaging mistake at this step is never emitting a terminal event. If your executor returns without driving the task to completed or failed, the task hangs in working and the calling agent waits forever. Always enqueue a terminal status.


Step 4: Wire the request handler and start the server

Permalink to “Step 4: Wire the request handler and start the server”

Now you put your executor on the network. Attach it to a request handler and a Starlette application, and that pairing is what actually exposes the JSON-RPC endpoint and the Agent Card.

import uvicorn
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.tasks import InMemoryTaskStore
from a2a.server.apps import A2AStarletteApplication

handler = DefaultRequestHandler(
    agent_executor=ReportSummarizerExecutor(),
    task_store=InMemoryTaskStore(),
)

app = A2AStarletteApplication(agent_card=card, http_handler=handler)

if __name__ == "__main__":
    uvicorn.run(app.build(), host="0.0.0.0", port=9999)

A2AStarletteApplication automatically exposes both /.well-known/agent-card.json and the JSON-RPC endpoint, so you do not wire routes by hand. Validate it with a request to the well-known path:

curl https://your-domain.example/.well-known/agent-card.json

You should get your Agent Card back as JSON. A common mistake is shipping InMemoryTaskStore to production. It loses all task state on restart, so swap in a durable TaskStore backed by a database before you go live.


Step 5: Handle the task lifecycle

Permalink to “Step 5: Handle the task lifecycle”

Tasks carry the state of any non-trivial request, and handling the lifecycle means emitting the right status transitions as your agent works so the caller always knows where things stand.

State Phase Meaning
submitted Initial Task received, not yet started
working Active Agent is processing the task
input-required Interrupt Agent needs more input from the caller
auth-required Interrupt Agent needs credentials to continue
completed Terminal Task finished successfully
failed Terminal Task could not be completed
canceled Terminal Task was canceled by request
rejected Terminal Task was refused by the agent

Emit TaskStatusUpdateEvent to move a task between states. Use input-required when you need another turn from the caller, and auth-required when the work needs credentials you do not yet have. Three methods support lifecycle management: tasks/get to poll state, tasks/cancel to cancel, and tasks/resubscribe to re-attach to an SSE stream after a disconnect.

A common mistake is treating input-required as terminal. It is an interrupt, not an end state, so the caller resumes the same taskId and contextId rather than starting a new task.


How do A2A agents discover and authenticate each other?

Permalink to “How do A2A agents discover and authenticate each other?”

Discovery happens through the well-known Agent Card path, and authentication happens through declared security schemes plus, as of v1.0, cryptographically Signed Agent Cards. Neither step relies on a central registry, because the protocol leaves discovery and identity largely to you.

Discovery. A client fetches a remote Agent Card at /.well-known/agent-card.json and reads its skills, transport, and security requirements before sending anything. There is no protocol-level registry or auto-discovery as of the current spec, so peers are configured manually. According to the A2A project’s own GitHub, both the Agent Registry proposal (issue #741) and Agent Identity Verification for Agent Cards (issue #1672) remain open, which confirms discovery and identity are unsolved at the protocol layer. For credentialed access to a richer card, the agent/getAuthenticatedExtendedCard method returns the authenticated extended Agent Card.

Security. Declare what you require in securitySchemes:

Scheme Use when
API Key Simple service-to-service calls with a shared key
HTTP Basic / Bearer Standard header-based auth
OAuth 2.0 Delegated access with scoped tokens
OpenID Connect Identity plus authorization in one flow
Mutual TLS Strong, certificate-based peer authentication

Never embed plaintext secrets in the card; prefer out-of-band, dynamically issued credentials. A2A v1.0 added Signed Agent Cards so a receiving agent can cryptographically verify that the domain owner issued the card. That is a real trust improvement, and it is worth being precise about what it proves. A signature confirms who issued the card. It does not confirm that the data behind the agent is certified, current, or consistently defined. Authenticity is not correctness, and it is not shared meaning.

The honest failure mode is trusting any reachable Agent Card. According to Palo Alto Networks (2025), A2A deployments face risks including Agent Card spoofing and tool squatting through discovery, so treat any discovered card as potentially hostile, verify its signature, and scope what a peer agent is allowed to do.


Step 8: Test interoperability across frameworks

Permalink to “Step 8: Test interoperability across frameworks”

The final build step is proving your agent talks to agents you did not write, which you do by exercising the two message methods and validating against a second framework.

Method What it does Returns
message/send Send a message synchronously A Task or Message
message/stream Send a message, stream updates SSE of status and artifact events
tasks/get Poll the state of a task Current Task
tasks/cancel Cancel a running task Updated Task
tasks/resubscribe Re-attach to an SSE stream Resumed SSE stream
tasks/pushNotificationConfig/set (and get / list / delete) Manage webhook push config Push config object
agent/getAuthenticatedExtendedCard Fetch the authenticated extended card Extended AgentCard

Call message/send for a synchronous round trip, then call message/stream and read the SSE stream of TaskStatusUpdateEvent and TaskArtifactUpdateEvent objects as the work progresses. To prove real interoperability, validate against a second framework with built-in A2A support, such as Google ADK, Microsoft Agent Framework, or BeeAI. Runnable examples live at github.com/a2aproject/a2a-samples.

A common mistake is testing only against your own server. Cross-framework interoperability is the entire reason A2A exists, so if you never call an agent built on a different stack, you have not actually validated the implementation.


Common pitfalls when implementing A2A

Permalink to “Common pitfalls when implementing A2A”

Beyond hello-world, four issues account for most A2A implementations that break in production: a stale discovery path, secrets leaked in the card, the assumption that the protocol solves trust, and the assumption that coordination produces agreement. Each one is avoidable once you know the failure mode.

Pitfall 1: Shipping the stale /.well-known/agent.json path

Permalink to “Pitfall 1: Shipping the stale /.well-known/agent.json path”

Older v0.2.x docs and samples serve the card at /.well-known/agent.json. Version 0.3 renamed it to agent-card.json. Copying an old tutorial means clients looking for the current path get a 404 and silently fail to discover you. Use agent-card.json everywhere.

Pitfall 2: Leaking secrets inside the Agent Card

Permalink to “Pitfall 2: Leaking secrets inside the Agent Card”

The Agent Card is publicly fetchable by design. Any API key, token, or connection string placed in it is exposed to anyone who reads the well-known path. Declare securitySchemes to describe how to authenticate and supply credentials out of band. Signed Agent Cards verify the issuer; they do nothing to keep an embedded secret private.

Pitfall 3: Assuming discovery and identity are solved by the protocol

Permalink to “Pitfall 3: Assuming discovery and identity are solved by the protocol”

There is no protocol-level registry and no built-in identity verification, which the open GitHub issues #741 and #1672 confirm. Configure peers manually, verify signatures, and treat the discovery surface as spoofable. According to Palo Alto Networks (2025), Agent Card spoofing and tool squatting are documented A2A risks, not hypotheticals.

Pitfall 4: Mistaking communication for agreement

Permalink to “Pitfall 4: Mistaking communication for agreement”

Two A2A-compliant agents can coordinate flawlessly and still return different numbers for “active customer” because each queries a different system with a different definition. The protocol carries messages, not definitions. The fix is to resolve meaning once, at the source, through a governed context layer that every agent reads from. This pitfall is the bridge into the question every implementation eventually hits.


How do two A2A agents agree on what data means?

Permalink to “How do two A2A agents agree on what data means?”

They do not, on the protocol alone. A2A standardizes the wire, not the meaning, so even a fully working implementation needs a shared governed context layer for the coordinating agents to agree on what their data and terms mean. This is where the build pays off and where its limit becomes clear.

Communication is not shared truth. Two A2A-compliant agents can coordinate perfectly and still disagree on “revenue,” “active customer,” or “churn” because A2A carries messages, not definitions. The durable asset is the governed Context Layer for AI underneath: lineage, ownership, certification, business glossary, and policy that resolve meaning once, at the source.

Start with the gap A2A leaves. The protocol leaves identity to external mechanisms, carries no semantic layer, and provides no shared definitions, lineage, ownership, or certification. That is a documented limitation, not a criticism. A2A does exactly what it set out to do, which is cross-framework coordination, and it does it well. The gap is simply outside its scope.

Atlan closes that gap as the Context Layer for AI. The Enterprise Data Graph is a unified living graph of lineage, business definitions, people, policies, and relationships, and it is delivered to agents through a governed Atlan MCP server that lets them search assets, resolve definitions, traverse lineage, inspect quality and classification, and apply policy at query time. The framing is complementary, not competing: MCP for tool access, A2A for delegation, and a governed context layer for shared meaning. A2A is the coordination channel; the context layer governs what the coordinating agents actually mean.

The proof point is scale. According to Atlan (2026), 95% of AI agents fail to scale, and the failure traces to missing governed context rather than missing connectivity. Customer evidence reinforces it: Workday describes co-building “the semantic layer that AI needs,” and DigiKey treats Atlan as a “context operating system” that delivers context to AI models. The protocol is the pipe. The Context Layer for AI is the durable bet underneath it.


Real stories from real customers

Permalink to “Real stories from real customers”

Workday and DigiKey did not start with a protocol decision. They built a governed context layer first, then activated it for AI agents. The protocol was the delivery vehicle; the governed context was the infrastructure that made the delivery worth having.

"We're excited to build the future of AI governance with Atlan. All of the work that we did to get to a shared language at Workday can be leveraged by AI via Atlan's MCP server…as part of Atlan's AI Labs, we're co-building the semantic layer that AI needs with new constructs, like context products."

Joe DosSantos, VP of Enterprise Data & Analytics, Workday

"Atlan is much more than a catalog of catalogs. It's more of a context operating system…Atlan enabled us to easily activate metadata for everything from discovery in the marketplace to AI governance to data quality to an MCP server delivering context to AI models."

Sridher Arumugham, Chief Data & Analytics Officer, DigiKey


Why the context layer outlives the protocol

Permalink to “Why the context layer outlives the protocol”

A2A is worth implementing. It gives multi-agent systems a real, standardized way to discover, delegate, and collaborate across frameworks, and the eight steps above get you to a working server in about a day. What it does not give you is agreement. The protocol carries messages between agents; it does not carry the definitions, lineage, ownership, or certification those agents need to mean the same thing by “revenue.” Signed Agent Cards prove who is talking, not whether what they say is correct or consistent. The teams that ship durable multi-agent systems treat the protocol as the easy part and invest in the governed Context Layer for AI underneath, because the context layer is the asset that outlives any single protocol.


FAQs about A2A protocol implementation

Permalink to “FAQs about A2A protocol implementation”

1. How do I implement an A2A agent in Python?

Permalink to “1. How do I implement an A2A agent in Python?”

Run pip install a2a-sdk, define an Agent Card from one or more AgentSkill objects, subclass AgentExecutor and implement execute() and cancel(), then wire a DefaultRequestHandler into an A2AStarletteApplication and run it with uvicorn. The server auto-exposes the Agent Card and the JSON-RPC endpoint.

2. What is an Agent Card and where is it hosted?

Permalink to “2. What is an Agent Card and where is it hosted?”

An Agent Card is the public JSON descriptor that lists an agent’s skills, transport, and security requirements. It is served at /.well-known/agent-card.json. Version 0.3 renamed this path from the older /.well-known/agent.json, so do not ship the legacy path.

3. What transport does A2A use?

Permalink to “3. What transport does A2A use?”

A2A uses JSON-RPC 2.0 over HTTP(S) for request and response, Server-Sent Events for streaming, and HTTP webhook push notifications for asynchronous updates. gRPC and HTTP+JSON are additional bindings. The default preferredTransport is "JSONRPC".

4. What are the A2A task lifecycle states?

Permalink to “4. What are the A2A task lifecycle states?”

A task moves from submitted to working to a terminal state of completed, failed, canceled, or rejected. Two interrupt states, input-required and auth-required, pause a task for more input or credentials without ending it.

5. How do agents discover and authenticate each other in A2A?

Permalink to “5. How do agents discover and authenticate each other in A2A?”

Clients fetch a peer’s Agent Card at the well-known path and configure peers manually, since the protocol has no central registry yet. Authentication uses declared security schemes such as OAuth 2.0 or mutual TLS, plus Signed Agent Cards in v1.0 to verify the issuer. Registry and identity verification remain open issues.

6. Which SDKs and frameworks support A2A?

Permalink to “6. Which SDKs and frameworks support A2A?”

Official SDKs exist for Python, JavaScript, Java, C#/.NET, Go, and Rust. Frameworks with built-in A2A support include Google ADK, Microsoft Agent Framework, and BeeAI, which makes them good targets for interoperability testing.

7. Can you use A2A and MCP together?

Permalink to “7. Can you use A2A and MCP together?”

Yes, and most production agents do. MCP connects an agent to its tools and data, while A2A connects an agent to other agents. They are complementary layers, so you run MCP for tool access and A2A for agent-to-agent delegation.

8. How do two A2A agents agree on what data means?

Permalink to “8. How do two A2A agents agree on what data means?”

They do not, on the protocol alone. A2A carries messages, not definitions, so two compliant agents can return different numbers for the same term. A governed context layer resolves shared definitions, lineage, and policy once at the source, which is what gives coordinating agents a single answer.


Sources

Permalink to “Sources”
  1. A2A Protocol Official Specification, v0.3.0 (Linux Foundation)
  2. A2A Python Quickstart Tutorial, A2A Project
  3. Google Cloud donates A2A to the Linux Foundation, Google Developers Blog
  4. Linux Foundation launches the Agent2Agent Protocol Project, Linux Foundation
  5. A2A surpasses 150 organizations in its first year, Linux Foundation
  6. A2A SDK Documentation, DeepWiki / a2aproject
  7. Agent Identity Verification for Agent Cards, a2aproject GitHub Issue #1672
  8. Safeguarding AI Agents: A2A Protocol Risks and Mitigations, Palo Alto Networks
  9. From Glue-Code to Protocols: A Critical Analysis of A2A and MCP Integration, arXiv 2505.03864
  10. Atlan MCP Server

Share this article

signoff-panel-logo

Atlan is the Context Layer for AI. A2A lets your agents coordinate; Atlan gives them one shared, governed answer for what the data means, delivered through a governed MCP server at query time. Trusted by Mastercard, Workday, General Motors, CME Group, HubSpot, and 400+ enterprises.

Bridge the context gap.
Ship AI that works.

[Website env: production]