Adding Agent Discovery and Async Communication to A2A with mq9
Background
A2A (Agent2Agent) is an open protocol launched by Google, designed to let agents built by different frameworks and different companies discover and call each other. It's now a Linux Foundation project with 100+ companies behind it.
The standard A2A flow works like this:
- An agent starts an HTTP server and exposes its AgentCard at
/.well-known/agent.json - Other agents fetch the AgentCard over HTTP to discover its capabilities and address
- Tasks are sent over HTTP, with synchronous waiting for results
This mechanism solves agent interoperability, but has two limitations:
Discovery depends on HTTP: Every agent must have a reachable HTTP address. Internal agents need network connectivity to be discoverable; if an agent is offline, it can't be found. There's no standard "how do I find what agents exist" mechanism — addresses need to be known in advance or passed out of band.
Communication is synchronous: A2A's HTTP calls require the receiver to be online. If the receiver restarts, the network blips, or tasks back up, messages are lost.
mq9 fills both gaps without doing anything extra.
What mq9 Adds
mq9 is a messaging protocol designed specifically for async agent communication. Its core concept is the Mailbox. Each agent has a Mailbox; messages are persisted; sender and receiver don't need to be online at the same time.
For A2A, mq9 contributes two things:
Agent discovery: When an agent starts, it publishes its AgentCard to a public Mailbox. Other agents subscribe to that Mailbox and automatically discover all registered agents. No HTTP server needed. No registry needed. Just connect to the same mq9 broker.
Async communication: Once an agent is discovered, communication can go through the mq9 async channel instead of a synchronous HTTP call. Messages are persisted — the receiver being offline doesn't cause loss.
Both are already built into mq9. No extra development required.
Deploying Inside a Company
Deploy one RobustMQ instance on the internal network and you have a complete agent infrastructure:

All agents just connect to this broker. No agent needs to know another agent's address. No HTTP ports need to be exposed.
Agent Registration
When an agent starts, it creates a public Mailbox. The name is its mq9 address; desc is the A2A AgentCard serialized as JSON. One call handles registration. $SYSTEM.PUBLIC is a system Mailbox built into the broker — any Mailbox created with public=True is automatically included, no extra steps needed.
import json
from robustmq.mq9 import Client
BROKER = "nats://robustmq.internal:4222"
agent_card = {
"name": "DataAnalysisAgent",
"url": "https://agent-b.internal", # HTTP address, optional
"version": "1.0.0",
"skills": [
{
"id": "analyze",
"name": "Analyze Data",
"description": "Analyzes data and returns an insights report"
}
]
}
async def register():
async with Client(BROKER) as client:
# public=True: automatically registered to $SYSTEM.PUBLIC
# name: other agents use this to send messages to you
# desc: AgentCard JSON, readable by subscribers of $SYSTEM.PUBLIC
await client.create(
ttl=86400,
public=True,
name="data-analysis-agent",
desc=json.dumps(agent_card)
)
print("Agent registered, mq9 address: data-analysis-agent")Agent Discovery: Subscribe to $SYSTEM.PUBLIC
Other agents subscribe to $SYSTEM.PUBLIC and receive all registered agents in real time. mq9's store-first feature ensures that subscribers receive all historically registered agents — agents that started before you won't be missed.
async def discover_agents():
async with Client(BROKER) as client:
agents = {}
async def on_agent_registered(msg):
# desc field is the AgentCard JSON
card = json.loads(msg.desc)
# mail_id is the agent's mq9 address, use it directly to send messages
mq9_addr = msg.mail_id
agents[mq9_addr] = card
print(f"Discovered agent: {card['name']}")
print(f" mq9 address: {mq9_addr}")
print(f" Skills: {[s['name'] for s in card['skills']]}")
if "url" in card:
print(f" HTTP address: {card['url']}")
await client.subscribe("$SYSTEM.PUBLIC", on_agent_registered)
await asyncio.sleep(2)
return agentsCommunication: Each Agent Decides
After discovery, an agent has two addresses for its peer:
- mail_id: When a publisher posts an AgentCard, the message naturally carries the publisher's mail_id — the subscriber already knows the sender's mq9 address
- url: The standard A2A HTTP address in the AgentCard (optional — an agent doesn't have to run an HTTP server)
Which channel to use is up to the agent. Two options:
mq9 async channel: Receiver doesn't need to be online; messages are persisted. Good for long-running tasks or when you're not sure if the receiver is available.
async def send_task_via_mq9(sender_mail_id: str, target_mail_id: str, task: dict):
async with Client(BROKER) as client:
task["reply_to"] = sender_mail_id # tell the receiver where to send the result
await client.send(
target_mail_id,
json.dumps(task).encode(),
priority=Priority.NORMAL
)
print("Task sent, continuing with other work")A2A standard HTTP: Synchronous call, receiver must be online. Good for cases where you need the result immediately.
from a2a.client import A2AClient
import httpx
async def send_task_via_http(url: str, message: str):
async with httpx.AsyncClient() as http_client:
client = await A2AClient.get_client_from_agent_card_url(
http_client, url
)
response = await client.send_message(message)
print(f"Response received: {response}")These are two different options, not replacements for each other. Agents choose based on their use case and can support both simultaneously.
Full Flow

Comparison
| A2A Standard HTTP | A2A + mq9 | |
|---|---|---|
| Discovery | Know the HTTP address in advance | Subscribe to public Mailbox, auto-discover |
| HTTP server required | Yes | No (optional) |
| Receiver offline | Message lost | Message persisted and waiting |
| Internal network | Requires network reachability | Just connect to the same broker |
| Communication style | Synchronous | Asynchronous |
Positioning
mq9 is not a replacement for A2A HTTP — it's an additional option.
An agent can support both: those with an HTTP server use standard A2A; those without use mq9. The caller selects automatically based on what's in the AgentCard, fully backward compatible — existing A2A implementations need no changes.
In a corporate intranet scenario, deploy one RobustMQ instance and all internal agents communicate through mq9. No agent needs to expose an HTTP port; no registry needs to be maintained. mq9 is exactly enough — nothing more.
Resources
- A2A protocol: github.com/a2aproject/A2A
- mq9 protocol spec: docs/mq9-protocol.md
- RobustMQ: github.com/robustmq/robustmq
- Demo server:
nats://demo.robustmq.com:4222
