Skip to main content
Version: Next

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 /chat route)
  • 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:

  1. Perplexity Search Agent — web search and research
  2. 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:

PortRoleWho talks to it
port (9002 / 9003)A2A HTTP (JSON-RPC)A2A clients, curl
bridge_port (8002 / 8003)Bridge uAgent + mailboxAgentverse 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.

a2a-inbound-adapter-example

Background: What is A2A? and the inbound adapter README.

Prerequisites

Versions
  • Python 3.10+
  • uagents==0.25.5 (pin used across these docs)
  • uagents-adapter[a2a-inbound]==0.6.2 (pulls a2a-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:

requirements.txt
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:

  1. Create an Agentverse account and (optional) an API key for other Agentverse HTTP APIs: Agentverse API key. AGENTVERSE_API_KEY is not required for this inbound bridge to poll mailbox; use it only if you call Agentverse REST APIs yourself.
  2. After the adapter prints an inspector URL, open it while logged in and choose Connect → Mailbox. See Mailbox agents.
  3. Set a unique UAGENTS_BRIDGE_SEED per 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"
.env.example
# 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

  1. Open Agentverse and browse the marketplace.
  2. Copy the agent address from the profile.
  3. Health check: open Agentverse chat (or ASI:One) and confirm the agent replies before you start a bridge.

Option B: Your own uAgents

  1. Create agents with the uAgents framework.
  2. Register them on Agentverse and keep them running.
  3. 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.

Unique name and seed

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

perplexity_adapter.py
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

finance_adapter.py
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.json
  • http://localhost:9003/.well-known/agent-card.json

Compatibility fallback (PREV_AGENT_CARD_WELL_KNOWN_PATH, older clients):

  • http://localhost:9002/.well-known/agent.json
  • http://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:

VariableUsed byRequired
UAGENTS_BRIDGE_SEEDInbound adapter / bridge identityRecommended (stable address)
AGENTVERSE_API_KEYOther Agentverse REST flows; not mailbox pollOptional
UAGENTS_MAILBOX_KEYNot 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.json and JSON-RPC
  • One process per target agent (this sample is two processes, not a cluster)

Troubleshooting

SymptomWhat to check
404 on /chatUse JSON-RPC POST / with message/send
Mailbox warning / no repliesInspector → Connect → Mailbox; target agent must be active in Agentverse chat
Port already in useFree 9002, 9003, 8002, 8003
Bridge address changes every restartSet UAGENTS_BRIDGE_SEED
Finance logs show Perplexity's agent1qvdrt7k…Wrong seed copied; Finance must be agent1qdv8n2z… with finance_bridge_seed_2024
UnicodeEncodeError on WindowsUTF-8 code page / PYTHONIOENCODING=utf-8
Copy-paste SyntaxError (import osimport)Copy the fenced block with language + title=, not stripped HTML

Next steps

  1. Add more independent adapter processes (new ports, new seeds).
  2. Build an A2A client that calls both endpoints (that is where “coordination” lives — not in these scripts).
  3. For a ready-made inbound planner, clone Multiagent Planner.
  4. Deploy with monitoring on both the A2A port and the bridge port.

Resources