Design 25 §7's second item. Two surfaces over one thing — a command line for somebody at a terminal, an MCP server for an agent — and both are adapters over the same three calls: what tools are there, what does this one take, call it. A second way of reaching a tool would be a second thing to keep correct. It uses the client a module's runtime uses. Not a bridge and not a second protocol: a person connects as their own bus user and publishes on the tool subjects their account permits, so "what may this person do" is answered by the same permission list that answers it for a module, and an audit has nothing separate to read. `mesh tools` lists what the *catalogue* has, not what this credential may call. The two differ and the difference is the point: somebody seeing only their own tools cannot tell "not installed" from "not yours", and those need different people to fix them. A failed call says which of three things happened, because the remedies are in three different places: nobody serves that tool, this credential may not call it, or the tool itself was slow. Without that they are one timeout and a stack trace. The MCP surface decides nothing. The tool names are the ones a person types, the schemas are the modules' own, and an answer is passed through unshaped — an adapter that summarised somebody else's answer would be deciding what matters in it. A tool that fails comes back as a tool error rather than a protocol error, because the request was well-formed and the mesh answered it. Written against the protocol directly: it is three methods and one framing, and a dependency here would be a dependency on every workstation. Tests drive both surfaces against a real bus, including that a host's notification is answered with nothing and an unknown method is refused. They run one file at a time, because each stands up a module serving the same tool subjects and run together their requests get split between them — which showed up as one test reading another's answer.
mesh-tools
The Novox Mesh tool runtime — the per-node process that makes a module's tools actually serve.
A module ships its tools (built on @novox/mesh-sdk); this
runtime is what loads them and puts them on the mesh. It:
- connects the mesh broker (novox/hq ADR 0001) — a concrete AMQP implementation of the sdk's
Brokercontract; - imports the assigned modules' compiled tool entrypoints, each of which registers its tools as it loads;
- serves them through the sdk's
serveToolsharness, answeringtools.invokeover the broker.
Everything hard — dispatch, collection, duplicate-name safety — is the sdk's. This is the thin wrapper that binds the broker and loads the modules. Keeping the AMQP client here, out of the sdk, is deliberate: a broker-client change never rebuilds a module (ADR 0039).
Running it
MESH_BROKER_URL amqp://… the mesh broker
MESH_TOOL_MODULES /a/tools/index.js,… the assigned modules' compiled tool entrypoints
node dist/main.js, or the container (Dockerfile). On a node the host resolves both variables
and starts it like any other supervised workload.
Verified
npm test stands up LavinMQ (the mesh's broker) and proves the whole path over real AMQP: the
runtime serves a registered tool, a separate connection invokes it by name and gets the result, and
an unknown tool is refused over the wire.