// Minimal stdio MCP server that gives a Claude Code session its typed // tools: `node mcp-server.mjs `. Newline-delimited // JSON-RPC 2.0 with initialize, tools/list, tools/call and ping. Each call // goes to the runner's tool socket; the server holds no capability. A // reply over REPLY_CAP is a tool error (tools.mjs, replyText). import { readFileSync } from "node:fs"; import { createInterface } from "node:readline"; import { callTool, replyText } from "./tools.mjs"; const [toolsFile, socket] = process.argv.slice(2); if (!toolsFile || !socket) { process.stderr.write("usage: mcp-server.mjs \n"); process.exit(2); } const tools = JSON.parse(readFileSync(toolsFile, "utf8")); const byName = new Map(tools.map((t) => [t.name, t])); const send = (message) => process.stdout.write(JSON.stringify({ jsonrpc: "2.0", ...message }) + "\n"); const reply = (id, result) => send({ id, result }); const error = (id, code, message) => send({ id, error: { code, message } }); async function handle(m) { const id = m.id; switch (m.method) { case "initialize": return reply(id, { protocolVersion: m.params?.protocolVersion ?? "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "mosaic", version: "1" }, }); case "ping": return reply(id, {}); case "tools/list": return reply(id, { tools: tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.parameters })) }); case "tools/call": { const name = m.params?.name; if (!byName.has(name)) return error(id, -32602, `unknown tool: ${name}`); try { const result = await callTool(socket, name, m.params?.arguments ?? {}); return reply(id, { content: [{ type: "text", text: replyText(result) }] }); } catch (e) { return reply(id, { content: [{ type: "text", text: e.message }], isError: true }); } } default: if (id !== undefined) return error(id, -32601, `method not found: ${m.method}`); } } createInterface({ input: process.stdin }).on("line", (line) => { if (!line.trim()) return; let m; try { m = JSON.parse(line); } catch { return error(null, -32700, "parse error"); } handle(m).catch((e) => m.id !== undefined && error(m.id, -32603, e.message)); });