--- topic: what runs on it status: accepted date: 2026-10-03 deciders: jochen reconstructed: false extends: 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md --- # 195. The mesh's tools are found by address, not announced whole ## Context The console answers MCP on a machine's loopback ([ADR 0152](0152-the-operators-surface-is-a-module-the-console.md), [to-be 34](../03-DESIGN/01-to-be/34-the-console.md)) and announces, at a session's start, every tool the mesh can say it has: 228 on 2026-10-03, 110 KB, taken once. Three things are wrong with that, measured in research [021](../01-RESEARCH/021-finding-a-tool-in-the-mesh/00-overview.md): - **Size.** Only one client's habit of deferring long lists keeps them out of the model's context. - **Ambiguity.** A module on two machines is listed once, `node` optional, *whichever answers* — for postgres and mssql, whose instances hold different data, a call that names no machine asks an arbitrary one. - **Staleness.** A tool that arrives after the session started is not listed until it reconnects. The mesh already has the structure a caller needs: seats held once for the mesh, seats held once per machine ([ADR 0159](0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md)), and modules assigned to machines, each assignment issued its own subjects ([ADR 0160](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md)). The operator's direction: *tools are discoverable, in layers, and asking novox's postgres is not asking ace's.* ## Considered Options 1. Keep the flat list and rely on the client. Rejected: the ambiguity and the staleness stay, and it is one client's behaviour. 2. One tool per assignment, the machine in the name. Rejected: the list multiplies, and an address in a tool's name meets the API's limit — letters, digits, `_` and `-`, at most 64 characters. 3. **A fixed handful of tools that walk the mesh's structure, the address an argument.** Chosen. 4. MCP resources or prompts. Rejected: unevenly supported, and an agent acts through tools. ## Decision **1. Everything the mesh answers has one address, by the layer it lives in:** | layer | address | answered by | |---|---|---| | a seat held once for the mesh | `.` | that seat's holder | | a seat held once per machine | `/.` | that machine's holder | | a module assigned to a machine | `/.` | that assignment | | a module whose instances are interchangeable (ADR 0160) | `.` as well | any of them | **A call to a module that is not interchangeable names its machine, or is refused** naming the machines it runs on. "Whichever answers" is no longer an answer for state a machine holds. **2. The console announces a fixed set of tools, not the catalogue:** - **`mesh_overview`** — the mesh's seats with their verbs, and its machines; - **`mesh_machine`** — one machine: the node seats it holds and the modules assigned to it, each with its tools by name; - **`mesh_search`** — words in, matching addresses out with one line each, across every layer; - **`mesh_describe`** — one address in, its description and argument schema out; - **`mesh_call`** — an address and its arguments in, the answer out, with the machine that gave it. Each is answered from the mesh when it is asked, so a tool that arrived a minute ago is found without the client reconnecting. The names are the API's kind of name; addresses never have to be. **3. The flat catalogue stays reachable, not announced:** the `mesh` client and a console setting can still list it whole, for a person reading it or a client that wants it. An agent pointed at the console sees the five. > **The mechanism changed — 2026-10-03, by ADR 0197.** Where the console learns what exists: not > from the catalogue's roster and the controller's printed lists, but from every runtime announcing > itself on the bus in the NATS services protocol, checked against the controller's records read as > JSON. The addresses and the five tools stand. ## Consequences - An agent spends a call or two finding a tool it does not know, and none on one it does; the context no longer carries 110 KB it mostly never uses. - The ambiguity is closed by the address, not by a description asking the agent to remember `node`. - What got harder: an agent that once saw a tool's schema up front now asks for it. `mesh_describe` and `mesh_search` answering with the schema of a close match keep that to one call. - The discovery verbs are the console's; the mesh's own records — seats, machines, assignments — are the controller's, and the console asks it rather than keeping a copy. ## How it is checked | Rule | Checked by | |---|---| | The console announces five tools | the console's test: `tools/list` answers exactly the five | | An address resolves to one subject per layer | the console's tests: a mesh seat, a node seat, an assignment, an interchangeable module, each called by address over a real bus | | A non-interchangeable module without a machine is refused, naming its machines | the same tests | | A tool that arrives after the session started is found | a test registering a module after the console's first answer and finding it by `mesh_search` | | Live | from a fresh session, *which databases does novox's postgres hold* is answered by novox's postgres, found through the five | ## References - [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md), [ADR 0159](0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md), [ADR 0160](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md) - Research [021](../01-RESEARCH/021-finding-a-tool-in-the-mesh/00-overview.md) - [to-be 34](../03-DESIGN/01-to-be/34-the-console.md)