import { assertEquals } from "@std/assert"; import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context"; import { rootedEnvironment, testExtension } from "@berry/api/testing.ts"; import { onPath, tempDir, until } from "@berry/util"; import mcp from "../../extensions/mcp/mod.ts"; import { BOOTSTRAP, Connection, RemoteEnvironment } from "./connection.ts"; for (const where of ["local", "remote"]) { for (const mode of ["escaped", "quiet-child"]) { Deno.test({ name: `${where} MCP disposal handles ${ mode === "escaped" ? "escaped descendants holding stderr" : "pipe-free descendants surviving their leader" }`, ignore: Deno.build.os !== "linux" || (mode === "escaped" && !onPath("setsid")), async fn() { await using dir = await tempDir({ prefix: "mcp-descendant-" }); await Deno.copyFile( new URL("../../extensions/mcp/test-server.ts", import.meta.url), dir.path + "/server.ts", ); await Deno.writeTextFile( dir.path + "/mcp.json", JSON.stringify({ mcpServers: { probe: { command: Deno.execPath(), args: ["run", "-A", "server.ts", mode] }, }, }), ); const connection = where === "remote" ? await Connection.open("descendant", [Deno.execPath(), "eval", "--quiet", BOOTSTRAP]) : undefined; const env = connection ? new RemoteEnvironment("descendant", connection, dir.path) : rootedEnvironment(dir.path, { cwd: "/" }); const ext = testExtension(mcp, { env }); let pid: number | undefined; try { await ext.configure({ configFile: "mcp.json" }); const listed = await ext.call("mcp_list", {}) as { status: string }[]; assertEquals(listed[0].status, "connected"); pid = Number(await Deno.readTextFile(dir.path + "/descendant")); let disposed = false; const disposing = ext.dispose().then(() => disposed = true); await until(() => disposed, { message: "MCP disposal waited indefinitely for inherited stderr", }); await disposing; if (mode === "quiet-child") { await until(async () => { const stat = await Deno.readTextFile(`/proc/${pid}/stat`).catch(() => ""); return !stat || stat.slice(stat.lastIndexOf(")") + 2).startsWith("Z"); }, { message: "an in-group descendant survived MCP disposal" }); } assertEquals((await env.readTextFile("mcp.json", context)).ok, true); } finally { if (pid) { try { Deno.kill(pid, "SIGKILL"); } catch { /* already gone */ } } await ext.dispose(); await connection?.close(); } }, }); } }