71 lines
4.5 KiB
Markdown
71 lines
4.5 KiB
Markdown
# mesh-tools
|
|
|
|
The Novox Mesh **tool runtime** — the one process per node that makes every assigned module's tools
|
|
actually serve (novox/hq ADR 0175).
|
|
|
|
A module ships its tools as a bundle (built on [`@novox/mesh-sdk`](https://git.novox.be/novox/mesh-sdk));
|
|
this runtime is what loads them and puts them on the mesh. It:
|
|
|
|
1. connects the mesh bus on the node's credential — a concrete implementation of the sdk's `Broker`
|
|
contract;
|
|
2. reads one membership per module it serves — what the mesh issued that module on this machine
|
|
(ADR 0160): where its tools are answered, which seats it holds — and follows each live;
|
|
3. imports each module's compiled tool entrypoints, each of which registers its tools as it loads,
|
|
guarded: a bundle that throws is named, in the log and in what `tools` answers for its module,
|
|
and the others serve;
|
|
4. serves every module's tools on that module's subjects and every held seat's verbs on the seat's.
|
|
|
|
A bundle that is not plain JavaScript — a Go or Rust binary, a Python script, or a JavaScript file
|
|
marked executable — is **launched** rather than imported (novox/hq ADR 0188): the runtime starts it
|
|
as a child with its own environment and speaks MCP over stdio to it, `tools/list` once and
|
|
`tools/call` per call. A tool it lists as `<seat>.<verb>` is the seat's implementation. A child that
|
|
exits is named in the log and started again on its next call. So a tools bundle may be written in
|
|
any language; the mesh's SDK for each is the stdio loop and nothing more (`src/launch.ts` is the
|
|
runtime's side of it).
|
|
|
|
Everything hard — dispatch, collection, duplicate-name safety — is the sdk's. This is the thin
|
|
wrapper that binds the bus and loads the modules. Keeping the bus client here, out of the sdk, is
|
|
deliberate: a bus-client change never rebuilds a module (ADR 0039). The runtime is module-agnostic:
|
|
it knows bundles and subjects, nothing of what any module does. A tool that needs root escalates
|
|
itself — root is the module's concern, not the runtime's.
|
|
|
|
## Running it
|
|
|
|
```
|
|
MESH_BROKER_FILE the node's sealed credential, as the mesh delivered it
|
|
MESH_TOOL_MODULES alpha=/…/alpha/tools/index.js,beta=/…/beta/dist/index.js,…
|
|
the modules to serve and their compiled entrypoints; several entries may
|
|
name one module. A bare path is an entrypoint of the credential's own module
|
|
— the one-module form a per-module container still sets.
|
|
MESH_OPERATOR_ACCOUNT whose machine this is, and MESH_OPERATOR_HOME where their home is; set by
|
|
the mesh when the node has an account, read by tools from their environment
|
|
MESH_BROKER_URL a plain URL instead of the credential, for the bootstrap case
|
|
```
|
|
|
|
`node dist/main.js`. On a node the controller composes the variables and the host supervises the
|
|
process like any other host-side workload (novox/hq to-be 38). The container (`Dockerfile`) is how
|
|
a module's own *service* may still be built; it is no longer how tools reach a node.
|
|
|
|
## `mesh` — the tools for whoever is on a machine
|
|
|
|
The same package carries the client (novox/hq design 25 §7, design 34): `mesh tools`, `mesh call
|
|
<module>.<tool> [json]`, `mesh mcp` (an MCP server over stdio for a program a person starts) and
|
|
`mesh serve` (the **console**: MCP over HTTP on a machine's loopback, started by the mesh as the
|
|
`mesh-console` module on the credential in `MESH_BROKER_FILE` — novox/hq ADR 0152). `mesh serve`
|
|
refuses to bind anything but loopback. With `--console <url>`, `tools` and `call` go through a console
|
|
already on the machine and need no credential.
|
|
|
|
Discovery asks the modules: every runtime answers a `tools` verb for each module it serves, with names,
|
|
descriptions and schemas from the code that answers them, and the console asks the catalogue which
|
|
modules the mesh holds and each module what it serves. A module that does not answer is named, never
|
|
dropped. A module may not name a tool of its own `tools`; the runtime refuses it at load.
|
|
|
|
## Verified
|
|
|
|
`npm test` runs against a real NATS server with JetStream (`MESH_TEST_NATS`, see any test's header
|
|
for the one-line `docker run`) and proves the whole path over the wire: the runtime serves a
|
|
registered tool, a separate connection invokes it by name and gets the result, an unknown tool is
|
|
refused, a runtime serves exactly the subjects it is issued and re-serves on a new membership, and
|
|
the node's runtime serves three modules' bundles on one credential — one of them broken, named and
|
|
not fatal — with every seat verb answering where the membership put it.
|