Detailed Notes on A2A Protocol
A2A was introduced by Google in April 2025 at Google Cloud Next, with contributions from over 50 partners like Atlassian, Cohere, and SAP. A2A emphasizes peer-to-peer agent interactions for complex, multi-agent workflows.
Definition
-
The Agent-to-Agent (A2A) Protocol addresses the fragmentation in AI agent development by providing a universal framework for agents—built on diverse platforms like LangGraph, CrewAI, or Google's Agent Development Kit (ADK)—to discover each other, exchange information, delegate tasks, and coordinate actions.
-
A2A treats agents as independent entities capable of long-running, asynchronous collaborations, including human-in-the-loop scenarios. Unlike traditional APIs or tool-calling protocols, A2A supports true multi-agent systems where agents can negotiate user experiences, handle multimodal data (text, images, audio), and manage task lifecycles without relying on a central orchestrator. This makes it ideal for "agentic" AI, where agents act proactively and collaboratively to solve complex problems.
Components
A2A's design revolves around modular, extensible elements that enable flexible agent interactions. Key components include:
- Agent Card:
- Explanation: The Agent Card is a JSON metadata document that serves as an agent's "business card," describing its identity, capabilities, skills, authentication requirements, and interaction preferences. It's crucial for discovery, allowing other agents to understand what an agent can do before initiating communication. Cards are typically hosted at a well-known URI like
https://<base_url>/.well-known/agent-card.json(following RFC 8615 standards) or shared via registries, catalogs, or direct configuration. Public cards provide basic info, while authenticated extended cards offer detailed, secure capabilities. This enables dynamic, decentralized agent networks, potentially integrated with P2P systems or blockchains for advanced discovery. - Role in Workflow: Clients fetch the card via HTTP GET to learn about supported transports (e.g., JSON-RPC, REST), authentication schemes (e.g., Bearer, OAuth), and features like streaming. Cards can include flags for capabilities (e.g., pushNotifications) and skills (e.g., "image_analysis" with input/output MIME types).
-
Python/JSON Example: Below is a Python dictionary representing a sample Agent Card schema, adapted from the official specification. In practice, you'd serialize this to JSON and host it.
```python agent_card = { "name": "Recipe Agent", "description": "An agent that generates recipes based on ingredients", "url": "https://recipe-agent.example.com", "provider": { "organization": "FoodAI Inc.", "url": "https://foodai.com" }, "version": "1.0.0", "documentationUrl": "https://docs.recipe-agent.example.com", "capabilities": { "streaming": True, "pushNotifications": True, "stateTransitionHistory": False }, "authentication": { "schemes": ["Bearer"], "credentials": "Optional API key for premium features" }, "defaultInputModes": ["text/plain", "application/json"], "defaultOutputModes": ["text/plain", "application/json"], "skills": [ { "id": "generate_recipe", "name": "Generate Recipe", "description": "Creates a recipe from given ingredients", "tags": ["cooking", "food"], "examples": ["Generate a recipe using chicken, rice, and vegetables"], "inputModes": ["text/plain"], "outputModes": ["application/json"] } ] }
To serialize and save/host:
import json with open("agent-card.json", "w") as f: json.dump(agent_card, f, indent=4) ```
This example shows basic fields; extended cards might include more secure details fetched after authentication.
-
Task:
- Explanation: The core unit of work in A2A, a Task is a stateful object representing a request from one agent to another. It has a lifecycle (e.g., submitted, running, input-required, completed, failed) and supports long-running operations with human intervention. Tasks are identified by unique IDs and can include context IDs for threading related interactions.
-
Python/JSON Example: A sample Task request in JSON-RPC format:
python task_request = { "jsonrpc": "2.0", "id": 1, "method": "task/create", "params": { "contextId": "session-123", "message": { "role": "user", "parts": [{"kind": "text", "text": "Generate a recipe for pasta"}] } } } -
Message:
-
Explanation: A communication unit between agents, containing a role ("user" or "agent") and one or more Parts (e.g., text, files, data). Messages facilitate collaboration, such as clarifying tasks or sharing updates.
-
Artifact:
-
Explanation: The output of a completed Task, composed of Parts (e.g., a generated image or JSON data). Artifacts support multimodal results.
-
Streaming (SSE) and Push Notifications:
- Explanation: SSE provides real-time updates from server to client (e.g., task progress). Push Notifications use webhooks for async updates, ideal for long-running tasks.
Architecture
A2A's architecture is decentralized and web-native, leveraging existing standards for broad compatibility. It's a client-server model where any agent can act as both client (initiating tasks) and server (fulfilling them), promoting peer-to-peer networks.
- Layers:
- Transport Layer: HTTP/HTTPS for core communication, with SSE for streaming and webhooks for notifications. Supports JSON-RPC 2.0 for structured calls; REST variants are possible but must align with RPC behavior.
- Discovery Layer: Agent Cards and registries (public/private) for finding agents. No dynamic negotiation; clients select based on cards.
- Security Layer: Aligns with OpenAPI schemes (e.g., OAuth, Bearer tokens). Agents declare schemes in cards; clients handle credentials out-of-band.
-
Interaction Layer: Task-oriented, with messages for collaboration. Supports async patterns for enterprise scalability.
-
Key Principles: Async-first for long tasks; simple reuse of standards; enterprise-ready with tracing/monitoring; flexible for multimodal UX negotiation.
How It Works
- Discovery: Client fetches Agent Card (e.g., via GET /.well-known/agent-card.json) to learn capabilities and auth.
- Authentication: Client obtains tokens (e.g., OAuth) and authenticates requests.
- Task Creation: Client sends JSON-RPC request (e.g., "task/create") with Message params; server creates Task and assigns ID.
- Collaboration: Agents exchange Messages; server updates via SSE or webhooks.
- Completion: Server returns Artifact; Task state changes to "completed."
Python Code Demonstration: Simple A2A Client-Server Interaction
Adapted from GitHub repos like a2aproject/A2A and sap156/Agent-to-Agent-A2A-Protocol-Implementation. This uses FastAPI for the server and requests for the client, simulating task creation and SSE updates.
Server Code (a2a_server.py):
from fastapi import FastAPI, Response
from pydantic import BaseModel
import uvicorn
import json
import time
from sse_starlette.sse import EventSourceResponse
app = FastAPI(title="Simple A2A Server")
# Agent Card Endpoint
@app.get("/.well-known/agent-card.json")
def get_agent_card():
return {
"name": "Task Agent",
"description": "Handles simple tasks",
"url": "http://localhost:8000",
"capabilities": {"streaming": True},
"skills": [{"id": "process_task", "name": "Process Task", "description": "Processes a text task"}]
}
class TaskRequest(BaseModel):
message: dict
# Task Creation (JSON-RPC style)
@app.post("/task/create")
def create_task(request: dict):
# Simulate task creation
task_id = "task-123"
return {"jsonrpc": "2.0", "result": {"id": task_id, "status": {"state": "submitted"}}}
# SSE for Updates
@app.get("/task/{task_id}/stream")
async def stream_task(task_id: str):
async def event_generator():
for state in ["running", "completed"]:
yield json.dumps({"status": {"state": state}})
time.sleep(2)
return EventSourceResponse(event_generator())
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Client Code (a2a_client.py):
import requests
import sseclient
SERVER_URL = "http://localhost:8000"
# Fetch Agent Card
card_response = requests.get(f"{SERVER_URL}/.well-known/agent-card.json")
print("Agent Card:", card_response.json())
# Create Task
task_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "task/create",
"params": {"message": {"role": "user", "parts": [{"kind": "text", "text": "Process this"}]}}
}
create_response = requests.post(f"{SERVER_URL}/task/create", json=task_request)
task_id = create_response.json()["result"]["id"]
print("Task Created:", task_id)
# Stream Updates
stream_response = requests.get(f"{SERVER_URL}/task/{task_id}/stream", stream=True)
client = sseclient.SSEClient(stream_response)
for event in client.events():
print("Update:", event.data)
Run server first, then client to see discovery, task creation, and streaming.
Advantages
- Interoperability: Agents from different vendors collaborate seamlessly.
- Asynchronous Support: Handles long tasks with real-time updates.
- Decentralized: No central hub; enables marketplaces and P2P networks.
- Secure and Enterprise-Ready: Built-in auth, tracing, and privacy.
Disadvantages
- Complexity: Dynamic discovery and async handling require robust implementation.
- Adoption Curve: As a new standard, ecosystem maturity is evolving.
- Overhead for Simple Tasks: Better for multi-agent than single-agent scenarios.
Use Cases
- E-Commerce Concierge: A purchasing agent delegates to seller agents (e.g., burger/pizza) via A2A for order coordination.
- Enterprise Workflows: HR agents collaborating with finance agents for payroll.
- Research Collaboration: Agents sharing data/analysis in real-time.
- Multi-Vendor Integration: Agents from Google, SAP, and Salesforce coordinating via A2A.