testToolsRunInAWorker

tcmts.cli.test.e2e.mcp.testToolsRunInAWorker · #5wgkmoi7 · term

The server runs quick tools' commands in a worker it keeps up: each answers as the command would at a terminal, an error doesn't leak into the next, what one changes the next sees, and calls made while it's busy are answered too (by a tcm of their own), as is a command it doesn't run (tcm run).

import { plainEnv, tcmBin, tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness";
import { session } from "#codebase/tcmts/cli/test/e2e/mcp";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";

export const testToolsRunInAWorker = () =>
  withChildren(async (keep) => {
    const dir = tempDir("mcp worker");
    const env = plainEnv();
    assert.equal(spawnSync(process.execPath, [tcmBin(), "init"], { cwd: dir, env }).status, 0);
    fs.writeFileSync(
      path.join(dir, "scratch.ts"),
      `// tcm: namespace lib\nexport const square = (x: number): number => x * x;\nexport const area = (s: number): number => square(s);\n`,
    );
    assert.equal(spawnSync(process.execPath, [tcmBin(), "add"], { cwd: dir, env }).status, 0);
    // (on the codebase's own branch: this is about the worker, not sessions)
    const s = session(dir, keep, "--direct");
    await s.request("initialize", {
      protocolVersion: "2025-06-18",
      capabilities: {},
      clientInfo: { name: "test", version: "0" },
    });

    const view = await s.call("tcm_view", { names: ["lib.square"] });
    assert.equal(view.isError, false);
    assert.match(view.text, /x \* x/);
    // an error, then not
    const missing = await s.call("tcm_view", { names: ["lib.nothing"] });
    assert.equal(missing.isError, true);
    assert.match(missing.text, /'lib\.nothing' not found/);
    // a name that isn't there doesn't hide the ones that are, and says what was meant
    const some = await s.call("tcm_view", { names: ["lib.sqare", "lib.square"], lines: '"1-3"' });
    assert.equal(some.isError, false);
    assert.match(some.text, /^'lib\.sqare' not found; did you mean lib\.square\?/);
    assert.match(some.text, /\*\*lib\.square\*\*[\s\S]*x \* x/);
    assert.equal((await s.call("tcm_todo")).isError, false);
    // a change, seen by what comes next
    assert.match(
      (await s.call("tcm_patch", { name: "lib.square", edits: [{ old: "x * x", new: "x ** 2" }] }))
        .text,
      /~ update +lib\.square/,
    );
    assert.match((await s.call("tcm_view", { names: ["lib.square"] })).text, /x \*\* 2/);
    assert.match((await s.call("tcm_dependents", { name: "lib.square" })).text, /^lib\.area$/m);
    // several at once: one in the worker, the rest each in a tcm of its own
    const [a, b, c] = await Promise.all([
      s.call("tcm_view", { names: ["lib.area"] }),
      s.call("tcm_find", { query: "square" }),
      s.call("tcm_status"),
    ]);
    assert.match(a.text, /square\(s\)/);
    assert.match(b.text, /lib\.square/);
    assert.match(c.text, /named definitions/);
    // a command the worker doesn't run
    assert.match((await s.call("tcm_run", { name: "lib.area", args: ["3"] })).text, /^9$/m);
    s.close();
  })

uses