Google’s Agent2Agent (A2A) Protocol
My Complete Technical Breakdown!
TL;DR — A2A is an open protocol (HTTP + JSON-RPC + SSE) that lets AI agents from different frameworks, vendors, and clouds talk to each other, delegate tasks, and collaborate — without sharing internal state, memory, or tools.
Why A2A Exists
Before A2A, multi-agent systems were walled gardens. LangChain agents talked to LangChain agents. CrewAI stayed inside CrewAI. If you wanted a Salesforce agent to hand off work to a SAP agent, you were writing brittle custom glue code.
A2A is the universal language layer that fixes this. Launched by Google in April 2025, now under the Linux Foundation, it’s an open standard backed by 150+ organizations including Salesforce, Workday, SAP, LangChain, and PayPal.
Simple mental model:
MCP (Anthropic) = how an agent connects to tools and data
A2A (Google) = how agents connect to other agents
They’re complementary. Use both.
Core Architecture: 3 Things to Internalize
1. Client Agent vs. Remote Agent
Every A2A interaction has two sides:
Client Agent — the orchestrator. Discovers capabilities, formulates tasks, sends requests, monitors progress.
Remote Agent — the specialist. Receives tasks, executes them, returns results. Runs as an HTTP server.
One agent can be both a client (to other agents downstream) and a remote agent (to orchestrators upstream). This is how you build hierarchies.
2. The Agent Card (/.well-known/agent.json)
The Agent Card is the identity and capability manifest of every A2A agent. It’s a JSON file served at a fixed, discoverable URL.
{
"name": "Invoice Processing Agent",
"description": "Extracts, validates, and routes invoices",
"url": "https://finance-agent.company.com",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": false
},
"skills": [
{
"id": "extract-invoice",
"name": "Extract Invoice Data",
"description": "Parses invoice PDFs and returns structured data",
"inputModes": ["file", "text"],
"outputModes": ["data"]
}
],
"authentication": {
"schemes": ["Bearer"]
}
}
Client agents fetch this card first. They use it to decide: “Can this agent do what I need? How do I talk to it?”
3. Tasks — The Unit of Work
All communication in A2A is task-oriented. A task has a lifecycle:
submitted → working → (input-required) → completed
↘ failed / canceled
Each task has:
A unique task ID
A message thread (back-and-forth messages between client and remote)
Artifacts (the actual outputs — files, data, structured results)
Status with real-time updates via SSE or push notifications
Transport Layer: What’s Actually Happening on the Wire
A2A runs on boring, enterprise-friendly standards. No proprietary SDKs required.
Layer Technology Transport HTTP/HTTPS Message format JSON-RPC 2.0 Real-time streaming Server-Sent Events (SSE) Async push Webhooks (push notifications) Auth OpenAPI auth schemes (Bearer, OAuth2, API Key) New in v0.3 gRPC support
Core JSON-RPC methods:
tasks/send → Create/continue a task (non-streaming)
tasks/sendSubscribe → Create/continue a task (streaming via SSE)
tasks/get → Fetch task status and artifacts
tasks/cancel → Cancel a running task
tasks/pushNotification/set → Register a webhook URL for async updates
Message Structure
Messages are the atoms of A2A communication. They contain Parts — typed content blocks:
{
"role": "user",
"parts": [
{
"type": "text",
"text": "Process this invoice and send to accounting"
},
{
"type": "file",
"mimeType": "application/pdf",
"data": "<base64-encoded-file>"
}
]
}
Part types: text, file, data (structured JSON). This makes A2A modality-agnostic — text, files, audio, video, structured data all flow through the same channel.
Build an A2A System From Scratch Today
Here’s a complete working example: a Client Agent (orchestrator) delegating to a Remote Agent (specialist). Uses Google’s official Python SDK.
Step 1: Install the SDK
pip install a2a-sdk
Step 2: Build the Remote Agent (the specialist)
# remote_agent.py
from a2a.server.apps import A2AStarlette
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.utils import new_agent_text_message
from a2a.types import AgentCard, AgentSkill, AgentCapabilities
import uvicorn
# Define what this agent can do
agent_card = AgentCard(
name="Summarizer Agent",
description="Summarizes text into bullet points",
url="http://localhost:8001",
version="1.0.0",
capabilities=AgentCapabilities(streaming=False),
skills=[
AgentSkill(
id="summarize",
name="Summarize Text",
description="Takes long text and returns key bullet points",
inputModes=["text"],
outputModes=["text"],
)
],
)
# The actual logic
class SummarizerExecutor(AgentExecutor):
async def execute(self, context: RequestContext, event_queue: EventQueue):
user_input = context.get_user_input()
# Your actual logic here — call an LLM, a function, anything
summary = f"Summary of: {user_input[:50]}...\n• Point 1\n• Point 2\n• Point 3"
await event_queue.enqueue_event(
new_agent_text_message(summary)
)
async def cancel(self, context: RequestContext, event_queue: EventQueue):
raise Exception("Cancel not supported")
# Wire it together
app = A2AStarlette(
agent_card=agent_card,
agent_executor=SummarizerExecutor(),
)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8001)
Step 3: Build the Client Agent (the orchestrator)
# client_agent.py
import asyncio
from a2a.client import A2AClient
from a2a.types import MessageSendParams, SendMessageRequest
from a2a.utils import new_agent_text_message
import httpx
REMOTE_AGENT_URL = "http://localhost:8001"
async def main():
async with httpx.AsyncClient() as http_client:
# Step 1: Discover the remote agent's capabilities
client = await A2AClient.get_client_from_agent_card_url(
http_client,
REMOTE_AGENT_URL
)
print(f"Connected to: {client.agent_card.name}")
print(f"Skills: {[s.name for s in client.agent_card.skills]}")
# Step 2: Send a task
request = SendMessageRequest(
message=new_agent_text_message(
"Please summarize the following article about climate change: "
"Scientists have found that global temperatures have risen by 1.1 degrees "
"Celsius since pre-industrial times, causing more frequent extreme weather events..."
),
configuration=MessageSendParams(
accepted_output_modes=["text"],
)
)
# Step 3: Get the result
response = await client.send_message(request)
# Extract the text from the response
for part in response.result.parts:
if part.root.type == "text":
print("\n--- Agent Response ---")
print(part.root.text)
if __name__ == "__main__":
asyncio.run(main())
Step 4: Run It
# Terminal 1 - start the remote agent
python remote_agent.py
# Terminal 2 - run the client
python client_agent.py
Output:
Connected to: Summarizer Agent
Skills: ['Summarize Text']
--- Agent Response ---
Summary of: Please summarize the following article about c...
• Point 1
• Point 2
• Point 3
Step 5: Add Streaming (for long-running tasks)
# Streaming version in the client
async with client.send_message_streaming(request) as stream:
async for event in stream:
if hasattr(event, 'delta'):
print(event.delta, end="", flush=True)
Step 6: Connect to Google ADK (Production Path)
# With Google's Agent Development Kit (ADK)
from google.adk.agents import LlmAgent
from google.adk.tools.a2a import A2AClient as ADKClient
# Expose your ADK agent as an A2A server
from google.adk.a2a import A2AServer
agent = LlmAgent(model="gemini-2.0-flash", instruction="You are a helpful orchestrator.")
server = A2AServer(agent=agent)
server.serve(port=8000)
Multi-Agent Architecture Patterns
Pattern 1: Hierarchical (Orchestrator → Specialists)
Orchestrator Agent
├── Research Agent (fetches and summarizes information)
├── Writer Agent (drafts documents)
└── Review Agent (quality checks output)
Pattern 2: Peer-to-Peer Pipeline
Intake Agent → Processing Agent → Routing Agent → Fulfillment Agent
Pattern 3: Human-in-the-Loop
When a task hits input-required status, the agent pauses and waits. Your client receives a notification, a human responds, and the task resumes. Built into the protocol — no custom logic needed.
A2A vs. MCP: When to Use What
MCP A2A Purpose Agent ↔ Tool/Data Agent ↔ Agent Analogy USB-C (connects to peripherals) TCP/IP (connects nodes) Use when Giving an agent access to APIs, DBs, files Delegating tasks to another agent Interaction model Request/Response Task lifecycle with state Example Agent queries a database via MCP Agent delegates research to a specialist agent via A2A
Best practice: Use MCP inside each agent for tool access. Use A2A between agents for delegation and collaboration.
Key Takeaways
1. The Agent Card is everything. It’s how agents find each other and negotiate capabilities. Host it at /.well-known/agent.json and keep it accurate.
2. Tasks are stateful, async, and first-class. Design for long-running tasks from day one. Don’t assume request/response.
3. Agents stay opaque. Remote agents never expose their internal state, memory, or tools to client agents. This is a feature, not a limitation — it enables cross-org agent collaboration.
4. You don’t need Google’s stack. A2A is a protocol spec. Any HTTP server that implements it is A2A-compliant. Works with LangChain, CrewAI, LlamaIndex, or raw Python.
5. MCP + A2A = the full stack. MCP gives agents their tools. A2A gives agents their network. Together, they’re the nervous system of multi-agent AI.
6. It’s production-ready. As of v0.3, A2A has gRPC support, signed Agent Cards, enterprise auth, and deploymnet paths on Cloud Run, GKE, and Agent Engine. Real companies (Tyson Foods, Gordon Food Service) are running it in production.
7. It’s open-source and protocol-first. Governed by the Linux Foundation. No vendor lock-in. The spec is the contract.
Resources
Spec & Docs: a2a-protocol.org
GitHub: github.com/a2aproject/A2A
Python SDK:
pip install a2a-sdkGoogle ADK: google.github.io/adk-docs
Quickstart Codelab: Google Codelabs — “Getting Started with Agent2Agent Protocol”
Please show some support 💖: https://www.instagram.com/blurred_ai
Follow me for more such FREE Resources to make your life easier :)


