As-is 13: fix five review findings on the mesh MCP server (issue 1008, review round 1)
mesh/merge-gate head f6199fafcee4 merged into trunk d0ee14712e18 — pass: the change touches no module of the mesh's graph
mesh/repo-check head f6199fafcee4 merged into trunk d0ee14712e18 — pass: its merge-check.sh passed
mesh/delivery delivered

node-tools still has a manifest (invokes *, waits, the mcp-endpoint provision) —
describe it in prose instead of saying it has none. The TypeScript sources
remain on main; only the built artifact is Go. The mesh-controller seat
answers 63 verbs as its own runtime, not 60-93 per node (that is node-tools'
own node-scoped seat verbs). Prefer reading mesh_overview live over a count
that drifts. An as-is document amends nothing — reworded accordingly.
This commit is contained in:
jochen
2026-10-17 13:39:14 +02:00
parent 65f69d96a9
commit f6199fafce
+43 -24
View File
@@ -26,12 +26,20 @@ module of that name.
**The tool runner is one of the mesh's core components**, named `node-tools` beside **The tool runner is one of the mesh's core components**, named `node-tools` beside
`mesh-controller`, `mesh-host` (the node-engine) and `nats` (the bus) — the mesh's own repositories' `mesh-controller`, `mesh-host` (the node-engine) and `nats` (the bus) — the mesh's own repositories'
check treats the four the same way, as the core whose health the mesh itself defines rather than a check treats the four the same way, as the core whose health the mesh itself defines rather than a
module's own. It is not a catalogue module: it has no manifest, is not assigned or unassigned, and module's own. `mesh-host` and `mesh-controller` supervise, compose and account for it as a core
carries no `invokes` grant of its own in a manifest, because there is no manifest. Its code lives process rather than as an ordinary assignment (`ComponentNodeTools`/`BusNodeTools` in both, the
under `node-tools/` in the `mesh-tools` repository (`internal/console`, `internal/announce`, witness and lease code that restarts and rolls it back, the account named `<node>.node-tools` on
`internal/bus`), and `mesh-host` and `mesh-controller` supervise, compose and account for it as a the bus).
core process rather than as an assignment (`ComponentNodeTools`/`BusNodeTools` in both, the witness
and lease code that restarts and rolls it back, the account named `<node>.node-tools` on the bus). **It still carries a manifest, `node-tools/module.json` in `mesh-tools`,** and that manifest is
where its grants live: `invokes` names `*`, so its account may call every tool on the mesh;
`waits` names the five event families `mesh_wait` may subscribe to (the knowledge base's reviews,
mesh-delivery's transitions, the forge's status, the controller's checked event, and the operator
channel's decision for the agent module) — the bus refuses a subscription outside this list
(ADR 0367 §2); and it
`provides` one provision, the MCP endpoint, at node scope, and `listens` on the port it serves from,
opened only to the machine itself. Its code lives under `node-tools/` in the `mesh-tools`
repository (`internal/console`, `internal/announce`, `internal/bus`).
**One runtime per node, run by the node-engine as a sibling process, never a container** **One runtime per node, run by the node-engine as a sibling process, never a container**
(ADR 0175). It holds the node's one bus credential and may call every tool on the mesh; its account (ADR 0175). It holds the node's one bus credential and may call every tool on the mesh; its account
@@ -48,9 +56,11 @@ body per request, no session and no event stream (`node-tools/internal/console/m
**It announces seven tools, not five** (ADR 0195, extended by ADR 0367): `mesh_overview`, **It announces seven tools, not five** (ADR 0195, extended by ADR 0367): `mesh_overview`,
`mesh_machine`, `mesh_search`, `mesh_describe`, `mesh_call`, `mesh_runtimes` and `mesh_wait`. The `mesh_machine`, `mesh_search`, `mesh_describe`, `mesh_call`, `mesh_runtimes` and `mesh_wait`. The
handshake's `instructions` text names all seven and the four address forms; measured live, the four handshake's `instructions` text names all seven and the four address forms. `mesh_overview` is
nodes' tool runners between them answer for 103 modules and 645 tools this way, with two module where the mesh-wide count of modules and tools is read live — a number copied into this document
instances (`route-proxy` on two of the nodes) named as not answering rather than silently absent. would drift; asked on 2026-10-17 it answered 103 modules and 645 tools across the whole mesh, with
two module instances (`route-proxy` on two of the nodes) named as not answering rather than silently
absent.
**Addresses, exactly as the handshake states them:** `<seat>.<verb>` for a seat held once for the **Addresses, exactly as the handshake states them:** `<seat>.<verb>` for a seat held once for the
mesh (`mesh-controller.status`); `<node>/<seat>.<verb>` for a seat every machine holds mesh (`mesh-controller.status`); `<node>/<seat>.<verb>` for a seat every machine holds
@@ -74,7 +84,7 @@ answering in under 100ms for hundreds of kilobytes of discovery.
**The seventh tool, added by ADR 0367**, lets a session wait on the bus for an event instead of **The seventh tool, added by ADR 0367**, lets a session wait on the bus for an event instead of
polling: a call names one or more granted event families and an optional filter, and either answers polling: a call names one or more granted event families and an optional filter, and either answers
the first match or says it timed out. The tool runner's own bus account hears only the families its the first match or says it timed out. The tool runner's own bus account hears only the families its
manifest-equivalent grant names; an event it hears is never taken as the state — a session reads the manifest's `waits` list names; an event it hears is never taken as the state — a session reads the
state from the verb that owns it once woken. It is meant to be called in a loop from a background state from the verb that owns it once woken. It is meant to be called in a loop from a background
command, not inline, because one call blocks until it answers or times out. command, not inline, because one call blocks until it answers or times out.
@@ -82,32 +92,41 @@ command, not inline, because one call blocks until it answers or times out.
**These already answer directly, not as a stated future.** Beside every assigned module's tools, **These already answer directly, not as a stated future.** Beside every assigned module's tools,
the tool runner asks the `mesh-controller` seat for its own verbs the same way (ADR 0154): measured the tool runner asks the `mesh-controller` seat for its own verbs the same way (ADR 0154): measured
live, that seat answers between 60 and 93 verbs per node (`status`, `push`, `assign`, `build`, live, the mesh-controller runtime is one instance, mesh-wide, and it answers 63 verbs for that one
`artifacts` and the rest), and the `mesh-controller` *module* answers none of its own — it serves no seat (`status`, `push`, `assign`, `build`, `artifacts` and the rest), and the `mesh-controller`
module tools, only its seat's, exactly as design 33 described while it was still only designed. *module* answers none of its own — it serves no module tools, only its seat's, exactly as design 33
described while it was still only designed. The 56-to-93 seat-verb counts each node's tool runner
answers (below) are a different thing: the tool runner serves the verbs of whichever node-scoped
seats are held on that node (`node-backup`, `node-build-agent`, `node-service-manager` among them),
on behalf of the modules holding them, not the mesh-controller seat's — that one is served by the
mesh-controller runtime itself, a separate process, and the tool runner only asks it and relays what
it answers.
## Where it runs ## Where it runs
**Every node, not a per-operator assignment.** Measured live: all four of the mesh's machines **Every node, not a per-operator assignment.** Measured live: all four of the mesh's machines
answer a `node-tools` runtime, each with its own module and tool count (between 294 and 312 tools, answer a `node-tools` runtime, each with its own module and tool count (between 294 and 312 tools,
56 to 93 seat verbs, in this reading). This is a fact that shipped past the design it came from: the 56 to 93 seat verbs, in this reading). This is a fact that shipped past the design it came from: the
to-be document this amends still describes it as assigned to "whichever machines an operator sits to-be document still describes it as assigned to "whichever machines an operator sits at"; what
at"; what actually runs is the opposite — every node the mesh holds carries one, because it is core actually runs is the opposite — every node the mesh holds carries one, because it is core
infrastructure the mesh itself depends on (discovery, the controller's own verbs, every module's infrastructure the mesh itself depends on (discovery, the controller's own verbs, every module's
tools), not a convenience assigned to a workstation. There is accordingly no assign or unassign verb tools), not a convenience assigned to a workstation. There is accordingly no assign or unassign verb
for it: removing it from a node is not an operation the controller or the catalogue exposes. for it: removing it from a node is not an operation the controller or the catalogue exposes.
## What this repository's prior text got wrong ## What this repository's prior text got wrong
The document this replaces described `mesh-console` as a catalogue module with an `invokes: ["*"]` The document this replaces described `mesh-console` as a catalogue module served by a container and
manifest, served by a container, assigned only to workstations, and implemented in TypeScript assigned only to workstations. That module and its container are gone; the manifest is not — it
(`mesh-tools src/mesh.ts`, `src/http.ts`, `src/runtime.ts`). None of that is true of what runs today: moved to `node-tools/module.json`, still declares `invokes: ["*"]`, and gained `waits` and the
the container, the manifest, the per-workstation assignment and the TypeScript implementation are `mcp-endpoint` provision, described above. What also moved is which language builds it: the
all gone, replaced in substance by ADR 0175's host-side, module-agnostic, Go runtime described manifest's `build.artifacts` names a Go binary built from `cmd/node-tools`, and that is what a node
above. The quirks it recorded from the `mesh-console` era (a module registered by hand recording its runs. The repository still carries the earlier TypeScript sources (`node-tools/src/mesh.ts`,
source as a URL; a *record*-policy module staying listed as not-answering until its rebuilt runtime `http.ts`, `runtime.ts`, `announce.ts`, `broker-nats.ts`, `client.ts`, `launch.ts`, `main.ts`,
is pushed) are not re-checked here: they were about the `mesh-console` container's registration path, `mcp.ts`) on main, but the manifest does not build from them. The quirks the prior text recorded
which no longer exists. from the `mesh-console` era (a module registered by hand recording its source as a URL; a
*record*-policy module staying listed as not-answering until its rebuilt runtime is pushed) are not
re-checked here: they were about the `mesh-console` container's registration path, which no longer
exists.
## How it is checked ## How it is checked