serveWorker
uses process
The MCP server's worker (tcm internal-worker): tcm kept running, so a tool doesn't start one. It reads commands
from stdin, a JSON line each ({ id, argv, env }), and runs them one at a time in this process, as run does;
each one's output goes to stdout and stderr as a command's would (what it starts too), and ends with a mark on
each: \0tcm-done <id> <exit code> on stdout, \0tcm-done <id> on stderr. What a command changes of the process
(its environment, where it is, the exit code) is put back after it.
export function serveWorker(run: (argv: string[]) => Promise<number>): Promise<void> { const one = async ({ id, argv, env, }: { id: number; argv: string[]; env?: Record<string, string>; }) => { const saved = { ...process.env }; const cwd = process.cwd(); Object.assign(process.env, env ?? {}); let code: number; try { code = await run(argv); } catch (e) { console.error(e instanceof Error ? (e.stack ?? e.message) : String(e)); code = 1; } for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k]; Object.assign(process.env, saved); if (process.cwd() !== cwd) process.chdir(cwd); process.exitCode = undefined; await new Promise((r) => process.stderr.write(`\0tcm-done ${id}\n`, r)); await new Promise((r) => process.stdout.write(`\0tcm-done ${id} ${Math.max(code, 0)}\n`, r)); }; return new Promise((resolve) => { let buffer = ""; let chain = Promise.resolve(); process.stdin.setEncoding("utf8"); process.stdin.on("data", (d: string) => { buffer += d; for (let nl = buffer.indexOf("\n"); nl >= 0; nl = buffer.indexOf("\n")) { const line = buffer.slice(0, nl).trim(); buffer = buffer.slice(nl + 1); if (line) chain = chain.then(() => one(JSON.parse(line) as Parameters<typeof one>[0])); } }); process.stdin.on("end", () => void chain.then(() => resolve())); }); }