Agent Mesh Protocol · v1.3
One agent asks another for help and gets an answer — even though neither can accept an inbound connection.
A network of specialists, no centre. Agents dispatch work to each other over MQTT, across laptops, VPNs and containers that have no route in.
examples/with-plugins.mjs, regenerated from an
actual run — so it cannot drift from what the code does. reviewer hits a
database migration it has no business judging, finds dba in the registry,
delegates that part, and answers as one voice.The problem
Every framework wants your agent to be a server
Expose an endpoint, get a URL, receive requests. But real agents don't live like that. They live on a laptop that sleeps at 6pm, on a VPN that drops, in a container with no inbound route, on a machine whose IP changed this morning.
Intelligence is moving to the edge. The interesting agents are no longer hosted endpoints — they are the one on your laptop with your repositories checked out, the one inside the VPN that can read production, the one on a colleague's machine that knows a system nobody else does. They hold context precisely because they are not in the cloud.
So invert the addressing. Let the agent be a client. It dials out, holds one connection, and receives work over it — addressable without being reachable.
| An agent is… | So the mesh gives it |
|---|---|
| offline half the day | work queued while it sleeps, delivered on wake |
| unknown to its peers | a retained capability profile — discovered without asking |
| slow, thinking for minutes | streamed milestones, and a result collectable an hour later |
| liable to die mid-thought | presence published by the broker itself, not a heartbeat |
| one of many | per-requester isolation as a subscription filter |
None of that is application code. It is what the transport already does — which is the whole argument for choosing it.
How it works
Four topics and a broker
Someone — a human, a CI job, or another agent — publishes a job. The agent picks it up, runs it, and streams back. Nothing on the delivery path polls.
# ask
mosquitto_pub -t 'agents/commands/reviewer/invoke' -m '{
"service": "code.review",
"requestedBy": "alice",
"args": { "repo": "acme/web-app", "pr": 42 }
}'
# listen — only your own traffic
mosquitto_sub -t 'agents/jobs/alice/#'
agents/jobs/alice/rev-118/events started · analyzing
agents/jobs/alice/rev-118/result {"verdict":"APPROVE"} ← retained
Alice could have disconnected and collected that result an hour later — it is retained
on the broker. And if the reviewer meets a migration it isn't qualified to judge, it
reads the registry, finds the agent that is, and asks. The chain carries
parentJobId, rootJobId and depth, so five agents
deep is still traceable to the one request that started it — and cancellable as one thing.
npm run demo — about a minute, no cloud,
no account.Use cases
What this is actually for
Every one of these has the same shape: the agent that can answer is not the agent that was asked, and it is somewhere you cannot open a port to.
The reviewer that asks a DBA
A code review hits a migration. The reviewing agent has no business judging lock behaviour, so it reads the registry, finds the agent that does, delegates that part and folds the answer into one review.
Without it: one prompt that pretends to know everything, or a human relaying between two chats.
The agent that can reach production
One agent sits where the logs and the read replica are. Everyone else asks it instead of everyone else getting VPN credentials. It dials out to the broker, so nothing inbound is opened and nothing new is exposed.
Without it: a bastion, a tunnel, and a widening circle of people with production access.
The colleague's laptop
Someone's machine holds context nobody else has — a checked-out monorepo, a decade of domain knowledge. Ask it at 2am; the broker queues the job and delivers it when the lid opens. The result is retained, so you collect it whenever you like.
Without it: "are you online?", or a queue and a retry policy you now maintain.
A pipeline that asks for an opinion
A CI job publishes an invoke and exits. No webhook endpoint to host, no connection to hold open, no callback URL that has to be reachable from wherever the agent happens to be. The verdict is on a retained topic when the next stage wants it.
Without it: a public callback endpoint, and a runner that stays alive waiting for it.
Agents that were never meant to meet
An OpenClaw agent in TypeScript and a Hermes agent in Python, discovering and delegating to each other with nothing in common but the specification. Adding a platform is a host plugin, not a negotiation.
Without it: whichever framework got there first wins, and the others integrate by scraping.
One place that does delivery
Results are matched against routes and delivered to Slack, a pull-request comment or a webhook — by a plugin no agent knows about. Adding a channel touches no agent, and no agent needs a Slack token.
Without it: every agent grows a formatter and a credential, and it stops scaling at the third one.
When it is the wrong tool: if your agents are already reachable HTTP services in one cluster, you do not need a broker in the middle — use whatever you have. Plexus earns its place when the interesting agent is behind something, asleep, or somebody else's.
The console
An operator view, on loopback
The OpenClaw host plugin serves a control panel bound to 127.0.0.1. One HTML
file, no external assets, no MQTT in the browser — the page talks to the plugin, and the
plugin talks to the mesh.
dist/web/index.html the plugin serves, so it cannot show a screen that no
longer exists.Three things in there are load-bearing rather than decorative. Every job keeps its
milestone timeline, so “why did this run twice?” is answerable afterwards rather
than only while it is happening. Capabilities are edited as data, and a prompt using
{{repo}} with no matching argument is refused — it would
render empty at dispatch and silently produce a bad job. And deployment variables are
write-only: the API returns a name, a source and a masked hint, and has no path that
reads a value back.
Where this sits
MCP connects an agent to tools.
Plexus connects an agent to other agents.
It isn't competing with your agent framework — it's the layer between frameworks. They compose: an agent can use MCP tools locally and answer Plexus requests from the mesh, and most useful ones do.
| Scope | Requires | Requester may be offline | |
|---|---|---|---|
| MCP | One agent using tools | A local process, or a reachable HTTP server | no |
| A2A | Agents interoperating | Both agents have reachable endpoints | no |
| HTTP + a queue | Whatever you assemble | A broker and the endpoints and the glue | depends |
| Plexus | Agents dispatching work to each other | Only that both can reach a broker | yes |
That last column isn't a feature, it's a consequence. An agent that dials out instead of listening spans laptops, VPNs, CI runners and cloud instances with no inbound port anywhere and nothing to defend.
Implementations
Three, sharing no code
A specification with one implementation is a description of that implementation. These were written independently against the spec, in different languages, with different MQTT clients and different plugin APIs.
OpenClaw host plugin
Puts a gateway's agent on the mesh, with an operator console. The reference implementation, and the one running in production.
Hermes host plugin
Drop-in for Hermes Agent. Implements the protocol again from the spec over
paho-mqtt — which is what makes it evidence.
plexus-agent
A client library and plugin host. Join the mesh in fifteen lines, from any Node process, with no platform at all.
The test that makes it a protocol
A Python agent and a Node agent are stood up against one broker, and each must discover, delegate to and answer the other — with lineage intact across the language boundary.
$ python hosts/hermes/tests/test_interop.py
hermes plugin online, offering research.summarise
[js] js-ready
hermes received job job-19d2160eed6f from the JS agent
[js] js-served
hermes delegated to js-reviewer and got risk=high
lineage intact across the language boundary: depth 1, parent linked
[js] js-got-answer
the JS agent received one combined answer from the Hermes agent
Writing the second one is also how the spec gets audited. docs/HOSTS.md is the guide for the next platform — the four jobs a host plugin has, and the traps that cost real time in the first one.
Install
macOS, Linux and Windows
Everything needs two things: a broker, and a host plugin for whatever agent platform you use. The installer works out which one you have.
git clone https://github.com/MoGhali/plexus && cd plexus
./install.sh
It never overwrites a config you already have, and re-running it updates — restarting the gateway only if the compiled output actually changed. Everything it does by hand:
# macOS and Linux
git clone https://github.com/MoGhali/plexus.git ~/.openclaw/extensions/mqtt-bridge
cd ~/.openclaw/extensions/mqtt-bridge
npm install && cp services.example.json services.json && npm run build
# Windows (PowerShell)
git clone https://github.com/MoGhali/plexus.git "$env:USERPROFILE\.openclaw\extensions\mqtt-bridge"
cd "$env:USERPROFILE\.openclaw\extensions\mqtt-bridge"
npm install; Copy-Item services.example.json services.json; npm run build
tools.alsoAllow is not optional. OpenClaw's tool profile is
an allowlist that excludes plugin-registered tools. Omit it and the agent
silently has none of them — executors cannot publish results, so jobs intermittently
finish without one, and delegation never happens. Nothing says why.
{ "tools": { "alsoAllow": ["mqtt_publish", "mesh_ask", "mesh_peers"] } }
openclaw config validate # ALWAYS FIRST — an invalid config stops the gateway starting
openclaw gateway restart # launchd, systemd or Task Scheduler, as appropriate
# macOS and Linux
git clone https://github.com/MoGhali/plexus
mkdir -p ~/.hermes/plugins && cp -r plexus/hosts/hermes ~/.hermes/plugins/plexus
pip install "paho-mqtt>=2.1"
# Windows (PowerShell)
New-Item -ItemType Directory -Force "$env:USERPROFILE\.hermes\plugins"
Copy-Item -Recurse plexus\hosts\hermes "$env:USERPROFILE\.hermes\plugins\plexus"
pip install "paho-mqtt>=2.1"
paho.mqtt from the interpreter Hermes itself runs
under. Installing it elsewhere is what ModuleNotFoundError: paho
almost always means.
// ~/.hermes/plexus.json
{ "broker": "mqtt://localhost:1883",
"agentId": "hermes",
"executor": "api", "apiUrl": "http://127.0.0.1:8000/v1",
"capabilities": [{
"service": "research.summarise",
"requestSchema": { "topic": "string" },
"prompt": "Research {{topic}}. Return JSON: summary, sources, confidence."
}] }
npm install plexus-agent
import { connect } from "plexus-agent";
const agent = await connect({
broker: "mqtt://localhost:1883",
agentId: "dba",
});
agent.serve("schema.review", async (job, ctx) => {
ctx.progress("checking lock behaviour");
return { risk: "high", finding: "ALTER without CONCURRENTLY locks writes" };
});
That agent is now discoverable by every other agent on the mesh, and jobs published while it is down are queued and delivered when it restarts.
Full steps, prerequisites and a troubleshooting table: docs/INSTALL.md.
The frame, and the plugins
Capabilities are data, not code
An agent on Plexus is a capability catalog — a name, an argument schema,
a prompt template. No code, no deploy, no restart. The frame contains no service name
anywhere: it never learns what code.review means, and does not need to.
{ "service": "schema.review",
"requestSchema": { "migration": "string" },
"prompt": "Review migration {{migration}}. Flag lock risk and missing indexes." }
An agent gains abilities the same way — by loading plugins, not by growing code. One connection, one registry entry, one durable session, however many plugins. An agent good at four things is still one agent on the mesh.
Honest limitations
What will bite you
Identity is not authenticated. MQTT delivers topic and payload only — a
publisher's broker identity does not travel with the message. So requestedBy
is self-declared, and owner scoping is a convention that keeps honest clients apart, not a
security boundary. The fix is broker-side ACLs, which means the protocol delegates its
hardest problem to your deployment. Know that going in.
Delegation holds sessions open. An asking agent waits, so a four-deep chain occupies four sessions. This model favours depth over breadth.
Young, and honestly so. The OpenClaw bridge runs daily against a real workload. The client library and the Hermes plugin are newer, exercised by their tests and examples rather than months of production. Expect to find things.
Requires a broker you operate. No hosted option — a feature if you care where your job payloads go, friction if you wanted to try it in five minutes.
Two agents on your machine, in about a minute
docker run -d -p 1883:1883 eclipse-mosquitto:2 \
sh -c 'printf "listener 1883\nallow_anonymous true\n" > /m.conf && mosquitto -c /m.conf'
git clone https://github.com/MoGhali/plexus && cd plexus
npm install
npm run demo