Skip to main content

SCRIPT

Constant SCRIPT 

Source
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.