pub const SCRIPT: &str = "// The QCode bridge: a Model Context Protocol server that lets the agent of one QCode tab learn\n// which tab it is, list the other tabs of its workspace, send a message to one or all of them,\n// and take the messages waiting for its own tab.\n//\n// A harness starts this file with `node` over stdio. It has no dependencies, because it runs in\n// every profile image and nothing but Node is promised there. It decides nothing itself: every\n// question goes to QCode on the host, through the workspace\'s socket in the folder this file lives\n// in, and QCode answers with the words the agent is shown. That keeps the rules (the person\'s\n// approval, the network direction, the loop limits) in one place, where the person is.\n//\n// The wire format is newline-delimited JSON-RPC 2.0, as the stdio transport of MCP defines it.\n// Both eras of the protocol are served: the `initialize` handshake of the revisions up to\n// 2025-11-25, and the per-request metadata and `server/discover` of 2026-07-28.\n//\n// The same tools reach opencode\'s shared server as a plugin (`qcode-opencode-plugin.mjs`), which\n// imports what it needs from here; this file only serves stdio when it is the program started.\n\nimport { createConnection } from \"node:net\";\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createInterface } from \"node:readline\";\n\nconst SOCKET = join(dirname(fileURLToPath(import.meta.url)), \"bridge.sock\");\nconst SERVER = { name: \"qcode\", version: \"1\" };\nconst MODERN = \"2026-07-28\";\nconst LEGACY = [\"2025-11-25\", \"2025-06-18\", \"2025-03-26\", \"2024-11-05\"];\nconst VERSION_KEY = \"io.modelcontextprotocol/protocolVersion\";\n// Longer than any approval QCode waits for before it answers on its own.\nconst HOST_WAIT_MS = 120000;\n\nexport const INSTRUCTIONS =\n \"Other tabs of this QCode workspace run agents too. list_tabs says which tab you are (`you`, \" +\n \"and its first line) and names the others; only list_tabs tells you your own id. send_message \" +\n \"hands one of them, or `all` of them, a message of a kind: `info` (no answer needed, the \" +\n \"default), `question` (you wait for the answer) or `report` (a task: you wait for a report of \" +\n \"the result). A message arrives under two lines naming its sender\'s tab id, its kind and how to \" +\n \"answer; answer a question, or report a task\'s result, with send_message to that id and kind \" +\n \"`info`. QCode may refuse a message, or wait for the person to allow it; the answer always says \" +\n \"what happened and why. A message is written into the other tab\'s prompt once that tab is quiet, \" +\n \"so it may still be waiting after send_message answers: list_tabs says how many messages each \" +\n \"tab still holds and what is stopping them. A window tab has no prompt: messages to it wait \" +\n \"until its agent calls check_inbox, and an agent in a window calls check_inbox at the start and \" +\n \"the end of every task.\";\n\nexport const TOOLS = [\n {\n name: \"list_tabs\",\n title: \"Say which tab you are and list the other agent tabs\",\n description:\n \"Says which tab you are (`you`: your id, title, coding tool, profile and workspace; the \" +\n \"first line of the text says it too) and lists the other tabs of this QCode workspace that \" +\n \"run an agent: the id to send to, the tab\'s title, its coding tool, its profile, whether it \" +\n \"reaches the network, how many messages are still waiting to be written into it (`waiting`) \" +\n \"and what is stopping them (`trouble`, null when nothing is). Ids count within this \" +\n \"workspace. Call it again to see whether a message you sent has arrived.\",\n inputSchema: { type: \"object\", additionalProperties: false },\n },\n {\n name: \"send_message\",\n title: \"Send a message to another agent tab\",\n description:\n \"Sends a message to the agent of another tab of this QCode workspace, by the id list_tabs \" +\n \"gave, or to every other agent tab with `all`. The message is written into that tab\'s \" +\n \"prompt under two lines naming this tab as its sender, the kind and how to answer, once the \" +\n \"tab is quiet: nobody typing in it and its tool done writing. The answer says whether the \" +\n \"message went in, is still waiting to go in, is waiting for the person\'s approval, or was \" +\n \"refused, and why; for `all`, tab by tab. Do not take a message that is still waiting for \" +\n \"delivered work; list_tabs says whether it has arrived.\",\n inputSchema: {\n type: \"object\",\n properties: {\n tab: { type: \"string\", description: \"The id of the tab, as list_tabs gives it, or `all` for every other agent tab.\" },\n text: { type: \"string\", description: \"The message, written for the agent that reads it.\" },\n kind: {\n type: \"string\",\n enum: [\"info\", \"question\", \"report\"],\n description:\n \"What you ask of the receiver: `info` for their information, no answer needed (the \" +\n \"default); `question` when you wait for their answer; `report` for a task whose result \" +\n \"they report when they finish. An answer to a question, and a report, are `info`.\",\n },\n },\n required: [\"tab\", \"text\"],\n additionalProperties: false,\n },\n },\n {\n name: \"check_inbox\",\n title: \"Take the messages waiting for this tab\",\n description:\n \"Returns every message other tabs of this QCode workspace sent to this tab that has not \" +\n \"reached you yet, each with the id of the tab that sent it and its kind, and takes them out of QCode: they are \" +\n \"yours now. A tab in a window (Antigravity IDE) has no prompt, so this is the only way its \" +\n \"messages reach it: call it at the start of every task and when you finish one. Answer a \" +\n \"message with send_message to the tab that sent it.\",\n inputSchema: { type: \"object\", additionalProperties: false },\n },\n];\n\n// The tab this server speaks for. QCode starts every harness tab with the variable set, and a\n// harness passes its environment on to the servers it starts; one that passes only some of it\n// still has the variable in its own process, so the parents are asked in turn.\nfunction token() {\n if (process.env.QCODE_BRIDGE) return process.env.QCODE_BRIDGE;\n let pid = process.ppid;\n for (let depth = 0; pid > 1 && depth < 16; depth += 1) {\n try {\n const environment = readFileSync(`/proc/${pid}/environ`, \"latin1\").split(\"\\0\");\n const found = environment.find((entry) => entry.startsWith(\"QCODE_BRIDGE=\"));\n if (found) return found.slice(\"QCODE_BRIDGE=\".length);\n const stat = readFileSync(`/proc/${pid}/stat`, \"latin1\");\n pid = Number(stat.slice(stat.lastIndexOf(\")\") + 2).split(\" \")[1]);\n } catch {\n return \"\";\n }\n }\n return \"\";\n}\n\nconst TOKEN = token();\n\n// Asks QCode one question and answers its one-line reply. `extra` is what a question carries\n// beside the token: the conversation, when the tab is told apart by it (opencode\'s shared\n// server speaks for every tab of its profile with one token).\nfunction ask(request, extra = {}) {\n return new Promise((resolve) => {\n const socket = createConnection(SOCKET);\n let received = \"\";\n let settled = false;\n const finish = (answer) => {\n if (settled) return;\n settled = true;\n socket.destroy();\n resolve(answer);\n };\n socket.setTimeout(HOST_WAIT_MS, () => finish(null));\n socket.on(\"connect\", () => socket.write(JSON.stringify({ token: TOKEN, ...extra, ...request }) + \"\\n\"));\n socket.on(\"data\", (chunk) => {\n received += chunk.toString(\"utf8\");\n const end = received.indexOf(\"\\n\");\n if (end >= 0) {\n try {\n finish(JSON.parse(received.slice(0, end)));\n } catch {\n finish(null);\n }\n }\n });\n socket.on(\"error\", () => finish(null));\n socket.on(\"close\", () => finish(null));\n });\n}\n\nfunction text(value, isError) {\n return { content: [{ type: \"text\", text: value }], isError };\n}\n\nconst UNREACHABLE =\n \"QCode did not answer: it is not running, or this workspace is not open in it. Nothing was sent.\";\n\nexport async function call(params, extra = {}) {\n const name = params?.name;\n const args = params?.arguments ?? {};\n if (name === \"list_tabs\") {\n const answer = await ask({ op: \"list\" }, extra);\n if (!answer) return text(UNREACHABLE, true);\n const result = text(answer.text, !answer.ok);\n if (answer.ok) result.structuredContent = { you: answer.you ?? null, tabs: answer.tabs ?? [] };\n return result;\n }\n if (name === \"send_message\") {\n if (typeof args.tab !== \"string\" || typeof args.text !== \"string\") {\n return text(\"send_message needs `tab` and `text`, both strings.\", true);\n }\n const kind = args.kind ?? \"info\";\n if (![\"info\", \"question\", \"report\"].includes(kind)) {\n return text(\"`kind` is one of info, question and report. Nothing was sent.\", true);\n }\n const answer = await ask({ op: \"send\", tab: args.tab, text: args.text, kind }, extra);\n if (!answer) return text(UNREACHABLE, true);\n const result = text(answer.text, !answer.ok);\n if (answer.sent) result.structuredContent = { sent: answer.sent };\n return result;\n }\n if (name === \"check_inbox\") {\n const answer = await ask({ op: \"inbox\" }, extra);\n if (!answer) return text(UNREACHABLE, true);\n const result = text(answer.text, !answer.ok);\n if (answer.ok) result.structuredContent = { messages: answer.messages ?? [] };\n return result;\n }\n return null;\n}\n\nfunction reply(id, result) {\n return { jsonrpc: \"2.0\", id, result };\n}\n\nfunction failure(id, code, message, data) {\n return { jsonrpc: \"2.0\", id, error: data === undefined ? { code, message } : { code, message, data } };\n}\n\n// Answers one message, or nothing for a notification.\nasync function handle(message) {\n if (message === null || typeof message !== \"object\" || Array.isArray(message)) {\n return failure(null, -32600, \"Invalid Request\");\n }\n const { id, method, params } = message;\n const isRequest = id !== undefined && id !== null;\n if (typeof method !== \"string\") return isRequest ? failure(id, -32600, \"Invalid Request\") : null;\n if (!isRequest) return null;\n\n // A request that carries its version is of the modern era, and is answered in it.\n const version = params?._meta?.[VERSION_KEY];\n const modern = version !== undefined;\n if (modern && version !== MODERN) {\n return failure(id, -32022, \"Unsupported protocol version\", { supported: [MODERN], requested: version });\n }\n const complete = (result) => reply(id, modern ? { resultType: \"complete\", ...result } : result);\n\n switch (method) {\n case \"initialize\": {\n const asked = params?.protocolVersion;\n return reply(id, {\n protocolVersion: LEGACY.includes(asked) ? asked : LEGACY[0],\n capabilities: { tools: {} },\n serverInfo: SERVER,\n instructions: INSTRUCTIONS,\n });\n }\n case \"server/discover\":\n return complete({\n supportedVersions: [MODERN],\n capabilities: { tools: {} },\n _meta: { \"io.modelcontextprotocol/serverInfo\": SERVER },\n instructions: INSTRUCTIONS,\n });\n case \"ping\":\n return complete({});\n case \"tools/list\":\n return complete({ tools: TOOLS });\n case \"tools/call\": {\n const result = await call(params);\n if (result === null) return failure(id, -32602, `Unknown tool: ${params?.name}`);\n return complete(result);\n }\n default:\n return failure(id, -32601, `Method not found: ${method}`);\n }\n}\n\nfunction send(message) {\n if (message !== null) process.stdout.write(JSON.stringify(message) + \"\\n\");\n}\n\n// Questions still waiting for QCode when the harness closes the input are answered before the\n// server leaves, since the harness may still be reading.\nlet pending = 0;\nlet closed = false;\nconst leaveWhenDone = () => {\n if (closed && pending === 0) process.exit(0);\n};\n\nasync function receive(line) {\n let parsed;\n try {\n parsed = JSON.parse(line);\n } catch {\n send(failure(null, -32700, \"Parse error\"));\n return;\n }\n if (Array.isArray(parsed)) {\n const answers = (await Promise.all(parsed.map(handle))).filter((answer) => answer !== null);\n if (answers.length > 0) send(answers);\n return;\n }\n send(await handle(parsed));\n}\n\n// Whether this file is the program that was started, rather than a module another one imported.\nfunction started() {\n try {\n return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n return false;\n }\n}\n\nif (started()) serve();\n\nfunction serve() {\n const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });\n lines.on(\"line\", async (line) => {\n if (line.trim() === \"\") return;\n pending += 1;\n try {\n await receive(line);\n } finally {\n pending -= 1;\n leaveWhenDone();\n }\n });\n lines.on(\"close\", () => {\n closed = true;\n leaveWhenDone();\n });\n}\n";Expand description
The MCP server a harness starts, carried inside the binary and written into each open
workspace’s Containers/MCP/ folder, so the server a container starts is always the one this
QCode speaks with.