A2A Inbound Adapter Example
This guide runs two independent A2A HTTP endpoints. Each endpoint is a bridge to a different Agentverse uAgent. There is no shared router or coordinator in the sample — A2A clients call each port separately.
By the end you will have:
- A Perplexity search bridge on A2A port 9002 (uAgent bridge port 8002)
- A Finance Q&A bridge on A2A port 9003 (uAgent bridge port 8003)
- Working JSON-RPC test requests (not a
/chatroute) - Agent cards at
/.well-known/agent-card.json
Page id / URL slug: a2a-inbound-adapter-example (this file is a2a-inbound-adapter.md).
What we are building
We'll expose two marketplace agents as A2A servers:
- Perplexity Search Agent — web search and research
- Finance Q&A Agent — financial Q&A
For a coordinator that routes among inbound agents, see Multiagent Planner in innovation-lab-examples. That is a different architecture from this page.
Architecture
Each adapter process starts two listeners:
| Port | Role | Who talks to it |
|---|---|---|
port (9002 / 9003) | A2A HTTP (JSON-RPC) | A2A clients, curl |
bridge_port (8002 / 8003) | Bridge uAgent + mailbox | Agentverse mailbox, inspector |
A2A client → :9002 → bridge uAgent :8002 → Agentverse target. Repeat the same pattern on :9003 / :8003 for Finance. Inspector URLs use the bridge port, not the A2A port.

Background: What is A2A? and the inbound adapter README.
Prerequisites
- Python 3.10+
uagents==0.25.5(pin used across these docs)uagents-adapter[a2a-inbound]==0.6.2(pullsa2a-sdk, uvicorn, httpx, click, python-dotenv)
See the uAgents Adapter guide.
Example clone path for related inbound samples (coordinator + Perplexity/Finance A2A agents, not these two bridge scripts):
git clone https://github.com/fetchai/innovation-lab-examples.git
cd innovation-lab-examples/a2a-uAgents-Integration/a2a-Inbound-Communication/Multiagent-Planner
The two files in this tutorial (perplexity_adapter.py, finance_adapter.py) live in the code blocks below. Pin dependencies with:
uagents==0.25.5
uagents-adapter[a2a-inbound]==0.6.2
python-dotenv>=1.0.0
python --version
pip install -r requirements.txt
PowerShell:
python --version
pip install -r requirements.txt
Step 1: Installation
pip install "uagents==0.25.5" "uagents-adapter[a2a-inbound]==0.6.2"
This extra installs a2a-sdk, uvicorn, httpx, click, and python-dotenv.
Step 2: Agentverse mailbox and environment
The inbound adapter creates a mailbox bridge uAgent (mailbox=True). The mailbox client authenticates with the bridge agent's identity (attestation). It does not read UAGENTS_MAILBOX_KEY (that name is not used by uagents_adapter or the mailbox client).
Do this before you start the Python processes:
- Create an Agentverse account and (optional) an API key for other Agentverse HTTP APIs: Agentverse API key.
AGENTVERSE_API_KEYis not required for this inbound bridge to poll mailbox; use it only if you call Agentverse REST APIs yourself. - After the adapter prints an inspector URL, open it while logged in and choose Connect → Mailbox. See Mailbox agents.
- Set a unique
UAGENTS_BRIDGE_SEEDper bridge so the bridge address is stable. Almanac/agent names must also be unique if you run more than one copy.
# macOS / Linux
export UAGENTS_BRIDGE_SEED="perplexity_bridge_seed_2024"
# Windows PowerShell
$env:UAGENTS_BRIDGE_SEED = "perplexity_bridge_seed_2024"
# Unique per process. Never commit real seeds or keys.
UAGENTS_BRIDGE_SEED=replace_with_your_unique_seed
# Optional: Agentverse REST APIs only (not mailbox polling)
# AGENTVERSE_API_KEY=your_agentverse_api_key
Load .env with python-dotenv as shown in the scripts below.
Step 3: Choose your agents
Two options:
Option A: Marketplace agents
- Open Agentverse and browse the marketplace.
- Copy the agent address from the profile.
- Health check: open Agentverse chat (or ASI:One) and confirm the agent replies before you start a bridge.
Option B: Your own uAgents
- Create agents with the uAgents framework.
- Register them on Agentverse and keep them running.
- Copy addresses and test chat the same way.
This tutorial uses these marketplace addresses as examples. They may go inactive. Prefer placeholders you verified in Option A:
- Perplexity Search:
agent1qgzd0c60d4c5n37m4pzuclv5p9vwsftmfkznksec3drux8qnhmvuymsmshp - Finance Q&A:
agent1qdv2qgxucvqatam6nv28qp202f3pw8xqpfm8man6zyegztuzd2t6yem9evl
Replace YOUR_PERPLEXITY_AGENT_ADDRESS / YOUR_FINANCE_AGENT_ADDRESS in the scripts if those marketplace agents do not respond.
Use a unique agent name and UAGENTS_BRIDGE_SEED for every bridge you run. Shared names collide on Almanac. The two sample seeds below are already different (perplexity_bridge_seed_2024 vs finance_bridge_seed_2024) and produce different bridge addresses.
Step 4: Build the adapter scripts
A2ARegisterTool.invoke() starts uvicorn and blocks until you press Ctrl+C. There is no success while True: time.sleep(1) after a normal start — that loop never runs while the server is up. Match the package README.
Perplexity Search Agent
import logging
import os
import sys
from dotenv import load_dotenv
from uagents_adapter import A2ARegisterTool
load_dotenv()
logging.basicConfig(level=logging.INFO)
def main() -> int:
"""Start A2A bridge for Perplexity Search Agent."""
os.environ.setdefault("UAGENTS_BRIDGE_SEED", "perplexity_bridge_seed_2024")
config = {
"agent_address": os.environ.get(
"TARGET_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",
}
adapter = A2ARegisterTool()
# Blocks in uvicorn.run() until Ctrl+C.
result = adapter.invoke(config)
if result.get("success") is False:
print(f"Failed to start bridge: {result}")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
Finance Q&A Agent
import logging
import os
import sys
from dotenv import load_dotenv
from uagents_adapter import A2ARegisterTool
load_dotenv()
logging.basicConfig(level=logging.INFO)
def main() -> int:
"""Start A2A bridge for Finance Q&A Agent."""
os.environ.setdefault("UAGENTS_BRIDGE_SEED", "finance_bridge_seed_2024")
config = {
"agent_address": os.environ.get(
"TARGET_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",
}
adapter = A2ARegisterTool()
# Blocks in uvicorn.run() until Ctrl+C.
result = adapter.invoke(config)
if result.get("success") is False:
print(f"Failed to start bridge: {result}")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
For DEBUG logs, change logging.basicConfig(level=logging.INFO) to logging.DEBUG in the same file (there is no --verbose flag on A2ARegisterTool.invoke()).
Step 5: Run the adapters
Terminal 1: Perplexity
python perplexity_adapter.py
Expected output (your process id and timestamps will differ). With seed perplexity_bridge_seed_2024 and bridge port 8002, the bridge address is:
agent1qvdrt7kqg2k67czm8wzs724jv63fsq5cr2lq4mymtptj576sdpu9yngzden
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]
Open the inspector URL (bridge port 8002) and connect Mailbox.
Terminal 2: Finance
python finance_adapter.py
With seed finance_bridge_seed_2024 and bridge port 8003, the bridge address is different:
agent1qdv8n2zucf50mvyzxwswe02swnwzmt5fctyeug6cewdkaz4wjd46qjggnz2
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: 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: agent1qdv2qgxucvqatam6nv28qp202f3pw8xqpfm8man6zyegztuzd2t6yem9evl
INFO: [a2a_agentverse_bridge]: Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8003&address=agent1qvdrt7kqg2k67czm8wzs724jv63fsq5cr2lq4mymtptj576sdpu9yngzden
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: [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: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 [14747]
Connect Mailbox on the Finance inspector URL (bridge port 8003).
Step 6: Test with A2A JSON-RPC
This adapter serves A2AStarletteApplication (JSON-RPC on /). There is no POST /chat route. Do not send { "message", "session_id" }.
Use message/send as in the inbound adapter README and A2A protocol docs.
JSON-RPC over HTTP (macOS / Linux):
curl -X POST http://localhost:9002/ \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"test-1","method":"message/send","params":{"message":{"role":"user","parts":[{"kind":"text","text":"What are the latest developments in quantum computing?"}],"messageId":"msg-1"},"contextId":"test_session_1"}}'
curl -X POST http://localhost:9003/ \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"test-2","method":"message/send","params":{"message":{"role":"user","parts":[{"kind":"text","text":"What should I consider when investing in tech stocks?"}],"messageId":"msg-2"},"contextId":"test_session_2"}}'
Single-line fallback (Windows PowerShell — use curl.exe so it is not Invoke-WebRequest):
curl.exe -X POST http://localhost:9002/ -H "Content-Type: application/json" -d "{\"jsonrpc\":\"2.0\",\"id\":\"test-1\",\"method\":\"message/send\",\"params\":{\"message\":{\"role\":\"user\",\"parts\":[{\"kind\":\"text\",\"text\":\"What are the latest developments in quantum computing?\"}],\"messageId\":\"msg-1\"},\"contextId\":\"test_session_1\"}}"
curl.exe -X POST http://localhost:9003/ -H "Content-Type: application/json" -d "{\"jsonrpc\":\"2.0\",\"id\":\"test-2\",\"method\":\"message/send\",\"params\":{\"message\":{\"role\":\"user\",\"parts\":[{\"kind\":\"text\",\"text\":\"What should I consider when investing in tech stocks?\"}],\"messageId\":\"msg-2\"},\"contextId\":\"test_session_2\"}}"
REST POST /v1/message:send exists on a2a-sdk REST apps. This inbound adapter uses JSON-RPC Starlette, so use POST / + method: "message/send" as above.
Step 7: Discover agent cards
Current A2A SDK well-known path (AGENT_CARD_WELL_KNOWN_PATH):
http://localhost:9002/.well-known/agent-card.jsonhttp://localhost:9003/.well-known/agent-card.json
Compatibility fallback (PREV_AGENT_CARD_WELL_KNOWN_PATH, older clients):
http://localhost:9002/.well-known/agent.jsonhttp://localhost:9003/.well-known/agent.json
curl http://localhost:9002/.well-known/agent-card.json
curl http://localhost:9003/.well-known/agent-card.json
curl.exe http://localhost:9002/.well-known/agent-card.json
curl.exe http://localhost:9003/.well-known/agent-card.json
Point any A2A client at those URLs. There is still no shared discovery/coordination between the two bridges unless you add a client that calls both.
Production
Verified environment variables for this adapter:
| Variable | Used by | Required |
|---|---|---|
UAGENTS_BRIDGE_SEED | Inbound adapter / bridge identity | Recommended (stable address) |
AGENTVERSE_API_KEY | Other Agentverse REST flows; not mailbox poll | Optional |
UAGENTS_MAILBOX_KEY | Not used — do not set this expecting mailbox auth | — |
macOS / Linux:
export UAGENTS_BRIDGE_SEED="your_secure_unique_seed"
# optional
export AGENTVERSE_API_KEY="your_agentverse_api_key"
Windows PowerShell:
$env:UAGENTS_BRIDGE_SEED = "your_secure_unique_seed"
# optional
$env:AGENTVERSE_API_KEY = "your_agentverse_api_key"
Mailbox setup remains: inspector Connect → Mailbox. See Mailbox agents.
Security
- Unique seeds and names per bridge
- Do not commit seeds or API keys
- Restrict who can reach the A2A HTTP ports
Scaling
- Reverse proxy in front of A2A ports
- Health-check
/.well-known/agent-card.jsonand JSON-RPC - One process per target agent (this sample is two processes, not a cluster)
Troubleshooting
| Symptom | What to check |
|---|---|
404 on /chat | Use JSON-RPC POST / with message/send |
| Mailbox warning / no replies | Inspector → Connect → Mailbox; target agent must be active in Agentverse chat |
| Port already in use | Free 9002, 9003, 8002, 8003 |
| Bridge address changes every restart | Set UAGENTS_BRIDGE_SEED |
Finance logs show Perplexity's agent1qvdrt7k… | Wrong seed copied; Finance must be agent1qdv8n2z… with finance_bridge_seed_2024 |
UnicodeEncodeError on Windows | UTF-8 code page / PYTHONIOENCODING=utf-8 |
Copy-paste SyntaxError (import osimport) | Copy the fenced block with language + title=, not stripped HTML |
Next steps
- Add more independent adapter processes (new ports, new seeds).
- Build an A2A client that calls both endpoints (that is where “coordination” lives — not in these scripts).
- For a ready-made inbound planner, clone Multiagent Planner.
- Deploy with monitoring on both the A2A port and the bridge port.