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.

A terminal recording: reviewer receives a code review, discovers a peer offering schema.review, delegates to it, folds the answer into one combined review, and a notify plugin reports the outcome.
A real recording of 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 daywork queued while it sleeps, delivered on wake
unknown to its peersa retained capability profile — discovered without asking
slow, thinking for minutesstreamed milestones, and a result collectable an hour later
liable to die mid-thoughtpresence published by the broker itself, not a heartbeat
one of manyper-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.

Terminal recording of two agents: reviewer discovers dba offers schema.review, delegates at depth 1, and returns one combined result to the requester.
Two agents, one request. 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.

Review that knows your systems

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.

Inside the VPN

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.

Machines that sleep

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.

CI and cron

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.

Different stacks

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.

Telling people

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.

Sixteen seconds through the panel: connection and durability, a job's timeline, capabilities edited as data, variables, and the dark theme. Recorded from the shipped panel against redacted fixtures — the same 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.

ScopeRequiresRequester may be offline
MCPOne agent using toolsA local process, or a reachable HTTP serverno
A2AAgents interoperatingBoth agents have reachable endpointsno
HTTP + a queueWhatever you assembleA broker and the endpoints and the gluedepends
PlexusAgents dispatching work to each otherOnly that both can reach a brokeryes

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.

TypeScript

OpenClaw host plugin

Puts a gateway's agent on the mesh, with an operator console. The reference implementation, and the one running in production.

Python

Hermes host plugin

Drop-in for Hermes Agent. Implements the protocol again from the spec over paho-mqtt — which is what makes it evidence.

JavaScript

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

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.

plexus-notify is the first one: Plexus moves work between agents, and notify moves the outcome to people — Slack, a pull-request comment, a webhook. No agent knows it is there.

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.

Who built this

Mohanad Ghali

Designed and built Plexus · @MoGhali

Plexus came out of a problem rather than an idea. The agent worth asking was on a machine nobody could reach — behind a VPN, on a laptop that sleeps, in a container with no route in — and every framework's answer was to make it a server it could never be.

So the addressing got inverted, and the rest of the protocol followed from that one decision. It has been running daily against a real workload since, which is where most of what is written down here came from: the client id that must not contain a process id, the watchdog that keys on settlement rather than silence, the tool allowlist whose omission is completely silent. None of those were designed. They were found.

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