The Agent2Agent (A2A) protocol is gaining attention as a way to facilitate interoperability between AI agents developed on different frameworks and platforms. To better understand its capabilities, limitations, and real-world implications, I explored the protocol and developed a proof-of-concept based on it.
This article shares the insights gained during that process, covering fundamental A2A concepts, communication patterns, security considerations, resilience mechanisms, and practical implementation aspects. I hope it serves as a useful reference for anyone developing interoperable, agent-based AI solutions.
What is the Agent2Agent protocol?
Agent2Agent(A2A) protocol is an open standard for communication between AI agents originally developed by Google and donated to the Linux Foundation. In recent years, we have seen a lot of growth in agentic applications. As this space matures, it makes more sense to use agents developed by other people or companies rather than reinventing the wheels ourselves.
In the early era of computers, people used computers locally, running programs on isolated machines that had no real way to talk to one another. Then came internet and suddenly computers could talk to each other over standard protocols like TCP/IP and HTTP. This one change unlocked everything we take for granted – email, web, APIs, cloud computing. We are at a similar point with AI agents today. Right now, most agentic systems work in their own silos, unable to collaborate with agents built on different frameworks or vendors. A2A is trying to be that protocol which will allow agents to work together regardless of the underlying tools they run on.
The core design principle worth calling out explicitly: Agents can securely exchange information to achieve a user’s goal without needing access to each other’s internal state, memory, or tools. An agent simply publishes what it can do, and other agents interact with it purely through messages, treating it as a black box. This is deliberate. It means a finance agent built in-house doesn’t need to know whether a travel-booking agent it’s talking to is running on LangChain, Google ADK, a custom stack, or something a vendor ships as a black box behind an API.

Why A2A? When to use It, and when not to
A delegated call isn’t just a tool call
The first time you look at an A2A message, it’s tempting to say, “Isn’t this just a REST call with extra steps?” It’s a fair question, and honestly a healthy one to ask before adopting any new protocol. But a tool call and a delegated A2A call are solving different problems.
When you call a tool like a weather API or a database query, you already know what you will get back. You know what the output will look like because you wrote the code, or someone gave you the documentation. The interaction is always the same: you put in the input; you get the same output. A tool does not make decisions it just does what it is told.
An agent is different. When you use A2A to delegate a task to another agent, you are not just calling a function. You are giving a goal to something that can think, plan and use tools to get the job done. This can take minutes, and the agent might even ask you questions along the way. You do not know how the agent will do the task, and you do not need to know. A2A is designed to handle this uncertainty. It has features for long tasks, updates, and checking the status.
Could you build a custom API to do all this? Yes, you could. Then you would have to maintain a special code for every new agent you want to work with. That code would break every time the other team changes how their agent works. A2A is a protocol that everyone can use, so you only have to write the integration code.
When A2A pays off
A2A is useful in a situation when you need to work with an agent that you do not control, and you want to be able to change your implementation without affecting the other agent.
This is important in environments where multiple teams and vendors are working together. For example, a company might use a customer support agent from one vendor, a fraud-detection agent from another vendor, and an internal scheduling agent. All these agents need to be able to work. A2A is also useful when you are building a platform, and you want other teams to be able to plug in their agents without you having to write special code for each one.
If you think you will need to add agents or swap vendors in the future, it is worth using A2A. The extra work you put in at the beginning will pay off in the long run.
When not to use A2A
You do not need to use A2A for everything. If you are building two agents that will only talk to each other and you are in control of both agents, a custom internal API might be simpler and faster to build. You do not need A2A if you are not solving an interoperability problem.
Also, if the other side of the interaction is a simple function like getting the current stock price or validating a zip code, you do not need A2A. That is just a tool call, not a delegation. Using A2A in this case would just add work.
If latency is a big concern for you, you should know that A2A can add a bit of overhead.
Core concepts
A2A protocol consists of the following core components: A2A client, A2A server, Agent card, Task, Message, Artifact and Part.
- A2A client – A2A client can be an app, service or AI agent that uses A2A protocol to delegate tasks to remote agent.
- A2A server – A2A server is any remote agent that exposes itself according to A2A protocol. It processes requests and provides status updates or results.
- Agent card – A2A server’s way of introducing itself and telling clients what it can do and how to interact with it.
- Task – The unit of work that a client delegates to an agent. Each task has a unique ID. Tasks are stateful and progresses through defined lifecycle states (submitted, working, input-required, completed, failed, etc).
- Message – A single turn in the conversation around that task. It can come from either the client or the agent. It contains one or more part holding the content.
- Artifact – The actual output produced by the task. For example, a generated itinerary, a list of hotels, or a completed document.
- Part – A piece of content inside a message or artifact. It can be text, a file, or structured data.
The important part isn’t just knowing these five concepts. It’s understanding how A2A defines them.
These concepts are defined in protobuf and can be exposed through different transport mechanisms, including JSON-RPC, gRPC, and REST/HTTP+JSON. This separates the protocol’s data model from the underlying transport.
In practice, this means two A2A implementations don’t necessarily have to use the same transport to follow the same protocol. One team might use gRPC for internal communication, while another might expose REST/HTTP+JSON because it’s easier to integrate and debug. As long as they follow the A2A data model and protocol semantics, they can still communicate using A2A.
Agent card
A useful way to think about the Agent Card is as an agent’s digital business card.
The agent provider publishes the Agent Card so that clients can discover the agent and understand how to interact with it. It is typically exposed as a JSON document through an HTTP endpoint, but the provider decides how that endpoint is made available. Depending on the deployment, the Agent Card endpoint may be publicly accessible or secured. If it is secured, the client needs to provide whatever authentication is required by the provider to retrieve the card.
The Agent Card contains the information a client needs to decide whether the agent is suitable and how to communicate with it, including:
- Who is this agent? – Its name, description, and identity.
- Where can I reach it? – The agent’s service endpoint.
- What can it do? – The skills and capabilities it expose.
- How can I interact with it? – The A2A capabilities and protocol details it supports.
- How do I authenticate? – The security schemes and authentication requirements expected by the agent.
Once the client retrieves the Agent Card, it has enough information to understand what the agent can do, where to send requests, and what authentication it needs to use.
This makes discovery relatively simple: the provider publishes the information, and the client retrieves it and decides whether the agent is suitable for the task.
Authentication and authorization
A2A defines how authentication requirements are described and used, but it does not handle the actual credential acquisition or validation.
An Agent Card can specify the authentication mechanisms required by an agent using security_schemes. It supports API keys, HTTP Bearer tokens, OAuth2, OpenID Connect, mutual TLS, and more. Different skills can even have different authentication requirements.
However, declaring a security scheme in the Agent Card does not mean A2A automatically obtains or validates the credentials.
The client is responsible for obtaining the credentials required by the security scheme and providing them when calling the agent server. For example, if the Agent Card requires an API key, the client needs to obtain that API key and send it in the expected header.
If OAuth2 is required, the client needs to handle the OAuth flow, obtain an access token from the appropriate authorization server, refresh it when necessary, and send it as a bearer token. Similarly, for mutual TLS, the client needs to have the appropriate client certificate and configure the connection accordingly.
For example, the flow for OAuth2 would roughly be:
Agent Card → Client identifies OAuth2 requirement → Client obtains OAuth credentials/token → Client attaches the access token → Agent Server receives the request.
The server side is similar: A2A does not validate the credentials itself. The agent server needs to integrate with its own authentication mechanism or middleware to validate the API key, bearer token, OAuth token, client certificate, etc. Without that server-side authentication layer, the request is effectively unauthenticated regardless of what credentials the client sends.
Once authentication is properly configured, the caller’s identity can also be used for authorization. For example, A2A’s task storage can associate tasks with the authenticated caller, allowing tasks/list to return only tasks belonging to that caller.
Four ways to communicate
- Synchronous – This is the simplest way of communication. There is one call, one blocking wait, one final answer. No task Id is generated and nothing to check back on later. This can be used where we expect agent to respond faster and hence waiting for it to respond is fine. The agent signals this itself by enqueuing a single Message rather than a Task.
- Polling – In this way of communication, the client agent keeps on calling the server periodically until it gets the terminal response. The agent hands back a Task immediately and the client periodically calls tasks/get until it reaches the terminal state. This way of communication can be used when the tasks take a noticeable amount of time but doesn’t need response right when response is generated.
- Server sent events – The client creates a connection with the server and keeps the connection open for streaming. Once the connection is established, the server sends the events as they are generated. The client agent in case of A2A protocol received the tasks status update event and task artifact update event. This kind of communication is well suited where the task may take longer, and client wants to receive incremental progress but doesn’t want to increase the load on the server by periodic calls like we do in polling.
- Push notifications – The client registers a callback URL up front, gets an immediate acknowledgment, and does nothing else. The agent’s server POSTs updates to that URL as the task progresses. Fits long-running, human-in-the-loop-shaped work where the client shouldn’t have to stay connected or keep asking.

| Pattern | Best suited for |
| Synchronous | Fast operations |
| Polling | Long running tasks where updates aren’t important |
| SSE | Long-running tasks where incremental updates matter |
| Push Notifications | Long-running tasks where maintaining a connection isn’t practical |
Building the PoC
GitHub PoC URL – GitHub – Ankitwasnik/agent2agent-poc: Agent2Agent protocol PoC
- Flight search (synchronous) – An agent that searches for flights based on the destination and travel date. Since the operation is expected to complete quickly, a synchronous interaction is a good fit. The client sends the request and receives the result directly, without needing to create or monitor a long-running task.
- Itinerary planning (polling) – An agent that creates a day-by-day itinerary for a destination. Generating the itinerary may take a few seconds because it involves an LLM call and additional planning work. This makes it a good candidate for an A2A Task, but the operation isn’t long running enough to require continuous updates. The client can periodically check the task status and retrieve the result when it’s ready.
- Hotel search (SSE streaming) – An agent that searches for hotels and sends results as they become available. Instead of waiting for the entire search to finish, the client can receive incremental updates through SSE. For example, the agent can stream individual hotel results as task artifacts or updates, followed by a final task-completion event. The fact that results arrive incrementally is an application-level choice. A2A provides the streaming mechanism through which those updates can be delivered.
- Visa/Travel-requirements (push notifications) – An agent that simulates checking visa and travel requirements for a particular nationality and destination. The PoC does not perform any actual visa or travel-requirement checks. The scenario is used to demonstrate a long-running A2A task. The agent simulates a process that may take a significant amount of time, such as document verification or external review. Instead of requiring the client to remain connected or continuously poll for updates, the client can configure push notifications and receive an update when the simulated task progresses or completes.
The four agents above each show one pattern in isolation. But most real tasks don’t stay that clean – an agent might be halfway through a long-running job and suddenly need an answer from you before it can continue. The next two agents show what that looks like.
Multi-turn interaction
- Restaurant reservation (multi-turn) – An agent that helps the user book a restaurant table through a multi-turn conversation. If some information is missing, such as the date, time, or number of guests, the agent doesn’t fail the task or ask the client to start over. Instead, the task moves toINPUT_REQUIREDand waits for the user’s response. The client can then send a follow-up message using the same task ID, allowing the agent to continue the existing task and conversation rather than creating a new one.
- Trip booking (long-running + multi-turn, combined) – An agent that combines both patterns. The booking process is long-running. For example, searching for suitable flights may take some time, so the client can poll the task while the work is in progress. Once the agent finds suitable options, it may need a decision from the user, such as which flight they want to book. At that point, the task moves to INPUT_REQUIRED and pauses until the user responds. The client sends the user’s choice using the same task ID, and the agent resumes the existing task and continues with the remaining booking steps.
Designing for failure
A protocol only earns trust once you know what happens when things go wrong, not just when they go right. A2A is explicit about the failure modes it expects a client to plan for:
- A forged or tampered agent card
- A version mismatch between client and agent
- A stream dropping mid-task
- A task getting stuck, paused indefinitely
- A webhook never arriving
For each of these scenarios, the A2A protocol and its SDK provides you a mechanism, but the client must implement the mechanism. In the PoC, we broke things in purpose to see what happens. The failure scenarios are implemented in the following folder of the GitHub repo – src/agent2agent/resilience. There wasn’t a change in the server implementation, the changes were in the client implementation. Client has to decide how long to wait, when to give up and fall back to polling, when to retry etc. We implemented the recommended solution for each of the above mentioned issue.
What a resilient agent client should actually do
A client only earns the label “resilient” if it goes in assuming every one of these will eventually happen: connections drop, webhooks never arrive, the agent takes longer than expected, its card gets tampered with in transit, and the network just flakes out for no reason.
- Verify identity before you trust capability: Don’t fetch a card and act on it. Fetch it, verify the signature against a public key you already decided to trust, and only then read
capabilities/supported_interfacesto decide how to talk to the agent. Being well-formed JSON doesn’t make a card self-authenticating. - Know what you got back before you assume you can watch it: A Message response is final. No task ID, nothing to poll, stream, or cancel. Only a Task response gives you an ID worth holding onto. Branch on which one you got; don’t assume every reply is pollable.
- Persist the task ID the moment you have it: Agent server provides a durable taskId for each submitted task. Persist this taskId to a DB the moment it is issued before anything else happens to the connection. This is the only thing that will help you resume the task.
- For multi-turn tasks, persist the conversation state, not just the ID: A task sitting in INPUT_REQUIRED is durable on the server, but a client that only wrote down the task ID knows the task exists without knowing what it’s waiting on. Recovering after a crash means being able to re-prompt the user for the missing piece, not just confirm the task is still alive.
- Follow Streaming -> SubscribeToTask -> GetTask in the order: Streaming provides the live view of the response. If the connection drops, resubscribing gets you back with the full snapshot. If the task got finished before resubscribing then GetTask method is the fallback that will provide the final response.
- Treat push notifications as an optimization, never a guarantee: If there is some issue with the agent server and you did not receive a push notification, you may not have the response even though server processed it. Pair every webhook registration with a timeout and a polling fallback.
- Decide your own timeout: Agents are non-deterministic and can take longer than expected for some tasks. Decide the timeout for your tasks and cancel the task on timeout.
Conclusion
A2A is ultimately about making agents interoperable. Instead of building a custom integration every time one agent needs to work with another, A2A provides a common language for discovery, delegation, task management, and exchanging results.
A2A gives us the building blocks such as Agent Cards, Tasks, Messages, and Artifact. It also shows us ways that agents can communicate with each other and how to keep things secure. However, A2A does not automatically make agent integration reliable. The client still has to take care of things like authentication, saving task progress, dealing with lost connections and recovering from failed webhooks.
As agentic systems become more common, we will likely have more situations where an agent needs to collaborate with other agents that it does not know much about. A common protocol can make that collaboration much easier to build and evolve.
The PoC in this article was mainly an experiment in understanding that boundary – what A2A provides, what the client still needs to take care of, and what happens when things go wrong. That, more than simply making two agents talk to each other, is where the real value of understanding A2A lies.
References/Links
- A2A Protocol
- https://learn.deeplearning.ai/courses/a2a-the-agent2agent-protocol/lesson/vtf72ap4/introduction
- GitHub – Ankitwasnik/agent2agent-poc: Agent2Agent protocol PoC
About Author
Ankit Wasnik is a senior software engineer specializing in the development of high-performance architectures and scalable digital solutions. Passionate about modern technologies and autonomous engineering, he enjoys breaking down complex technical concepts into practical, accessible information for developers. When he is not writing production-ready code or optimizing distributed systems, he actively explores the ever-evolving landscape of AI-native engineering and decentralized protocols.