Building Multi-Agent Systems with A2A Inbound Adapter
What We're Buildingā
In this hands-on guide, we'll build a multi-agent system that exposes specialized Agentverse uAgents through the A2A protocol. By the end, you'll have:
- Multiple A2A endpoints running on different ports
- Specialized agents accessible to A2A clients
- Real-world examples of agent coordination and discovery
- Production-ready configuration for deployment
We'll create two specialized agents:
- Perplexity Search Agent - AI-powered web search and research
- Finance Q&A Agent - Financial analysis and planning assistance
Architectureā

Each agent will run as a separate A2A server, allowing A2A clients to discover and use them independently or in coordination.
š ļø Step 1: Installationā
Install the A2A Inbound adapter package:
pip install "uagents-adapter[a2a-inbound]"
This installs all required dependencies including:
a2a-sdk[all,sqlite]>=0.2.11
- A2A SDK with all features + SQLite supportuvicorn>=0.27.0
- ASGI server for running the A2A bridge serverhttpx>=0.25.0
- Async HTTP clientclick>=8.0.0
- CLI framework for command-line interfacepython-dotenv>=1.0.0
- Environment variable management
Step 2: Choose Your Agentsā
You have two options for agents:
Option A: Use Existing Agents from Agentverse Marketplaceā
- Visit Agentverse.ai
- Browse the marketplace for agents that match your needs
- Copy the agent address from the agent's profile page
- Ensure the agent is active and responding to messages
Option B: Build Your Own Agentsā
- Create uAgents using the uAgents framework
- Register on Agentverse and get them running
- Copy the agent addresses from your agent profiles
- Test that they respond to chat messages
For this tutorial, we'll use pre-configured agents from the marketplace:
- Perplexity Search Agent:
agent1qgzd0c60d4c5n37m4pzuclv5p9vwsftmfkznksec3drux8qnhmvuymsmshp
- Finance Q&A Agent:
agent1qdv2qgxucvqatam6nv28qp202f3pw8xqpfm8man6zyegztuzd2t6yem9evl
Step 3: Build the Multi-Agent Systemā
Agent 1: Perplexity Search Agentā
Create a file called perplexity_adapter.py
:
import os
import sys
import time
from uagents_adapter.a2a_inbound.adapter import A2ARegisterTool
def main():
"""Start A2A bridge for Perplexity Search Agent."""
# Set bridge seed for consistent agent identity (required in production)
os.environ["UAGENTS_BRIDGE_SEED"] = "perplexity_bridge_seed_2024"
# Configure the bridge
config = {
"agent_address": "agent1qgzd0c60d4c5n37m4pzuclv5p9vwsftmfkznksec3drux8qnhmvuymsmshp",
"name": "Perplexity Search Agent",
"description": "AI-powered web search and research assistant with real-time information access",
"skill_tags": ["search", "research", "web", "ai", "information", "news"],
"skill_examples": ["Search for latest AI news", "Research quantum computing trends", "Find information about climate change"],
"port": 9002,
"bridge_port": 8002,
"host": "localhost"
}
# Start the A2A bridge
adapter = A2ARegisterTool()
try:
result = adapter.invoke(config)
if result.get("success"):
# Keep the server running
while True:
time.sleep(1)
else:
print(f"ā Failed to start bridge: {result}")
return 1
except KeyboardInterrupt:
print("\nš Shutting down Perplexity bridge...")
return 0
except Exception as e:
print(f"ā Error: {e}")
return 1
if __name__ == "__main__":
sys.exit(main())
Agent 2: Finance Q&A Agentā
Create a file called finance_adapter.py
:
import os
import sys
import time
from uagents_adapter.a2a_inbound.adapter import A2ARegisterTool
def main():
"""Start A2A bridge for Finance Q&A Agent."""
# Set bridge seed for consistent agent identity (required in production)
os.environ["UAGENTS_BRIDGE_SEED"] = "finance_bridge_seed_2024"
# Configure the bridge
config = {
"agent_address": "agent1qdv2qgxucvqatam6nv28qp202f3pw8xqpfm8man6zyegztuzd2t6yem9evl",
"name": "Finance Q&A Agent",
"description": "AI-powered financial advisor and Q&A assistant for investment, budgeting, and financial planning guidance",
"skill_tags": ["finance", "investment", "budgeting", "financial_planning", "assistance"],
"skill_examples": ["Analyze AAPL stock performance", "Compare crypto portfolios", "Budget planning advice"],
"port": 9003,
"bridge_port": 8003,
"host": "localhost"
}
# Start the A2A bridge
adapter = A2ARegisterTool()
try:
result = adapter.invoke(config)
if result.get("success"):
# Keep the server running
while True:
time.sleep(1)
else:
print(f"ā Failed to start bridge: {result}")
return 1
except KeyboardInterrupt:
print("\nš Shutting down Finance bridge...")
return 0
except Exception as e:
print(f"ā Error: {e}")
return 1
if __name__ == "__main__":
sys.exit(main())
Step 4: Run Your Multi-Agent Systemā
Terminal 1: Start Perplexity Agentā
python perplexity_adapter.py
Expected output:
INFO:root:š Using provided bridge port: 8002
INFO:uagents_adapter.a2a_inbound.agentverse_executor:š Using user-provided bridge seed from environment
INFO: [a2a_agentverse_bridge]: Starting agent with address: agent1qvdrt7kqg2k67czm8wzs724jv63fsq5cr2lq4mymtptj576sdpu9yngzden
INFO:uagents_adapter.a2a_inbound.agentverse_executor:A2A Bridge agent started with address: agent1qvdrt7kqg2k67czm8wzs724jv63fsq5cr2lq4mymtptj576sdpu9yngzden
INFO:uagents_adapter.a2a_inbound.agentverse_executor:Target Agentverse agent: agent1qgzd0c60d4c5n37m4pzuclv5p9vwsftmfkznksec3drux8qnhmvuymsmshp
INFO: [a2a_agentverse_bridge]: Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8002&address=agent1qvdrt7kqg2k67czm8wzs724jv63fsq5cr2lq4mymtptj576sdpu9yngzden
INFO: [a2a_agentverse_bridge]: Starting server on http://0.0.0.0:8002 (Press CTRL+C to quit)
INFO: [a2a_agentverse_bridge]: Starting mailbox client for https://agentverse.ai
INFO: [a2a_agentverse_bridge]: Mailbox access token acquired
INFO: [uagents.registration]: Registration on Almanac API successful
INFO:uagents_adapter.a2a_inbound.agentverse_executor:ā
A2A Bridge to Agentverse started successfully
INFO:root:š A2A server starting on localhost:9002
INFO:root:š Bridging to Agentverse agent: agent1qgzd0c60d4c5n37m4pzuclv5p9vwsftmfkznksec3drux8qnhmvuymsmshp
INFO:root:š Agent name: Perplexity Search Agent
INFO:root:š·ļø Tags: search, research, web, ai, information, news
INFO: Started server process [14746]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://localhost:9002 (Press CTRL+C to quit)
INFO: [uagents.registration]: Almanac contract registration is up to date!
Terminal 2: Start Finance Agentā
python finance_adapter.py
Expected output:
INFO:root:š Using provided bridge port: 8003
INFO:uagents_adapter.a2a_inbound.agentverse_executor:š Using user-provided bridge seed from environment
INFO: [a2a_agentverse_bridge]: Starting agent with address: agent1qdv8n2zucf50mvyzxwswe02swnwzmt5fctyeug6cewdkaz4wjd46qjggnz2
INFO:uagents_adapter.a2a_inbound.agentverse_executor:A2A Bridge agent started with address: agent1qdv8n2zucf50mvyzxwswe02swnwzmt5fctyeug6cewdkaz4wjd46qjggnz2
INFO:uagents_adapter.a2a_inbound.agentverse_executor:Target Agentverse agent: agent1qdv2qgxucvqatam6nv28qp202f3pw8xqpfm8man6zyegztuzd2t6yem9evl
INFO: [a2a_agentverse_bridge]: Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8003&address=agent1qdv8n2zucf50mvyzxwswe02swnwzmt5fctyeug6cewdkaz4wjd46qjggnz2
INFO: [a2a_agentverse_bridge]: Starting server on http://0.0.0.0:8003 (Press CTRL+C to quit)
INFO: [a2a_agentverse_bridge]: Starting mailbox client for https://agentverse.ai
INFO:uagents_adapter.a2a_inbound.agentverse_executor:ā
A2A Bridge to Agentverse started successfully
INFO:root:š A2A server starting on localhost:9003
INFO:root:š Bridging to Agentverse agent: agent1qdv2qgxucvqatam6nv28qp202f3pw8xqpfm8man6zyegztuzd2t6yem9evl
INFO:root:š Agent name: Finance Q&A Agent
INFO:root:š·ļø Tags: finance, investment, budgeting, financial_planning, assistance
INFO: Started server process [14541]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://localhost:9003 (Press CTRL+C to quit)
INFO: [uagents.registration]: Registration on Almanac API successful
INFO: [uagents.registration]: Almanac contract registration is up to date!
INFO: [a2a_agentverse_bridge]: Mailbox access token acquired
š IMPORTANT: Connect Bridge Agents to Mailboxā
For each agent you start, you MUST complete this step:
-
Look for the Inspector Link in the terminal output:
Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8002&address=agent1q...
-
Click the Inspector Link - This will open the Agentverse Agent Inspector in your browser
-
Connect to Mailbox:
- Click the "Connect" button in the Inspector UI
- Select "Mailbox" from the connection options
- Click "Finish" to complete the connection
-
Repeat for Each Agent - You need to do this for both the Perplexity agent (port 8002) and Finance agent (port 8003)
Why This is Required:
- The bridge agents need mailbox access to communicate with your target Agentverse agents
- Without this connection, messages won't reach your target agents
- This establishes the secure communication channel between the bridge and Agentverse
For detailed mailbox connection instructions, refer to: Mailbox Agents Documentation
Success Indicators: After connecting, you should see these log messages:
INFO: [a2a_agentverse_bridge]: Mailbox access token acquired
INFO: [uagents.registration]: Registration on Almanac API successful
Step 5: Test Your Multi-Agent Systemā
Test Agent Discoveryā
Check that both agents are discoverable:
# Discover Perplexity agent
curl http://localhost:9002/.well-known/agent.json
# Discover Finance agent
curl http://localhost:9003/.well-known/agent.json
Test Perplexity Search Agentā
curl -X POST http://localhost:9002 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "What are the latest developments in AI agents?"}],
"messageId": "search-query-1"
},
"contextId": "search-session-123"
},
"id": "search-request-1"
}'
Test Finance Q&A Agentā
curl -X POST http://localhost:9003 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "How should I diversify my investment portfolio?"}],
"messageId": "finance-query-1"
},
"contextId": "finance-session-456"
},
"id": "finance-request-1"
}'
š§ Step 6: Configuration Explainedā
Key Configuration Parametersā
Agent Addressā
"agent_address": "agent1qgzd0c60d4c5n37m4pzuclv5p9vwsftmfkznksec3drux8qnhmvuymsmshp"
- What it is: Unique identifier for your Agentverse uAgent
- How to get it: Copy from Agentverse agent profile page
- Important: Must be an active, running agent
Ports Configurationā
"port": 9002, # A2A server port (what clients connect to)
"bridge_port": 8002, # Internal bridge uAgent port
- A2A Port: Where A2A clients send requests
- Bridge Port: Internal communication with Agentverse
- Rule: Each agent needs unique ports to avoid conflicts
Bridge Seedā
os.environ["UAGENTS_BRIDGE_SEED"] = "perplexity_bridge_seed_2024"
- Purpose: Ensures consistent bridge agent addresses
- Important: Use unique seeds for different agents
- Production: Always set this in production deployments
Agent Metadataā
"name": "Perplexity Search Agent",
"description": "AI-powered web search and research assistant",
"skill_tags": ["search", "research", "web"],
"skill_examples": ["Search for latest AI news", "Research trends"]
- Discovery: Helps A2A clients understand agent capabilities
- Tags: Used for categorization and filtering
- Examples: Show users what queries work well
Step 7: Build an A2A Client for Multi-Agent Coordinationā
Now that your agents are running, let's create a simple A2A client that can discover and coordinate with both agents.
Simple A2A Clientā
Create a2a_client_example.py
:
import asyncio
import json
from uuid import uuid4
from typing import Dict, Any
import httpx
from a2a.client import A2ACardResolver, A2AClient
from a2a.types import (
AgentCard,
MessageSendParams,
SendMessageRequest,
)
class SimpleA2AClient:
"""Simple A2A client for multi-agent coordination."""
def __init__(self):
self.agents: Dict[str, dict] = {}
self.httpx_client = None
async def __aenter__(self):
"""Async context manager entry."""
self.httpx_client = httpx.AsyncClient(timeout=30)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Async context manager exit."""
if self.httpx_client:
await self.httpx_client.aclose()
async def discover_agent(self, url: str, name: str):
"""Discover an agent and add to available agents."""
try:
resolver = A2ACardResolver(self.httpx_client, url)
agent_card = await resolver.get_agent_card()
client = A2AClient(self.httpx_client, agent_card, url=url)
self.agents[name] = {
'card': agent_card,
'client': client,
'url': url
}
print(f"ā
Discovered {name}: {agent_card.name}")
print(f" Description: {agent_card.description}")
print(f" Skills: {[skill.name for skill in agent_card.skills]}")
return True
except Exception as e:
print(f"ā Failed to discover {name} at {url}: {e}")
return False
async def send_to_agent(self, agent_name: str, message: str, context_id: str = None) -> dict:
"""Send a message to a specific agent."""
if agent_name not in self.agents:
raise ValueError(f"Agent {agent_name} not found")
client = self.agents[agent_name]['client']
payload = {
'message': {
'role': 'user',
'parts': [{'kind': 'text', 'text': message}],
'messageId': uuid4().hex,
}
}
if context_id:
payload['message']['contextId'] = context_id
request = SendMessageRequest(
id=str(uuid4()),
params=MessageSendParams(**payload)
)
response = await client.send_message(request)
return response.model_dump(mode='json', exclude_none=True)
def list_agents(self):
"""List all discovered agents."""
print("\nš Available Agents:")
for name, info in self.agents.items():
card = info['card']
print(f" ⢠{name} ({card.name})")
print(f" URL: {info['url']}")
print(f" Description: {card.description}")
print()
async def main():
"""Demonstrate A2A client with multi-agent coordination."""
async with SimpleA2AClient() as client:
print("š Discovering agents...")
# Discover both agents
agents_to_discover = [
("perplexity", "http://localhost:9002"),
("finance", "http://localhost:9003"),
]
for name, url in agents_to_discover:
await client.discover_agent(url, name)
# List discovered agents
client.list_agents()
# Example 1: Single agent query
print("š§Ŗ Example 1: Single Agent Query")
print("-" * 40)
try:
response = await client.send_to_agent(
"perplexity",
"What are the latest trends in sustainable investing?",
context_id="search-session-001"
)
print("Perplexity Response:")
print(json.dumps(response, indent=2))
except Exception as e:
print(f"Error querying Perplexity agent: {e}")
print("\n" + "="*50 + "\n")
# Example 2: Coordinated multi-agent query
print("š§Ŗ Example 2: Multi-Agent Coordination")
print("-" * 40)
# Query both agents with related questions
search_task = client.send_to_agent(
"perplexity",
"Find recent news about ESG investment performance in 2025",
context_id="coordination-001"
)
finance_task = client.send_to_agent(
"finance",
"How should I evaluate ESG investment opportunities for my portfolio?",
context_id="coordination-002"
)
try:
# Wait for both responses
search_result, finance_result = await asyncio.gather(search_task, finance_task)
print("š Search Agent (ESG News):")
print(json.dumps(search_result, indent=2))
print("\n" + "-"*40 + "\n")
print("š° Finance Agent (ESG Evaluation):")
print(json.dumps(finance_result, indent=2))
except Exception as e:
print(f"Error in multi-agent coordination: {e}")
if __name__ == "__main__":
print("š A2A Multi-Agent Client Example")
print("=" * 50)
asyncio.run(main())
Run the A2A Clientā
With both agents running (from Step 4), run the client:
python a2a_client_example.py
Expected Output:
š A2A Multi-Agent Client Example
==================================================
š Discovering agents...
ā
Discovered perplexity: Perplexity Search Agent
Description: AI-powered web search and research assistant with real-time information access
Skills: ['Perplexity Search Agent Bridge']
ā
Discovered finance: Finance Q&A Agent
Description: AI-powered financial advisor and Q&A assistant for investment, budgeting, and financial planning guidance
Skills: ['Finance Q&A Agent Bridge']
š Available Agents:
⢠perplexity (Perplexity Search Agent)
URL: http://localhost:9002
Description: AI-powered web search and research assistant with real-time information access
⢠finance (Finance Q&A Agent)
URL: http://localhost:9003
Description: AI-powered financial advisor and Q&A assistant for investment, budgeting, and financial planning guidance
š§Ŗ Example 1: Single Agent Query
----------------------------------------
Perplexity Response:
{
"jsonrpc": "2.0",
"id": "abc123...",
"result": {
"id": "task-456...",
"contextId": "search-session-001",
"state": "completed",
"artifacts": [
{
"name": "agentverse_result",
"parts": [
{
"text": "Based on current market analysis, sustainable investing trends in 2025 show..."
}
]
}
]
}
}
==================================================
š§Ŗ Example 2: Multi-Agent Coordination
----------------------------------------
š Search Agent (ESG News):
{
"jsonrpc": "2.0",
"result": {
"state": "completed",
"artifacts": [
{
"name": "agentverse_result",
"parts": [
{
"text": "Recent ESG investment performance data shows significant growth..."
}
]
}
]
}
}
----------------------------------------
š° Finance Agent (ESG Evaluation):
{
"jsonrpc": "2.0",
"result": {
"state": "completed",
"artifacts": [
{
"name": "agentverse_result",
"parts": [
{
"text": "To evaluate ESG investments, consider these key criteria: environmental impact metrics..."
}
]
}
]
}
}
š Step 8: Interactive A2A Clientā
For a more interactive experience, create interactive_client.py
:
import asyncio
import json
from uuid import uuid4
import httpx
from a2a.client import A2ACardResolver, A2AClient
from a2a.types import MessageSendParams, SendMessageRequest
async def interactive_client():
"""Interactive A2A client for testing agents."""
agents = {}
async with httpx.AsyncClient(timeout=30) as client:
# Discover agents
agent_configs = [
("Perplexity Search", "http://localhost:9002"),
("Finance Q&A", "http://localhost:9003"),
]
print("š Discovering agents...")
for name, url in agent_configs:
try:
resolver = A2ACardResolver(client, url)
card = await resolver.get_agent_card()
a2a_client = A2AClient(client, card, url=url)
agents[name] = a2a_client
print(f"ā
{name} ready")
except Exception as e:
print(f"ā {name} failed: {e}")
if not agents:
print("No agents available. Make sure they're running!")
return
print(f"\nš Available agents: {list(agents.keys())}")
print("š” Type 'quit' to exit, 'list' to see agents\n")
context_id = str(uuid4())
while True:
try:
# Get user input
user_input = input("You: ").strip()
if user_input.lower() == 'quit':
break
elif user_input.lower() == 'list':
print(f"Available agents: {list(agents.keys())}")
continue
elif not user_input:
continue
# Route based on keywords
agent_name = None
if any(word in user_input.lower() for word in ['search', 'news', 'research', 'find']):
agent_name = "Perplexity Search"
elif any(word in user_input.lower() for word in ['finance', 'money', 'invest', 'stock', 'budget']):
agent_name = "Finance Q&A"
else:
print("š¤ Which agent should I use?")
for i, name in enumerate(agents.keys(), 1):
print(f" {i}. {name}")
choice = input("Choose (1-2): ").strip()
if choice == '1':
agent_name = list(agents.keys())[0]
elif choice == '2':
agent_name = list(agents.keys())[1]
else:
print("Invalid choice!")
continue
# Send to chosen agent
print(f"š¤ Sending to {agent_name}...")
payload = {
'message': {
'role': 'user',
'parts': [{'kind': 'text', 'text': user_input}],
'messageId': uuid4().hex,
'contextId': context_id
}
}
request = SendMessageRequest(
id=str(uuid4()),
params=MessageSendParams(**payload)
)
response = await agents[agent_name].send_message(request)
result = response.model_dump(mode='json', exclude_none=True)
# Extract and display response
if 'result' in result and 'artifacts' in result['result']:
artifacts = result['result']['artifacts']
for artifact in artifacts:
if 'parts' in artifact:
for part in artifact['parts']:
if 'text' in part:
print(f"š¤ {agent_name}: {part['text']}")
else:
print(f"š¤ Raw response: {json.dumps(result, indent=2)}")
print()
except KeyboardInterrupt:
break
except Exception as e:
print(f"ā Error: {e}")
print("š Goodbye!")
if __name__ == "__main__":
asyncio.run(interactive_client())
Run the interactive client:
python interactive_client.py
šÆ Next Stepsā
Scaling Your Systemā
- Add More Agents: Follow the same pattern to add specialized agents
- Load Balancing: Use nginx or similar for distributing requests
- Service Discovery: Implement agent registry for dynamic discovery
- Monitoring: Add health checks and metrics collection
Advanced Featuresā
- Agent Orchestration: Build workflows that coordinate multiple agents
- Context Sharing: Share conversation context between agents
- Fallback Chains: Implement fallback when primary agents are unavailable
- Response Aggregation: Combine responses from multiple agents
Customizationā
- Custom Agents: Build your own specialized uAgents
- Business Logic: Add routing logic for complex queries
- Integration: Connect to external APIs and services
- UI/UX: Build web interfaces for your multi-agent system
š Congratulations!ā
You've successfully built a multi-agent system with:
- ā Two specialized A2A agents running independently
- ā Standard A2A protocol compatibility for client integration
- ā Production-ready configuration with proper environment management
- ā Testing and monitoring capabilities
- ā Coordination patterns for multi-agent workflows
References
This demonstrates how uAgents adapters can bring collaborative A2A Agent systems into a networked environment, making complex workflows accessible through standardized messaging protocols.Now Agentverse uAgents are now accessible to any A2A-compatible client, opening up possibilities for integration with AI assistants, web applications, and other agent systems!