--- topic: what runs on it status: accepted date: 2026-10-02 deciders: jochen reconstructed: false extends: 02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md --- # 188. A module's own code is bundles in any language, and a tools bundle speaks MCP to the runtime ## Context [ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md) put one tool runtime on every node and said a module brings its tools as a bundle. The runtime that exists is written in TypeScript and brings a bundle to life by **importing it into its own process**, which only JavaScript can be. The SDK ([ADR 0039](0039-what-the-sdk-holds-and-refuses.md)) is one TypeScript package. The builder knows three toolchains — TypeScript, Go, Python — and every one of the 35 catalogue modules with tools wraps them in a container on the runtime's TypeScript image. Nothing in the records says a module's code may be written in anything else, and nothing refuses a module that wraps its own code in an image to get around that. The operator's direction, stated on 2026-10-02 and repeated: *the SDK is the most important part; we must not limit developers; tools can be written in any possible language — Rust, C, Go, JavaScript. A service in Go or Rust as a systemd unit must be possible too. One module can deliver all kinds of bundles: one for its tools, one for a seat's implementation, one for a daemon. Support the bare minimum first, as a skeleton; a full implementation comes when the work requires it.* Measured against that: the `bundle` artifact kind already names a language and the `process` resource already runs a command from an unpacked bundle as a unit the host writes ([ADR 0150](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md)), so a Go daemon as a native service is possible today and one module in the catalogue does it. What is not possible is a tool in any language but one, and what is not written is that any of this is the rule. ## Considered Options 1. **One SDK, one language, as now.** Rejected: it limits who can write a module to one ecosystem, which the operator declines, and it is what made every module's tools a container on one image. 2. **A full bus client per language.** Each SDK speaks the bus itself; the runtime only supervises. Rejected: a transport in every SDK is what ADR 0039 refuses, and a bus change would then rebuild every module in every language — the cascade, multiplied. 3. **A tools bundle is a process the runtime launches and speaks a small local protocol to, and that protocol is MCP over stdio.** Chosen. The runtime already speaks MCP outward (the console); speaking it inward to a child process is the same vocabulary. Every language that has an MCP server library can write a tools bundle today with no mesh SDK at all, and the mesh's own SDK for a language is a thin convenience over it. The transport stays in the runtime, so a bus change rebuilds nothing. 4. **A protocol of the mesh's own design.** Rejected: a second way to describe a tool, its schema and its call, inventing what MCP already settled, for no gain. ## Decision **1. A module's own code is bundles, in any language the mesh has a toolchain for, and never an image.** A `bundle` names its language and what it is for. Images are for third-party software a module installs — a database, a forge — never for code the module wrote. One module may declare several bundles: its tools, its implementation of a seat's verbs, a daemon, a step. Each is built alone and delivered alone, as [ADR 0156](0156-an-artifact-is-what-a-build-produces-and-the-store-is-named-for-its-scope.md) already has it. **2. A bundle the runtime serves is a process that speaks MCP over stdio.** The node's runtime launches it as the bundle names it — an interpreter and a file, or a binary — with the runtime's environment, asks `tools/list`, and answers each call on the bus by `tools/call`. A tool whose name is `.` is the module's implementation of that seat's verb; any other name is the module's own tool. Everything the runtime does with what it is told — subjects from the membership, a held seat's verbs, the `tools` answer, a bundle that fails named and the others serving — stays as [ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md) and [ADR 0160](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md) have it. A TypeScript bundle may still be imported into the runtime's own process; that is a shortcut over the same contract, not a second contract, and a TypeScript bundle written against the protocol is served the same way as any other. **3. A bundle that is a service is a `process`**, run by the host as a unit, in whatever language it is compiled from, exactly as the host's own bundle already is. Nothing new is decided here; it is said so that it is the rule and not an example. **4. One thin SDK per language, and the test of ADR 0039 applies to each.** An SDK for a language holds the MCP-over-stdio loop, the tool-definition type and the few primitives a module's code needs; it holds no transport, no module's client and nothing volatile. Where a language has a sound MCP library, the SDK wraps it rather than re-implementing it. The languages are those that make sense to write a module in; the first set is TypeScript, Go, Python, Rust and C, and the set grows when a module needs one, not before. **5. Skeleton first.** Each piece — a toolchain, a launcher, an SDK — exists at the bare minimum that lets one bundle in that language be built, delivered and answer one tool on the live mesh. Anything beyond that is added when a module needs it. A skeleton that is not proven by one bundle answering is not a skeleton; it is a promise. ## Consequences - The runtime gains a launcher beside its loader. The loader, the memberships, the seats and the failure handling built for ADR 0175 stand; the launcher is the one new step. - The builder gains a toolchain per language, each at the skeleton: compile, pack, name the entrypoint. Rust and C are new; a language that compiles to a binary says its operating system as a Go bundle already does. - An existing MCP server in any language is already a valid tools bundle. What the mesh adds is the subjects, the seats and the memberships around it. - The gate [to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP2 adds — refusing a tools container built on the runtime's image — widens: a module whose own code is an image artifact is refused at registration, naming this record. - What got harder: a tools bundle is now a process per module on the node rather than code in one process, so the runtime supervises children and restarts one that dies. The one-process shape ADR 0175 counted on for the TypeScript shortcut remains available for it. - ADR 0039's "what the SDK holds" now reads per language; its refusals are unchanged and are the reason option 2 was rejected. ## How it is checked | Rule | Checked by | |---|---| | A module's own code is never an image | the catalogue's registration check: a manifest with a `bundle` kind of own code *and* an image artifact built from the module's own directory is refused, naming this record | | A tools bundle in a language other than TypeScript answers on the bus | the runtime's tests: a bundle written against the protocol in a second language, launched, its tool called over a real bus | | A TypeScript bundle written against the protocol is served like any other | the same tests, with the TypeScript shortcut off | | Each SDK is thin | each SDK's own README states what it holds under ADR 0039's test, and its size is in the mesh's records | | Live | a tool in a compiled language answers from the node's runtime on one machine | ## References - [ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md), [ADR 0039](0039-what-the-sdk-holds-and-refuses.md), [ADR 0150](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md), [ADR 0156](0156-an-artifact-is-what-a-build-produces-and-the-store-is-named-for-its-scope.md), [ADR 0160](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md) - [To-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) — the work packages this record widens - The Model Context Protocol's stdio transport — the local protocol a tools bundle speaks