Skip to content

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:

  1. An agent starts an HTTP server and exposes its AgentCard at /.well-known/agent.json
  2. Other agents fetch the AgentCard over HTTP to discover its capabilities and address
  3. 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:

img

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.

python
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.

python
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 agents

Communication: 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.

python
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.

python
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

img


Comparison

A2A Standard HTTPA2A + mq9
DiscoveryKnow the HTTP address in advanceSubscribe to public Mailbox, auto-discover
HTTP server requiredYesNo (optional)
Receiver offlineMessage lostMessage persisted and waiting
Internal networkRequires network reachabilityJust connect to the same broker
Communication styleSynchronousAsynchronous

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

🎉 既然都登录了 GitHub,不如顺手给我们点个 Star 吧!⭐ 你的支持是我们最大的动力 🚀