testUnknownToolOptionsAreRefused
A tool call with an option the tool doesn't have (one a newer tcm has, or a misspelt one) is refused, naming it,
the build, and the options the tool has, rather than run without it: a cwd this server's tcm_cli didn't know
would have run the command on the codebase itself. Options of objects inside the call are checked too. The tools
that read the codebase take live, as the ones that change it do.
import { okIn, 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 fs from "node:fs"; import path from "node:path"; export const testUnknownToolOptionsAreRefused = () => withChildren(async (keep) => { const dir = tempDir("unknown options"); okIn(dir, "init"); fs.writeFileSync(path.join(dir, "scratch.ts"), `// tcm: namespace lib\nexport const x = 1;\n`); okIn(dir, "add"); const a = session(dir, keep, "--direct"); await a.request("initialize", { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "test", version: "0" }, }); fs.writeFileSync(path.join(dir, "y.ts"), `// tcm: namespace lib\nexport const y = 2;\n`); const cli = await a.call("tcm_cli", { args: ["add", "y.ts"], workdir: "elsewhere" }); assert.equal(cli.isError, true); assert.match( cli.text, /^tcm_cli has no `workdir` option in this build \(#[a-z2-7]{8}\): known are args, background, build, cwd, live\. Nothing was done\./, ); assert.doesNotMatch(okIn(dir, "ls", "lib"), /lib\.y/); const patched = await a.call("tcm_patch", { patches: [{ name: "lib.x", edits: [{ old: "1", new: "3" }], als: true }], }); assert.equal(patched.isError, true); assert.match( patched.text, /no `patches\[0\]\.als` option[^:]*: known in patches\[0\] are all, edits/, ); assert.match(okIn(dir, "view", "lib.x"), /x = 1/); // what it has is done as before assert.equal((await a.call("tcm_view", { names: ["lib.x"] })).isError, false); const listed = (await a.request("tools/list")).result!.tools as { name: string; inputSchema: { properties: Record<string, unknown> }; }[]; for (const t of ["tcm_view", "tcm_history", "tcm_todo", "tcm_status", "tcm_find", "tcm_search"]) assert.ok( "live" in listed.find((l) => l.name === t)!.inputSchema.properties, `${t} takes live`, ); assert.ok(!("live" in listed.find((l) => l.name === "tcm_job")!.inputSchema.properties)); })