import assert from "node:assert/strict"; import test from "node:test"; import { formatFailure, startCraftos, startSandbox, waitFor, type Sandbox } from "./harness.js"; // Drives the MCP tools end to end: a real reactive mcp-computer server (libsandbox + // libmcpcomputer) on a real CraftOS-PC client, reached through the real gateway. The // MCP app is driven in-process via inject() (no extra listener). async function callTool(sandbox: Sandbox, name: string, args: Record = {}): Promise { const res = await sandbox.mcpApp.inject({ method: "POST", url: "/", payload: { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name, arguments: args } }, }); const body = res.json<{ result?: { content?: { text?: string }[] }; error?: { message?: string } }>(); if (body.error) { throw new Error(`tool ${name} returned a JSON-RPC error: ${body.error.message}`); } return body.result?.content?.[0]?.text ?? ""; } test("MCP exec-lua / run-file / write-file / probe round-trip over the gateway", async () => { const sandbox = await startSandbox(); const craftos = startCraftos("mcp-server-client.lua", { computerId: 7, computerLabel: "base", shellArgs: [sandbox.baseUrl, "-"], }); try { await waitFor(() => sandbox.registry.has("local", "7:base"), 12_000, "computer registered"); assert.equal(await callTool(sandbox, "probe-computers"), "ok from 7:base"); const exec = await callTool(sandbox, "exec-lua", { traposId: "7:base", code: "print('hi'); return 1 + 1" }); assert.match(exec, /^computer: 7:base\nok: true/); assert.match(exec, /"value":2/); // return value serialized assert.match(exec, /\nhi$/); // captured print output const program = "local a, b = ...; print(a .. ':' .. b); return arg[1], arg[2], shell.getRunningProgram()"; const write = await callTool(sandbox, "write-file", { traposId: "7:base", path: "/mcp-it.lua", content: program, }); assert.match(write, /ok: true/); assert.match(write, new RegExp(`bytes: ${program.length}`)); const run = await callTool(sandbox, "run-file", { traposId: "7:base", path: "/mcp-it.lua", args: ["install", "pkg"] }); assert.match(run, /^computer: 7:base\nok: true/); assert.match(run, /"value":"install"/); assert.match(run, /"value":"pkg"/); assert.match(run, /"value":"\/mcp-it\.lua"/); assert.match(run, /\ninstall:pkg$/); // Read it back through exec-lua to prove the write actually landed. const verify = await callTool(sandbox, "exec-lua", { traposId: "7:base", code: "local h = fs.open('/mcp-it.lua', 'r'); local c = h.readAll(); h.close(); return c", }); assert.match(verify, /"shell.getRunningProgram/); // Unknown traposId is a transport (not_connected) failure, not a crash. const ghost = await callTool(sandbox, "exec-lua", { traposId: "9:ghost", code: "return 1" }); assert.match(ghost, /^computer: 9:ghost\nok: false\nerror: not_connected:/); } catch (error) { throw new Error(formatFailure(error instanceof Error ? error.message : String(error), craftos.snapshot()), { cause: error, }); } finally { craftos.abort(); await craftos.done.catch(() => undefined); await sandbox.close(); } });