The module owns /etc/claude-code: managed-mcp.json lists the console as `mesh` over HTTP on loopback plus the servers in its mcp_servers setting (exclusive, by the operator's choice — the https rule of managedMcpServers refuses a loopback console); managed-settings.json carries the attribution convention, keeps claude.ai connectors, and adds the key-helper only for an API-key licence; CLAUDE.md says how a session here works. Rendered whenever the runtime collects the tools, written only on change, through the operator account's sudo. Under the home, only the credentials file, only on a hand-over. Nothing declared under a home or /etc; the console's port comes from node-tools' mcp-endpoint (mesh-tools #34).
40 lines
2.1 KiB
TypeScript
40 lines
2.1 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { render } from "../dist/render.js";
|
|
|
|
const facts = { node: "workstation", console: "http://127.0.0.1:4270/mcp" };
|
|
|
|
test("the console is the `mesh` server, and an operator's servers are listed beside it", () => {
|
|
const out = render(facts, { mcp_servers: { search: { type: "http", url: "https://s.example/mcp" } } }, null, "/h");
|
|
const mcp = JSON.parse(out["managed-mcp.json"]);
|
|
assert.deepEqual(Object.keys(mcp.mcpServers), ["mesh", "search"]);
|
|
assert.deepEqual(mcp.mcpServers.mesh, { type: "http", url: facts.console });
|
|
});
|
|
|
|
test("a setting cannot replace the mesh's own entry, and a name the vendor refuses is left out", () => {
|
|
const out = render(facts, { mcp_servers: { mesh: { type: "http", url: "http://evil" }, "bad name": {} } }, null, "/h");
|
|
const mcp = JSON.parse(out["managed-mcp.json"]);
|
|
assert.equal(mcp.mcpServers.mesh.url, facts.console);
|
|
assert.ok(!("bad name" in mcp.mcpServers));
|
|
});
|
|
|
|
test("managed settings carry the mesh's keys only, and the key-helper only for an API-key licence", () => {
|
|
const sub = JSON.parse(render(facts, {}, { licence: "personal", kind: "subscription" }, "/h")["managed-settings.json"]);
|
|
assert.deepEqual(sub, { attribution: { commit: "", pr: "" }, allowAllClaudeAiMcps: true });
|
|
const key = JSON.parse(render(facts, {}, { licence: "api", kind: "api-key" }, "/state/api-key-helper")["managed-settings.json"]);
|
|
assert.equal(key.apiKeyHelper, "/state/api-key-helper");
|
|
assert.ok(!("model" in key), "a preference is the person's");
|
|
});
|
|
|
|
test("the instruction file names the node and its role, and no other node", () => {
|
|
const md = render(facts, { role: "the laptop" }, null, "/h")["CLAUDE.md"];
|
|
assert.match(md, /\*\*Node:\*\* `workstation`/);
|
|
assert.match(md, /\*\*Role:\*\* the laptop/);
|
|
assert.match(md, /records\.records_search/);
|
|
});
|
|
|
|
test("rendering is deterministic, so an unchanged input writes nothing", () => {
|
|
const s = { mcp_servers: { b: { type: "http", url: "https://b" }, a: { type: "http", url: "https://a" } } };
|
|
assert.deepEqual(render(facts, s, null, "/h"), render(facts, s, null, "/h"));
|
|
});
|