Lab 054: A2A Protocol β Build Interoperable Multi-Agent SystemsΒΆ
The Three Agentic Protocols
A2A is one of three open protocols for the agentic era: MCP (agentβtools, Lab 012), A2A (agentβagent, this lab), and AG-UI (agentβuser, Lab 077). Together they form the complete interoperability stack.
What You'll LearnΒΆ
- What the A2A (Agent-to-Agent) protocol is β JSON-RPC 2.0 over HTTPS, governed by the Linux Foundation
- How Agent Cards work as the discovery mechanism for agent capabilities
- How to programmatically parse Agent Cards and inspect skills, streaming, and pushNotifications
- The key differences between A2A (peer-to-peer agent communication) and MCP (agent-to-tool access)
IntroductionΒΆ
Modern AI systems rarely consist of a single agent. Real-world solutions require multiple specialized agents that discover each other, negotiate capabilities, and collaborate on tasks. The Agent-to-Agent (A2A) protocol standardizes this communication.
A2A and MCP solve different problems:
| Protocol | Purpose | Direction |
|---|---|---|
| A2A | Agent β Agent communication | Peer-to-peer β agents delegate tasks to other agents |
| MCP | Agent β Tool access | Client-server β agents call tools, databases, APIs |
Think of A2A as agents talking to each other, and MCP as agents using tools. A complete multi-agent system typically uses both protocols.
The ScenarioΒΆ
You are an Integration Architect at OutdoorGear Inc. The company operates 3 specialized agents:
- ProductSearchAgent β searches the product catalog by category, price, and availability
- OrderManagementAgent β manages orders, returns, and shipping updates
- CustomerSupportAgent β handles inquiries, complaints, and FAQ responses
Each agent publishes an Agent Card β a JSON document describing its identity, capabilities, skills, and authentication requirements. Your job is to load these cards, analyze what each agent can do, and understand how A2A enables them to discover and collaborate with each other.
A2A Protocol Essentials
A2A uses JSON-RPC 2.0 over HTTPS as its transport layer. Each agent publishes an Agent Card at a well-known URL (typically /.well-known/agent.json). Client agents discover available agents by fetching these cards, inspecting their skills, and sending task requests using the JSON-RPC protocol.
PrerequisitesΒΆ
| Requirement | Why |
|---|---|
| Python 3.10+ | Parse and analyze Agent Cards |
json (built-in) |
Load agent card data |
No external packages are required β this lab uses only the Python standard library.
π¦ Supporting FilesΒΆ
Download these files before starting the lab
Save all files to a lab-054/ folder in your working directory.
| File | Description | Download |
|---|---|---|
agent_cards.json |
Configuration / data file | π₯ Download |
broken_a2a.py |
Bug-fix exercise (3 bugs + self-tests) | π₯ Download |
Step 1: Understanding the A2A ProtocolΒΆ
A2A defines a standard for peer-to-peer agent communication. The protocol specifies:
| Concept | Description |
|---|---|
| Agent Card | JSON document advertising an agent's identity, URL, capabilities, skills, and auth |
| Skills | Named operations an agent can perform (e.g., search_products, track_order) |
| Capabilities | Feature flags: streaming, pushNotifications, stateTransitionHistory |
| Task | A unit of work sent from one agent to another via JSON-RPC 2.0 |
| Artifact | The result returned by an agent after completing a task |
Agent Card StructureΒΆ
{
"name": "ProductSearchAgent",
"description": "Searches the OutdoorGear product catalog",
"url": "https://agents.outdoorgear.com/product-search",
"version": "1.0.0",
"provider": "OutdoorGear Inc.",
"capabilities": {
"streaming": true,
"pushNotifications": false,
"stateTransitionHistory": false
},
"skills": [
{"id": "search_products", "name": "Search Products", "description": "Search by category, price, and stock"}
],
"authentication": {"type": "bearer", "required": true}
}
A2A Communication FlowΒΆ
βββββββββββββββ 1. Fetch Agent Card βββββββββββββββββββ
β Client β ββββββββββββββββββββββββΊ β Remote Agent β
β Agent β 2. Inspect skills β (Agent Card) β
β β ββββββββββββββββββββββββ β β
β β 3. Send JSON-RPC task β β
β β ββββββββββββββββββββββββΊ β β
β β 4. Receive artifact β β
β β ββββββββββββββββββββββββ β β
βββββββββββββββ βββββββββββββββββββ
Step 2: Load Agent CardsΒΆ
Load the three OutdoorGear agent cards from the JSON file:
import json
with open("lab-054/agent_cards.json") as f:
cards = json.load(f)
print(f"Total agents: {len(cards)}")
for card in cards:
print(f" β’ {card['name']} (v{card['version']}) β {card['description']}")
Expected output:
Total agents: 3
β’ ProductSearchAgent (v1.0.0) β Searches the OutdoorGear product catalog by category, price range, and availability
β’ OrderManagementAgent (v2.1.0) β Manages customer orders including status tracking, returns, and shipping updates
β’ CustomerSupportAgent (v1.3.0) β Handles customer inquiries, complaints, and FAQ responses with sentiment awareness
Step 3: Analyze Agent CapabilitiesΒΆ
Parse each agent's capabilities, skills, and authentication to build a discovery summary:
3a β Skills InventoryΒΆ
total_skills = 0
for card in cards:
skills = card["skills"]
total_skills += len(skills)
print(f"\n{card['name']} β {len(skills)} skill(s):")
for skill in skills:
print(f" β’ {skill['name']}: {skill['description']}")
print(f"\nTotal skills across all agents: {total_skills}")
Expected output:
ProductSearchAgent β 2 skill(s):
β’ Search Products: Search by category, price, and stock
β’ Get Product Details: Retrieve full specs for a product ID
OrderManagementAgent β 3 skill(s):
β’ Track Order: Get real-time order status
β’ Process Return: Initiate a product return
β’ Update Shipping: Change shipping address or speed
CustomerSupportAgent β 2 skill(s):
β’ Answer FAQ: Respond to common questions
β’ Handle Complaint: Process and resolve complaints
Total skills across all agents: 7
3b β Capability FlagsΒΆ
print("Agent Capabilities Matrix:")
print(f"{'Agent':<25} {'Streaming':<12} {'Push':<12} {'History':<12}")
print("-" * 61)
for card in cards:
caps = card["capabilities"]
print(f"{card['name']:<25} {str(caps['streaming']):<12} "
f"{str(caps['pushNotifications']):<12} "
f"{str(caps['stateTransitionHistory']):<12}")
push_count = sum(1 for c in cards if c["capabilities"]["pushNotifications"])
print(f"\nAgents supporting pushNotifications: {push_count}")
Expected output:
Agent Capabilities Matrix:
Agent Streaming Push History
-------------------------------------------------------------
ProductSearchAgent True False False
OrderManagementAgent False True True
CustomerSupportAgent True True False
Agents supporting pushNotifications: 2
3c β Authentication TypesΒΆ
auth_types = sorted(set(c["authentication"]["type"] for c in cards))
print(f"Authentication types used: {auth_types}")
for card in cards:
auth = card["authentication"]
print(f" {card['name']}: {auth['type']} (required={auth['required']})")
Expected output:
Authentication types used: ['bearer', 'oauth2']
ProductSearchAgent: bearer (required=True)
OrderManagementAgent: bearer (required=True)
CustomerSupportAgent: oauth2 (required=True)
Step 4: A2A vs MCP β Protocol ComparisonΒΆ
Understanding when to use each protocol is essential for multi-agent architecture:
| Dimension | A2A | MCP |
|---|---|---|
| Purpose | Agent-to-agent task delegation | Agent-to-tool access |
| Transport | JSON-RPC 2.0 over HTTPS | JSON-RPC 2.0 over stdio/SSE |
| Discovery | Agent Cards at /.well-known/agent.json |
Tool manifests in MCP server |
| Direction | Peer-to-peer (bidirectional) | Client β Server (unidirectional) |
| Auth | OAuth 2.0, bearer tokens | Server-defined (API keys, OAuth) |
| Governance | Linux Foundation | Anthropic (open standard) |
| Use case | "Ask another agent to do something" | "Call a tool / read a resource" |
When to Use WhichΒΆ
Customer asks: "Find me a waterproof tent under $200 and track my last order"
ββββββββββββββββββββ
β Coordinator β
β Agent β
ββββββββ¬ββββββββββββ
β
ββββββββββββββΌβββββββββββββ
A2A β A2A β β A2A
βΌ βΌ βΌ
ββββββββββββββββ ββββββββββββ ββββββββββββββββ
β ProductSearch β β Order β β Customer β
β Agent β β Mgmt β β Support β
ββββββββ¬ββββββββ ββββββ¬ββββββ ββββββββ¬ββββββββ
MCP β MCP β MCP β
βΌ βΌ βΌ
ββββββββββββββββ ββββββββββββ ββββββββββββββββ
β Catalog DB β β Order DB β β FAQ KB β
β (MCP Server) β β (MCP) β β (MCP Server) β
ββββββββββββββββ ββββββββββββ ββββββββββββββββ
- A2A connects the Coordinator to specialized agents (peer-to-peer delegation)
- MCP connects each agent to its back-end tools and data sources
Step 5: Build a Mock A2A Request/Response FlowΒΆ
Simulate how a client agent discovers and communicates with a remote agent using A2A:
import json
def discover_agent(cards, skill_id):
"""Find an agent that has the requested skill."""
for card in cards:
for skill in card["skills"]:
if skill["id"] == skill_id:
return card
return None
def build_a2a_request(card, skill_id, params):
"""Build a JSON-RPC 2.0 request for an A2A task."""
return {
"jsonrpc": "2.0",
"method": "tasks/send",
"id": "req-001",
"params": {
"id": "task-001",
"message": {
"role": "user",
"parts": [{"type": "text", "text": json.dumps(params)}]
},
"metadata": {
"target_agent": card["name"],
"skill": skill_id
}
}
}
def mock_a2a_response(request):
"""Simulate an A2A response."""
return {
"jsonrpc": "2.0",
"id": request["id"],
"result": {
"id": request["params"]["id"],
"status": {"state": "completed"},
"artifacts": [
{
"parts": [{"type": "text", "text": "Found 3 matching products"}]
}
]
}
}
# Discovery: find who can search products
agent = discover_agent(cards, "search_products")
print(f"Discovered agent: {agent['name']} at {agent['url']}")
# Build request
request = build_a2a_request(agent, "search_products", {"category": "tents", "max_price": 200})
print(f"\nA2A Request:\n{json.dumps(request, indent=2)}")
# Get response
response = mock_a2a_response(request)
print(f"\nA2A Response:\n{json.dumps(response, indent=2)}")
Expected output:
Discovered agent: ProductSearchAgent at https://agents.outdoorgear.com/product-search
A2A Request:
{
"jsonrpc": "2.0",
"method": "tasks/send",
"id": "req-001",
"params": {
"id": "task-001",
"message": {
"role": "user",
"parts": [{"type": "text", "text": "{\"category\": \"tents\", \"max_price\": 200}"}]
},
"metadata": {
"target_agent": "ProductSearchAgent",
"skill": "search_products"
}
}
}
A2A Response:
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"id": "task-001",
"status": {"state": "completed"},
"artifacts": [
{
"parts": [{"type": "text", "text": "Found 3 matching products"}]
}
]
}
}
π Bug-Fix ExerciseΒΆ
The file lab-054/broken_a2a.py has 3 bugs in the Agent Card parser. Can you find and fix them all?
Run the self-tests to see which ones fail:
You should see 3 failed tests. Each test corresponds to one bug:
| Test | What it checks | Hint |
|---|---|---|
| Test 1 | Total skill count across all agents | Should sum skills from all cards, not just the first |
| Test 2 | Agents supporting push notifications | Check pushNotifications, not streaming |
| Test 3 | Authentication types | Return the type field (string), not the required field (bool) |
Fix all 3 bugs, then re-run. When you see π All 3 tests passed, you're done!
π§ Knowledge CheckΒΆ
Q1 (Multiple Choice): What transport mechanism does the A2A protocol use?
- A) gRPC over HTTP/2
- B) JSON-RPC 2.0 over HTTPS
- C) GraphQL over WebSocket
- D) REST over HTTP with OpenAPI
β Reveal Answer
Correct: B) JSON-RPC 2.0 over HTTPS
The A2A protocol uses JSON-RPC 2.0 over HTTPS as its transport layer. This provides a standardized request/response format with method names, parameters, and error codes β all transmitted securely over HTTPS.
Q2 (Multiple Choice): What is the primary purpose of an Agent Card in A2A?
- A) Storing the agent's conversation history
- B) Capability discovery β advertising what an agent can do
- C) Encrypting messages between agents
- D) Rate-limiting incoming requests
β Reveal Answer
Correct: B) Capability discovery β advertising what an agent can do
An Agent Card is a JSON document published at a well-known URL that describes an agent's identity, skills, capabilities (streaming, push notifications), and authentication requirements. Client agents fetch these cards to discover what remote agents can do before sending task requests.
Q3 (Run the Lab): How many total skills exist across all 3 OutdoorGear agents?
Sum the skills arrays from all agent cards in π₯ agent_cards.json.
β Reveal Answer
7
ProductSearchAgent has 2 skills (search_products, get_details), OrderManagementAgent has 3 skills (track_order, process_return, update_shipping), and CustomerSupportAgent has 2 skills (answer_faq, handle_complaint). Total: 2 + 3 + 2 = 7.
Q4 (Run the Lab): How many agents support pushNotifications?
Check the capabilities.pushNotifications field in each agent card.
β Reveal Answer
2
OrderManagementAgent (pushNotifications: true) and CustomerSupportAgent (pushNotifications: true) support push notifications. ProductSearchAgent does not (pushNotifications: false).
Q5 (Run the Lab): What authentication types are used across the 3 agents?
Inspect the authentication.type field in each card and collect the unique values.
β Reveal Answer
bearer and oauth2
ProductSearchAgent and OrderManagementAgent use bearer token authentication. CustomerSupportAgent uses oauth2. The two unique auth types are bearer and oauth2.
SummaryΒΆ
| Topic | What You Learned |
|---|---|
| A2A Protocol | JSON-RPC 2.0 over HTTPS for peer-to-peer agent communication |
| Agent Cards | JSON documents for capability discovery (skills, capabilities, auth) |
| Skills & Capabilities | How agents advertise what they can do and what features they support |
| A2A vs MCP | A2A for agentβagent delegation; MCP for agentβtool access |
| Discovery Flow | Fetch Agent Card β Inspect skills β Send task β Receive artifact |