serveWorker

tcmts.cli.mcp.serveWorker · #x33ryt6p · term · runs on the server

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()));
  });
}

used by