import { existsSync } from "node:fs"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { describe, expect, it } from "vitest"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import { MCP_SERVER_NAME } from "../src/mcp-server.js"; import { newProject } from "../src/template.js"; const entry = resolve(process.cwd(), "dist/main/mcp-stdio.js"); function textOf(result: { content: { type: string; text?: string }[] }): string { return result.content.find((block) => block.type === "text")?.text ?? ""; } describe("editor --mcp-stdio headless mode (spawned entry over stdio)", () => { it("spawns a drop-in stdio MCP server for --dir: handshake + full surface + a tool call", async () => { expect(existsSync(entry)).toBe(true); const dir = await mkdtemp(join(tmpdir(), "editor-stdio-")); try { const project = await newProject("Stdio Farm", dir); const client = new Client({ name: "editor-stdio-test", version: "0.0.1" }); const transport = new StdioClientTransport({ command: "node", args: [entry, "--dir", project.root] }); await client.connect(transport); try { expect(client.getServerVersion()).toMatchObject({ name: MCP_SERVER_NAME, version: "0.1.0" }); const tools = await client.listTools(); expect(tools.tools).toHaveLength(124); const info = JSON.parse(textOf(await client.callTool({ name: "server.info", arguments: {} }))) as { engine: string; content: { map: string[]; time: string[]; item: string[] }; }; expect(info.engine).toBe("nengine"); expect(info.content.map).toEqual(["stdio-farm"]); expect(info.content.item.sort()).toEqual(["hoe", "stdio-farm", "stdio-farm-seed"]); } finally { await client.close(); } } finally { await rm(dir, { recursive: true, force: true }); } }); it("item.create over stdio writes content into the project's content dir", async () => { expect(existsSync(entry)).toBe(true); const dir = await mkdtemp(join(tmpdir(), "editor-stdio-")); try { const project = await newProject("Stdio Farm", dir); const client = new Client({ name: "editor-stdio-test", version: "0.0.1" }); const transport = new StdioClientTransport({ command: "node", args: [entry, "--dir", project.root] }); await client.connect(transport); try { const created = await client.callTool({ name: "item.create", arguments: { def: { id: "sickle", name: "Sickle", kind: "tool", stackable: false } }, }); expect(created.isError).not.toBe(true); const file = resolve(project.root, "content/items/sickle.yaml"); expect(existsSync(file)).toBe(true); } finally { await client.close(); } } finally { await rm(dir, { recursive: true, force: true }); } }); });