{"version":3,"file":"server-B8cpNdpi.mjs","names":["Buffer","osSep","toStandardJSONSchema","Buffer","packageJSON.version"],"sources":["../src/auth.ts","../src/browser-rpc.ts","../src/http-options.ts","../src/server/sessions.ts","../src/result.ts","../src/tool/output.ts","../src/tool/registration.ts","../package.json","../src/server/lifecycle.ts","../src/server.ts"],"sourcesContent":["import { createHash, timingSafeEqual } from 'node:crypto'\n\nexport function bearerToken(header: string | undefined | null): string | null {\n  return header?.startsWith('Bearer ') ? header.slice('Bearer '.length) : null\n}\n\nexport function mcpRequestToken(\n  authorization: string | undefined | null,\n  headerToken: string | undefined | null\n): string | null {\n  return bearerToken(authorization) ?? headerToken ?? null\n}\n\nexport function isAuthorized(provided: string | null, expected: string | null): boolean {\n  if (expected === null) return true\n  if (provided === null) return false\n\n  const providedDigest = createHash('sha256').update(provided, 'utf8').digest()\n  const expectedDigest = createHash('sha256').update(expected, 'utf8').digest()\n  return timingSafeEqual(providedDigest, expectedDigest)\n}\n","import { randomUUID } from 'node:crypto'\n\nimport * as v from 'valibot'\nimport type { WebSocket } from 'ws'\n\nimport { isAuthorized } from '#mcp/auth'\nimport type { RPCJSONObject } from '#mcp/json'\nimport type { PendingRequest } from '#mcp/rpc-types'\n\nconst RPC_TIMEOUT = 20_000\nconst APP_WAIT_TIMEOUT = 10_000\n\nconst APP_NOT_CONNECTED_MESSAGE =\n  'OpenPencil app is not connected. STOP and tell the user: \"The OpenPencil desktop app is not running, no document is open, or the desktop app is connected to a different MCP server. Please start OpenPencil, open a document, and try again.\" Do NOT attempt to start the app yourself or retry automatically.'\n\ntype BrowserRPCBridgeOptions = {\n  authToken: string | null\n  onConnectionChange: () => void\n  /** How long an RPC waits for the app to register before failing with APP_NOT_CONNECTED. Defaults to 10 s. */\n  appWaitTimeoutMs?: number\n}\n\ntype ConnectionListener = (connected: boolean) => void\n\n/** Any JSON object; the per-type schemas below run only once its `type` is known. */\nconst BrowserEnvelopeJSON = v.pipe(\n  v.string(),\n  v.parseJson(),\n  v.custom<Record<string, unknown>>(\n    (value) => typeof value === 'object' && value !== null && !Array.isArray(value)\n  )\n)\n\nconst HandshakeMessage = v.looseObject({ token: v.optional(v.nullable(v.string())) })\n\nconst RequestMessage = v.looseObject({\n  type: v.literal('request'),\n  id: v.pipe(v.string(), v.nonEmpty())\n})\n\nconst ResponseMessage = v.looseObject({\n  type: v.literal('response'),\n  id: v.pipe(v.string(), v.nonEmpty()),\n  ok: v.optional(v.boolean()),\n  error: v.optional(v.string())\n})\n\ntype BrowserMessage = v.InferOutput<typeof RequestMessage> | v.InferOutput<typeof ResponseMessage>\n\nfunction stripEnvelope(msg: BrowserMessage): Record<string, unknown> {\n  const { type: _type, id: _id, ...body } = msg\n  return body\n}\n\nfunction responsePayload(result: unknown): RPCJSONObject {\n  if (result && typeof result === 'object' && !Array.isArray(result)) {\n    return result as RPCJSONObject\n  }\n  return { result }\n}\n\nfunction sendJSON(ws: WebSocket, body: Record<string, unknown>) {\n  if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(body))\n}\n\nfunction createSettler<T>(resolve: (value: T) => void, reject: (error: Error) => void) {\n  let settled = false\n  return {\n    resolve: (value: T) => {\n      if (settled) return\n      settled = true\n      resolve(value)\n    },\n    reject: (error: Error) => {\n      if (settled) return\n      settled = true\n      reject(error)\n    },\n    isSettled: () => settled\n  }\n}\n\nexport function createBrowserRPCBridge({\n  authToken,\n  onConnectionChange,\n  appWaitTimeoutMs = APP_WAIT_TIMEOUT\n}: BrowserRPCBridgeOptions) {\n  const pending = new Map<string, PendingRequest>()\n  const clients = new Set<WebSocket>()\n  const connectionWaiters = new Set<PendingRequest>()\n  // Track which WebSocket clients have authenticated via a valid\n  // register message. Unauthenticated clients can only send register;\n  // all other message types (request, response) are rejected.\n  const authenticatedClients = new Set<WebSocket>()\n  const connectionListeners = new Set<ConnectionListener>()\n  let browserWs: WebSocket | null = null\n  let browserRegistered = false\n  let bridgeClosed = false\n\n  function isConnected(): boolean {\n    return Boolean(browserWs && browserRegistered)\n  }\n\n  function notifyConnectionChange() {\n    onConnectionChange()\n    const connected = isConnected()\n    for (const listener of connectionListeners) listener(connected)\n  }\n\n  function subscribeConnectionChange(listener: ConnectionListener): () => void {\n    connectionListeners.add(listener)\n    listener(isConnected())\n    return () => connectionListeners.delete(listener)\n  }\n\n  function notifyConnectionWaiters() {\n    for (const waiter of connectionWaiters) {\n      clearTimeout(waiter.timer)\n      waiter.resolve(undefined)\n    }\n    connectionWaiters.clear()\n  }\n\n  function rejectConnectionWaiters(reason: string) {\n    for (const waiter of connectionWaiters) {\n      clearTimeout(waiter.timer)\n      waiter.reject(new Error(reason))\n    }\n    connectionWaiters.clear()\n  }\n\n  function waitForConnection(): Promise<void> {\n    return new Promise<void>((resolve, reject) => {\n      let waiter: PendingRequest | null = null\n\n      const timer = setTimeout(() => {\n        if (waiter) connectionWaiters.delete(waiter)\n        reject(new Error(APP_NOT_CONNECTED_MESSAGE))\n      }, appWaitTimeoutMs)\n\n      waiter = {\n        resolve: () => {\n          clearTimeout(timer)\n          resolve()\n        },\n        reject: (error: Error) => {\n          clearTimeout(timer)\n          reject(error)\n        },\n        timer\n      }\n      // Add the waiter BEFORE checking browser state to avoid a lost-wakeup\n      // race: if the browser registers between sendRPC's initial check and\n      // this point, notifyConnectionWaiters() will have already fired and\n      // cleared the set. Without this re-check, the waiter would stall for\n      // appWaitTimeoutMs even though the browser is connected.\n      connectionWaiters.add(waiter)\n      if (browserWs && browserWs.readyState === browserWs.OPEN && browserRegistered) {\n        waiter.resolve(undefined)\n        connectionWaiters.delete(waiter)\n      }\n    })\n  }\n\n  function rejectAllPending(reason: string) {\n    for (const [, req] of pending) {\n      clearTimeout(req.timer)\n      req.reject(new Error(reason))\n    }\n    pending.clear()\n  }\n\n  function sendRegisterPrompt(ws: WebSocket) {\n    // Send a prompt inviting the client to register as the browser.\n    // IMPORTANT: Do NOT include the auth token here. On TCP, any local\n    // process can connect via WebSocket — sending the secret token would\n    // leak credentials to unauthenticated clients. The legitimate browser\n    // app already knows the token (from the discovery file or Vite env)\n    // and sends it proactively in its ws.onopen handler. The token field\n    // is set to null to signal that auth is required without revealing\n    // the secret. When auth is disabled (authToken === null), null is\n    // still correct — it means \"no token needed.\"\n    sendJSON(ws, { type: 'register', token: null })\n  }\n\n  function broadcastRegisterPrompt() {\n    for (const client of clients) sendRegisterPrompt(client)\n  }\n\n  function sendRPC(body: Record<string, unknown>): Promise<unknown> {\n    if (bridgeClosed) return Promise.reject(new Error('Server shutting down'))\n    return new Promise((resolve, reject) => {\n      const doSend = () => {\n        const ws = browserWs\n        if (!ws || ws.readyState !== ws.OPEN || !browserRegistered) {\n          reject(new Error(APP_NOT_CONNECTED_MESSAGE))\n          return\n        }\n        const id = randomUUID()\n        const settle = createSettler(resolve, reject)\n        const timer = setTimeout(() => {\n          pending.delete(id)\n          settle.reject(new Error(`RPC timeout (${Math.round(RPC_TIMEOUT / 1000)}s)`))\n        }, RPC_TIMEOUT)\n        pending.set(id, { resolve: settle.resolve, reject: settle.reject, timer })\n        try {\n          ws.send(JSON.stringify({ ...body, type: 'request', id }))\n        } catch (e) {\n          clearTimeout(timer)\n          pending.delete(id)\n          if (!settle.isSettled()) {\n            settle.reject(e instanceof Error ? e : new Error(String(e)))\n          }\n        }\n      }\n\n      if (browserWs && browserWs.readyState === browserWs.OPEN && browserRegistered) {\n        doSend()\n      } else {\n        void waitForConnection().then(doSend).catch(reject)\n      }\n    })\n  }\n\n  async function handleClientRequest(ws: WebSocket, msg: v.InferOutput<typeof RequestMessage>) {\n    try {\n      const result = await sendRPC(stripEnvelope(msg))\n      sendJSON(ws, { ...responsePayload(result), type: 'response', id: msg.id, ok: true })\n    } catch (e) {\n      sendJSON(ws, {\n        type: 'response',\n        id: msg.id,\n        ok: false,\n        error: e instanceof Error ? e.message : String(e)\n      })\n    }\n  }\n\n  function registerBrowser(ws: WebSocket, token: string | null) {\n    if (bridgeClosed) return\n    if (!isAuthorized(token, authToken)) {\n      ws.close()\n      return\n    }\n    // Mark this client as authenticated — it can now send requests.\n    authenticatedClients.add(ws)\n    const previousBrowserWs = browserWs\n    browserWs = ws\n    browserRegistered = true\n    if (previousBrowserWs && previousBrowserWs !== ws) {\n      // Reject in-flight requests to the old browser. Without this, pending\n      // requests sit in the pending map until RPC_TIMEOUT (20s), because\n      // handleClose for the old socket returns early (browserWs is already\n      // set to the new socket, so browserWs !== previousBrowserWs).\n      rejectAllPending('Browser reconnected')\n      if (previousBrowserWs.readyState === ws.OPEN) {\n        previousBrowserWs.close()\n      }\n    }\n    notifyConnectionWaiters()\n    notifyConnectionChange()\n    broadcastRegisterPrompt()\n  }\n\n  function handleBrowserResponse(msg: v.InferOutput<typeof ResponseMessage>, ws: WebSocket) {\n    if (!browserRegistered || browserWs !== ws) return\n    const req = pending.get(msg.id)\n    if (!req) return\n    pending.delete(msg.id)\n    clearTimeout(req.timer)\n    if (msg.ok === false) {\n      req.reject(new Error(msg.error ?? 'RPC failed'))\n    } else {\n      req.resolve(stripEnvelope(msg))\n    }\n  }\n\n  function handleMessage(data: string, ws: WebSocket) {\n    if (bridgeClosed) return\n    const envelope = v.safeParse(BrowserEnvelopeJSON, data)\n    if (!envelope.success) {\n      if (envelope.issues.some((issue) => issue.type === 'parse_json')) {\n        console.warn('Malformed automation message:', v.summarize(envelope.issues))\n      } else {\n        ws.close()\n      }\n      return\n    }\n    const msg = envelope.output\n\n    if (msg.type === 'auth' || msg.type === 'register') {\n      // `auth` authenticates a stdio bridge client without registering it as\n      // the browser app, so it can send request/response messages without\n      // becoming the RPC target. The token is validated the same way as\n      // registerBrowser — when auth is disabled (authToken === null), any\n      // token is accepted. A message without a token is ignored; a token of\n      // any other type closes the socket.\n      const handshake = v.safeParse(HandshakeMessage, msg)\n      if (!handshake.success) {\n        ws.close()\n        return\n      }\n      const { token } = handshake.output\n      if (token === undefined) return\n      if (msg.type === 'register') {\n        registerBrowser(ws, token)\n      } else if (isAuthorized(token, authToken)) {\n        authenticatedClients.add(ws)\n      } else {\n        ws.close()\n      }\n      return\n    }\n    // All non-register messages require authentication. Without this\n    // check, an unauthenticated WebSocket client (that hasn't sent a\n    // valid register message) could bypass the HTTP auth on /rpc by\n    // sending request messages over the WebSocket directly.\n    if (!authenticatedClients.has(ws)) {\n      ws.close()\n      return\n    }\n    const request = v.safeParse(RequestMessage, msg)\n    if (request.success) {\n      void handleClientRequest(ws, request.output)\n      return\n    }\n    const response = v.safeParse(ResponseMessage, msg)\n    if (response.success) handleBrowserResponse(response.output, ws)\n  }\n\n  function handleClose(ws: WebSocket) {\n    clients.delete(ws)\n    authenticatedClients.delete(ws)\n    if (browserWs !== ws) return\n    browserWs = null\n    browserRegistered = false\n    rejectAllPending('Browser disconnected')\n    // Intentionally NOT rejecting connectionWaiters here. If a request\n    // entered waitForConnection() before the close event (e.g. during a\n    // CLOSING→CLOSED transition), the waiter should keep waiting the full\n    // APP_WAIT_TIMEOUT for a reconnect. registerBrowser will resolve it\n    // via notifyConnectionWaiters if the browser reconnects in time.\n    notifyConnectionChange()\n  }\n\n  function handleConnection(ws: WebSocket) {\n    if (bridgeClosed) return\n    clients.add(ws)\n    // Transport security restricts WHO can connect: Unix socket with\n    // 0o600 permissions (same-user only) or TCP localhost (any local\n    // process). The authenticatedClients set gates WHAT connected clients\n    // can do: only those that sent a valid register message may forward\n    // requests. The register prompt does NOT include the auth token —\n    // the browser app sends the token proactively (from the discovery\n    // file or Vite env).\n    sendRegisterPrompt(ws)\n  }\n\n  function close() {\n    bridgeClosed = true\n    rejectAllPending('Server shutting down')\n    rejectConnectionWaiters('Server shutting down')\n    browserWs = null\n    browserRegistered = false\n    clients.clear()\n    authenticatedClients.clear()\n    connectionListeners.clear()\n  }\n\n  return {\n    close,\n    isConnected,\n    subscribeConnectionChange,\n    sendRPC,\n    handleConnection,\n    handleMessage,\n    handleClose\n  }\n}\n","export const MCP_CORS_METHODS = ['GET', 'POST', 'DELETE', 'OPTIONS']\n\n// x-mcp-token: OpenPencil extension for MCP clients that cannot set the\n// Authorization header (e.g. some IDE plugins). Not part of the MCP spec.\nexport const MCP_CORS_HEADERS = [\n  'Content-Type',\n  'Authorization',\n  'x-mcp-token',\n  'mcp-session-id',\n  'Last-Event-ID',\n  'mcp-protocol-version'\n]\n\nexport const MCP_EXPOSED_HEADERS = ['mcp-session-id', 'mcp-protocol-version']\n","import { randomUUID } from 'node:crypto'\n\nimport { McpServer, WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server'\n\nimport type { MCPAgentKind, MCPAgentSession } from '#mcp/tool/registration'\n\nexport type MCPTransport = {\n  handleRequest: (request: Request) => Promise<Response>\n  close: () => Promise<void>\n}\n\ntype MCPSession = {\n  transport: MCPTransport\n  server: McpServer\n  lastSeen: number\n}\n\ntype MCPSessionManagerOptions = {\n  serverVersion: string\n  registerTools: (server: McpServer, session: MCPAgentSession) => void\n  /** Told when a session ends, so the app stops showing its agent. */\n  onSessionClosed?: (sessionId: string) => void\n}\n\nconst MAX_MCP_SESSIONS = 10\nconst MCP_SESSION_TTL_MS = 15 * 60_000\nconst SESSION_CLOSE_TIMEOUT_MS = 5_000\n\nfunction describeError(e: unknown): string {\n  return e instanceof Error ? e.message : String(e)\n}\n\n/**\n * Wraps a promise with a timeout. Resolves with undefined if the timeout\n * elapses before the promise settles. Used to prevent `transport.close()`\n * from hanging indefinitely — the MCP SDK's close() does not enforce a\n * timeout internally.\n */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T | undefined> {\n  let timer: ReturnType<typeof setTimeout> | undefined\n  return Promise.race([\n    promise.finally(() => {\n      if (timer) clearTimeout(timer)\n    }),\n    new Promise<T | undefined>((resolve) => {\n      timer = setTimeout(() => resolve(undefined), ms)\n    })\n  ])\n}\n\nasync function closeSession(session: MCPSession): Promise<void> {\n  try {\n    await withTimeout(session.transport.close(), SESSION_CLOSE_TIMEOUT_MS)\n  } catch (e) {\n    // Best-effort: the transport may already be closed or in a bad state\n    process.stderr.write(`  MCP session: transport close warning (${describeError(e)})\\n`)\n  }\n  try {\n    await withTimeout(session.server.close(), SESSION_CLOSE_TIMEOUT_MS)\n  } catch (e) {\n    // Best-effort\n    process.stderr.write(`  MCP session: server close warning (${describeError(e)})\\n`)\n  }\n}\n\nexport function createMCPSessionManager({\n  serverVersion,\n  registerTools,\n  onSessionClosed\n}: MCPSessionManagerOptions) {\n  const sessions = new Map<string, MCPSession>()\n  const closing = new Set<Promise<void>>()\n\n  function scheduleClose(session: MCPSession, id?: string): Promise<void> {\n    if (id) onSessionClosed?.(id)\n    const task = closeSession(session).finally(() => closing.delete(task))\n    closing.add(task)\n    return task\n  }\n\n  function notifyToolsChanged() {\n    for (const session of sessions.values()) {\n      try {\n        session.server.sendToolListChanged()\n      } catch {\n        continue\n      }\n    }\n  }\n\n  function cleanupExpired() {\n    const now = Date.now()\n    for (const [id, session] of sessions) {\n      if (now - session.lastSeen > MCP_SESSION_TTL_MS) {\n        sessions.delete(id)\n        void scheduleClose(session, id)\n      }\n    }\n  }\n\n  const creating = new Map<string, Promise<MCPTransport>>()\n  let closed = false\n\n  async function createSession(id: string, kind: MCPAgentKind): Promise<MCPTransport> {\n    if (closed) throw new Error('Session manager is closed')\n    const inFlight = creating.get(id)\n    if (inFlight) return inFlight\n\n    const promise = (async () => {\n      const server = new McpServer({ name: 'open-pencil', version: serverVersion })\n      registerTools(server, { id, kind })\n\n      const transport = new WebStandardStreamableHTTPServerTransport({\n        sessionIdGenerator: () => id,\n        enableJsonResponse: true\n      })\n      // Await the MCP handshake before storing/returning the transport so\n      // handleRequest cannot race the SDK's initialization on a fresh\n      // session. Without this, the /mcp route calls resolveTransport() and\n      // immediately awaits handleRequest(), which can fail if connect() has\n      // not completed yet.\n      try {\n        await server.connect(transport)\n      } catch (e) {\n        await transport.close().catch(() => undefined)\n        await server.close().catch(() => undefined)\n        throw e\n      }\n      // `closed` is mutated by clear() which can run concurrently — TypeScript's\n      // control-flow analysis can't track this cross-closure mutation.\n      // oxlint-disable-next-line no-unnecessary-condition\n      if (closed) {\n        // Manager was closed while we were connecting — clean up immediately.\n        await transport.close().catch(() => undefined)\n        await server.close().catch(() => undefined)\n        throw new Error('Session manager closed during session creation')\n      }\n      sessions.set(id, { transport, server, lastSeen: Date.now() })\n      return transport\n    })()\n\n    creating.set(id, promise)\n    try {\n      return await promise\n    } finally {\n      creating.delete(id)\n    }\n  }\n\n  function resolveTransport(\n    sessionId: string | undefined,\n    kind: MCPAgentKind = 'mcp'\n  ): Promise<MCPTransport | { error: 'too_many' | 'closed' }> {\n    if (closed) return Promise.resolve({ error: 'closed' })\n    cleanupExpired()\n    const existing = sessionId ? sessions.get(sessionId) : undefined\n    if (existing) return Promise.resolve(existing.transport)\n    // Reuse an in-flight creation for the same sessionId before enforcing the cap.\n    if (sessionId) {\n      const inFlight = creating.get(sessionId)\n      if (inFlight) {\n        // Wrap with the same catch as createSession so clear() during\n        // in-flight creation returns { error: 'closed' } instead of throwing.\n        return inFlight.catch((e) => {\n          if (closed) return { error: 'closed' as const }\n          throw e\n        })\n      }\n    }\n    if (sessions.size + creating.size + closing.size >= MAX_MCP_SESSIONS) {\n      return Promise.resolve({ error: 'too_many' })\n    }\n    return createSession(sessionId ?? randomUUID(), kind).catch((e) => {\n      // If the manager was closed during session creation, return the structured\n      // error instead of letting the throw escape as a route-level 500.\n      if (closed) return { error: 'closed' as const }\n      throw e\n    })\n  }\n\n  function touch(sessionId: string | undefined, transport: MCPTransport) {\n    const resolvedSessionId =\n      sessionId ?? [...sessions.entries()].find(([, entry]) => entry.transport === transport)?.[0]\n    if (!resolvedSessionId) return\n    const session = sessions.get(resolvedSessionId)\n    if (session) session.lastSeen = Date.now()\n  }\n\n  /**\n   * Look up an existing session's transport without creating a new session.\n   * Used for DELETE requests where creating a session just to delete it\n   * would waste a session slot.\n   */\n  function getExistingTransport(\n    sessionId: string | undefined\n  ): MCPTransport | { error: 'not_found' | 'closed' } {\n    if (closed) return { error: 'closed' }\n    if (!sessionId) return { error: 'not_found' }\n    const existing = sessions.get(sessionId)\n    if (existing) return existing.transport\n    return { error: 'not_found' }\n  }\n\n  function deleteSession(sessionId: string | undefined) {\n    if (closed) return\n    if (!sessionId) return\n    const session = sessions.get(sessionId)\n    if (!session) return\n    sessions.delete(sessionId)\n    void scheduleClose(session, sessionId)\n  }\n\n  async function clear() {\n    closed = true\n    // Collect sessions before awaiting in-flight creations so new sessions\n    // can't be added during shutdown.\n    const all = [...sessions]\n    sessions.clear()\n    // Wait for in-flight session creations to finish (they will check\n    // `closed` and clean up without storing the session).\n    const inFlight = [...creating.values()]\n    await Promise.allSettled(inFlight)\n    creating.clear()\n    // Await both previously-scheduled closes (from cleanupExpired/deleteSession)\n    // and closes for sessions still alive at clear() time.\n    await Promise.allSettled([\n      ...closing,\n      ...all.map(([id, session]) => scheduleClose(session, id))\n    ])\n  }\n\n  return { clear, deleteSession, getExistingTransport, notifyToolsChanged, resolveTransport, touch }\n}\n","import { Buffer } from 'node:buffer'\n\nexport type MCPContent =\n  | { type: 'text'; text: string }\n  | { type: 'image'; data: string; mimeType: string }\n\nexport type MCPResult = { content: MCPContent[]; isError?: boolean }\n\nexport const MAX_RESULT_BYTES = 900_000\n\nexport function resultTooLargeMessage(kind: string, bytes: number, hint: string): string {\n  return `${kind} is too large (${Math.round(bytes / 1024)}KB, limit ${Math.round(\n    MAX_RESULT_BYTES / 1024\n  )}KB). ${hint}`\n}\n\nexport function ok(data: unknown, toolName?: string): MCPResult {\n  const text = JSON.stringify(data, null, 2)\n  const bytes = Buffer.byteLength(text, 'utf8')\n  if (bytes > MAX_RESULT_BYTES) {\n    return fail(\n      new Error(\n        resultTooLargeMessage(\n          toolName ? `Result from \"${toolName}\"` : 'Result',\n          bytes,\n          'Narrow the request with depth/root_id/node_types, get_node, or find_nodes.'\n        )\n      )\n    )\n  }\n  return { content: [{ type: 'text', text }] }\n}\n\nexport function fail(e: unknown): MCPResult {\n  const msg = e instanceof Error ? e.message : String(e)\n  return { content: [{ type: 'text', text: JSON.stringify({ error: msg }) }], isError: true }\n}\n","import { lstat, mkdir, readlink, realpath, writeFile } from 'node:fs/promises'\nimport { dirname, basename, isAbsolute, join, parse, resolve, sep as osSep } from 'node:path'\n\nimport { compact } from 'es-toolkit/array'\nimport { toUint8Array } from 'js-base64'\n\nimport { ok } from '#mcp/result'\nimport type { MCPResult } from '#mcp/result'\n\n/** Returns true for filesystem errors that indicate a missing or unresolvable path (ENOENT, ENOTDIR, ELOOP). */\nfunction isMissingPathError(e: unknown): boolean {\n  const code =\n    typeof e === 'object' && e !== null && 'code' in e ? (e as { code?: unknown }).code : undefined\n  return code === 'ENOENT' || code === 'ENOTDIR' || code === 'ELOOP'\n}\n\n/**\n * Resolve a file path and verify it stays within the allowed root directory.\n * Uses fs.realpath to resolve symlinks for the security check, preventing\n * traversal attacks where a symlink inside root points outside root.\n * Returns the resolved (normalized) path for display and file operations.\n */\n\n/** Walk up from `p` until we find a path that realpath() can resolve. */\nasync function resolveRealAncestor(\n  p: string\n): Promise<{ realAncestor: string; remainder: string }> {\n  let current = resolve(p)\n  let remainder = ''\n  let iterations = 0\n  do {\n    try {\n      const real = await realpath(current)\n      return { realAncestor: real, remainder }\n    } catch (e) {\n      if (!isMissingPathError(e)) throw e\n      const parent = dirname(current)\n      if (parent === current) return { realAncestor: current, remainder }\n      remainder = osSep + basename(current) + remainder\n      current = parent\n    }\n    iterations++\n  } while (iterations < 64) // depth limit to prevent infinite loops\n  // Fail closed: if we exceeded the depth limit, the path may contain\n  // unresolved symlink components (e.g., circular symlinks). Returning a\n  // partially-resolved path would allow it to pass containment checks\n  // even though the fully-resolved path could be outside root.\n  throw new Error(`Path resolution depth limit exceeded (possible circular symlinks): ${p}`)\n}\n\n/**\n * Walks the non-existent path segments (remainder) from the real ancestor and\n * rejects any that are symlinks. A symlink in a non-existent path segment\n * could point outside the allowed root — when the file is eventually written,\n * the OS follows the symlink chain and the write lands outside root.\n */\nasync function assertNoSymlinksInRemainder(realAncestor: string, remainder: string): Promise<void> {\n  if (!remainder) return\n  const segments = compact(remainder.split(osSep))\n  let current = realAncestor\n  for (const seg of segments) {\n    current = join(current, seg)\n    try {\n      const stat = await lstat(current)\n      if (stat.isSymbolicLink()) {\n        throw new Error(`Path is outside the allowed root: symlink at ${current}`)\n      }\n    } catch (e) {\n      if (e instanceof Error && e.message.includes('outside the allowed root')) throw e\n      if (!isMissingPathError(e)) throw e\n      // lstat failed — the component doesn't exist at all, which is expected\n      // since we're creating a new file. No symlink to worry about.\n    }\n  }\n}\n\n/** Throw if `rootPath` is trivially broad (e.g. \"/\" or \"C:\\\"). */\nfunction assertNarrowRoot(rootPath: string, original: string): void {\n  const normalized = resolve(rootPath)\n  const parsedRoot = parse(normalized).root\n  if (normalized === '/' || normalized === osSep || normalized === parsedRoot) {\n    throw new Error(\n      `Root path is too broad: \"${original}\" (resolved to \"${normalized}\"). ` +\n        'Specify a narrower OPENPENCIL_MCP_ROOT directory.'\n    )\n  }\n}\n\n/** Resolve `p` to its realpath-validated canonical form, handling non-existent paths. */\nasync function resolveRealPath(p: string): Promise<string> {\n  try {\n    return await realpath(p)\n  } catch (e) {\n    if (!isMissingPathError(e)) throw e\n    const parentDir = dirname(p)\n    const baseName = basename(p)\n    try {\n      const realParent = await realpath(parentDir)\n      return join(realParent, baseName)\n    } catch (e) {\n      if (!isMissingPathError(e)) throw e\n      const { realAncestor, remainder } = await resolveRealAncestor(parentDir)\n      await assertNoSymlinksInRemainder(realAncestor, remainder)\n      return join(realAncestor, remainder.slice(osSep.length), baseName)\n    }\n  }\n}\n\nexport interface SafePathResult {\n  /** The user-provided normalized path (for display/error messages). */\n  resolved: string\n  /** The canonical realpath-validated path (for filesystem operations). */\n  realPath: string\n}\n\nconst MAX_SYMLINK_DEPTH = 16\n\n/**\n * Resolve a dangling symlink's target. When realpath fails on a symlink,\n * read the link target and recursively validate it is inside root.\n * Returns the canonical realPath of the target, or undefined if `resolved`\n * is not a symlink.\n */\nasync function resolveDanglingSymlink(\n  resolved: string,\n  root: string,\n  realRoot: string,\n  symlinkDepth: number\n): Promise<string | undefined> {\n  try {\n    const stat = await lstat(resolved)\n    if (!stat.isSymbolicLink()) return undefined\n    // Dangling symlink — realpath already failed, which means the\n    // target doesn't exist. Read the symlink target and validate it\n    // is inside root. Absolute-path targets pointing outside root are\n    // rejected. Relative targets are resolved relative to the symlink\n    // directory and checked against root.\n    const linkTarget = await readlink(resolved)\n    const linkDir = dirname(resolved)\n    const resolvedTarget = isAbsolute(linkTarget) ? linkTarget : resolve(linkDir, linkTarget)\n    // Recursively validate the target (handles nested symlinks).\n    // Pass the already-canonical realRoot so the recursive call skips\n    // redundant assertNarrowRoot + realpath(root) work.\n    const targetResult = await resolveSafePathInternal(\n      resolvedTarget,\n      root,\n      symlinkDepth + 1,\n      realRoot\n    )\n    return targetResult.realPath\n  } catch (e) {\n    if (e instanceof Error) {\n      // Re-throw security, root-scope, and depth-limit errors — these must\n      // not be swallowed. They indicate genuine validation failures, not\n      // the expected \"path doesn't exist\" case.\n      if (\n        e.message.includes('outside the allowed root') ||\n        e.message.includes('Root path is too broad') ||\n        e.message.includes('depth limit exceeded')\n      )\n        throw e\n    }\n    if (!isMissingPathError(e)) throw e\n    // lstat failed (file doesn't exist at all) — not a symlink\n    return undefined\n  }\n}\n\nasync function resolveSafePathInternal(\n  filePath: string,\n  root: string,\n  symlinkDepth: number,\n  realRoot?: string\n): Promise<SafePathResult> {\n  if (symlinkDepth >= MAX_SYMLINK_DEPTH) {\n    throw new Error(\n      `Symlink resolution depth limit exceeded (possible circular symlinks): ${filePath}`\n    )\n  }\n\n  const normalizedRoot = resolve(root)\n  const resolved = isAbsolute(filePath) ? resolve(filePath) : resolve(normalizedRoot, filePath)\n\n  if (!realRoot) {\n    assertNarrowRoot(root, root)\n\n    try {\n      realRoot = await realpath(root)\n    } catch (e) {\n      if (!isMissingPathError(e)) throw e\n      const { realAncestor, remainder } = await resolveRealAncestor(root)\n      realRoot = remainder ? join(realAncestor, remainder.slice(osSep.length)) : realAncestor\n    }\n\n    assertNarrowRoot(realRoot, root)\n  }\n\n  const realSep = realRoot.endsWith('/') || realRoot.endsWith('\\\\') ? '' : osSep\n\n  let realPath: string\n  try {\n    realPath = await realpath(resolved)\n  } catch (e) {\n    if (!isMissingPathError(e)) throw e\n    const symlinkRealPath = await resolveDanglingSymlink(resolved, root, realRoot, symlinkDepth)\n    realPath = symlinkRealPath ?? (await resolveRealPath(resolved))\n  }\n\n  if (!realPath.startsWith(realRoot + realSep) && realPath !== realRoot) {\n    throw new Error(`Path is outside the allowed root: ${root}`)\n  }\n  return { resolved, realPath }\n}\n\nexport async function resolveSafePath(filePath: string, root: string): Promise<SafePathResult> {\n  return resolveSafePathInternal(filePath, root, 0)\n}\n\nexport async function writeToolOutput(\n  toolName: string,\n  result: Record<string, unknown>,\n  filePath: string,\n  root: string\n): Promise<MCPResult | null> {\n  const { resolved, realPath } = await resolveSafePath(filePath, root)\n  // Use the canonical realPath for filesystem operations to prevent TOCTOU:\n  // an attacker could swap a directory component with a symlink between\n  // resolveSafePath's validation and the writeFile call. Writing to the\n  // realpath-validated path ensures the write lands inside root.\n  const parentDir = dirname(realPath)\n  await mkdir(parentDir, { recursive: true })\n  // TOCTOU mitigation: re-verify the parent directory still resolves inside\n  // the root after mkdir. An attacker who replaces an ancestor directory\n  // with a symlink between resolveSafePath and the write would be detected\n  // here — realpath follows the ancestor symlink and resolves outside root.\n  await resolveSafePath(parentDir, root)\n  if (toolName === 'export_svg' && typeof result.svg === 'string') {\n    await writeFile(realPath, result.svg, 'utf8')\n    await resolveSafePath(realPath, root)\n    return ok({ written: resolved, byteLength: Buffer.byteLength(result.svg, 'utf8') })\n  }\n  if (toolName === 'export_image' && typeof result.base64 === 'string') {\n    const bytes = toUint8Array(result.base64)\n    await writeFile(realPath, bytes)\n    await resolveSafePath(realPath, root)\n    return ok({ written: resolved, byteLength: bytes.length })\n  }\n  if (toolName === 'get_jsx' && typeof result.jsx === 'string') {\n    await writeFile(realPath, result.jsx, 'utf8')\n    await resolveSafePath(realPath, root)\n    return ok({ written: resolved, byteLength: Buffer.byteLength(result.jsx, 'utf8') })\n  }\n  return null\n}\n","import { Buffer } from 'node:buffer'\nimport { resolve } from 'node:path'\n\nimport type { McpServer, ToolCallback, ToolAnnotations } from '@modelcontextprotocol/server'\n// eslint-disable-next-line open-pencil/no-mixed-case-acronym-identifiers -- Upstream export spelling.\nimport { toStandardJsonSchema as toStandardJSONSchema } from '@valibot/to-json-schema'\nimport * as v from 'valibot'\n\nimport { CODEGEN_PROMPT } from '@open-pencil/core/tools'\n\nimport type { RPCJSONObject } from '#mcp/json'\nimport { MAX_RESULT_BYTES, fail, ok, resultTooLargeMessage } from '#mcp/result'\nimport { createToolDescriptors, getMCPToolDefinitions } from '#mcp/tool/manifest'\nimport type { ToolDescriptor, ToolEffect, ToolPolicy } from '#mcp/tool/metadata'\nimport { resolveSafePath, writeToolOutput } from '#mcp/tool/output'\nimport { isToolEnabled } from '#mcp/tool/policy'\nimport { SELECTION_SCOPE_FILE_OUTPUT_ERROR } from '#mcp/tool/scope'\n\nexport type RPCSender = (body: Record<string, unknown>) => Promise<unknown>\n\nconst automationTargetSchema = {\n  document_id: v.optional(\n    v.pipe(v.string(), v.description('Optional OpenPencil document/tab ID to target'))\n  ),\n  page_id: v.optional(\n    v.pipe(v.string(), v.description('Optional page ID to target within the document'))\n  )\n}\n\nfunction splitAutomationTarget(args: Record<string, unknown>): {\n  target: { document_id?: string; page_id?: string }\n  args: Record<string, unknown>\n} {\n  const { document_id, page_id, ...rest } = args\n  const target: { document_id?: string; page_id?: string } = {}\n  if (typeof document_id === 'string') target.document_id = document_id\n  if (typeof page_id === 'string') target.page_id = page_id\n  return { target, args: rest }\n}\n\ntype RPCResponse = { ok?: boolean; result?: unknown; target?: unknown; error?: string }\n\nasync function sendCommand(\n  sendRPC: RPCSender,\n  command: string,\n  args: Record<string, unknown>\n): Promise<RPCResponse> {\n  const res = (await sendRPC({ command, args })) as RPCResponse\n  if (res.ok === false) throw new Error(res.error)\n  return res\n}\n\nfunction withTarget<T extends object>(body: T, res: RPCResponse): T & { target?: unknown } {\n  return res.target ? { ...body, target: res.target } : body\n}\n\n/** Who sends a session's tool calls: any MCP client, or an ACP or Pi harness chat in the app. */\nexport const MCP_AGENT_KINDS = ['mcp', 'acp', 'harness'] as const\nexport type MCPAgentKind = (typeof MCP_AGENT_KINDS)[number]\n\n/** The session tool calls come from, so the app can show the client as an agent at work. */\nexport interface MCPAgentSession {\n  id: string\n  kind: MCPAgentKind\n}\n\nexport interface RegisterToolsOptions {\n  policy: ToolPolicy\n  mcpRoot?: string | null\n  sendRPC: RPCSender\n  agentSession?: MCPAgentSession\n}\n\nfunction toolAnnotations(effect: ToolEffect): ToolAnnotations {\n  return {\n    readOnlyHint: effect === 'read',\n    destructiveHint: effect === 'write'\n  }\n}\n\nfunction descriptorByName(descriptors: readonly ToolDescriptor[]): Map<string, ToolDescriptor> {\n  return new Map(descriptors.map((descriptor) => [descriptor.name, descriptor]))\n}\n\nexport function registerTools(mcpServer: McpServer, options: RegisterToolsOptions): void {\n  const { policy, sendRPC, agentSession } = options\n  // Sent with each tool call: the session, and the client's name once it has introduced itself.\n  const agent = () =>\n    agentSession\n      ? {\n          session: agentSession.id,\n          kind: agentSession.kind,\n          client: mcpServer.server.getClientVersion()?.name\n        }\n      : undefined\n  const resolvedRoot = options.mcpRoot ? resolve(options.mcpRoot) : null\n  const descriptors = descriptorByName(createToolDescriptors(resolvedRoot !== null))\n  const register = <InputArgs extends v.GenericSchema>(\n    name: string,\n    toolOptions: { description?: string; inputSchema: InputArgs },\n    handler: ToolCallback<ReturnType<typeof toStandardJSONSchema<InputArgs>>>\n  ) => {\n    const descriptor = descriptors.get(name)\n    if (!descriptor) throw new Error(`Missing MCP tool descriptor for \"${name}\"`)\n    if (!isToolEnabled(descriptor, policy)) return\n    mcpServer.registerTool(\n      name,\n      {\n        description: toolOptions.description ?? descriptor.description,\n        inputSchema: toStandardJSONSchema(toolOptions.inputSchema),\n        annotations: toolAnnotations(descriptor.effect),\n        _meta: { 'openpencil/capabilities': descriptor.capabilities }\n      },\n      handler\n    )\n  }\n\n  for (const def of getMCPToolDefinitions()) {\n    register(\n      def.name,\n      {\n        description: def.description,\n        inputSchema: v.strictObject({ ...def.input.entries, ...automationTargetSchema })\n      },\n      async (args: Record<string, unknown>) => {\n        try {\n          const { target, args: toolArgs } = splitAutomationTarget(args)\n          if (policy.scope === 'selection' && toolArgs.path !== undefined) {\n            return fail(new Error(SELECTION_SCOPE_FILE_OUTPUT_ERROR))\n          }\n          const result = await sendRPC({\n            command: 'tool',\n            args: {\n              ...target,\n              name: def.name,\n              args: toolArgs,\n              agent: agent(),\n              // A client limited to the selection says so, whatever the server's own scope.\n              ...(policy.scope === 'selection' ? { scope: policy.scope } : {})\n            }\n          })\n          const res = result as { ok?: boolean; result?: unknown; error?: string }\n          if (res.ok === false) return fail(new Error(res.error))\n          const r = res.result as RPCJSONObject | undefined\n          const filePath = typeof toolArgs.path === 'string' ? toolArgs.path : null\n          if (r && filePath && resolvedRoot) {\n            const written = await writeToolOutput(def.name, r, filePath, resolvedRoot)\n            if (written) return written\n          }\n          if (r && 'base64' in r && 'mimeType' in r) {\n            const base64 = String(r.base64)\n            const bytes = Buffer.byteLength(base64, 'utf8')\n            if (bytes > MAX_RESULT_BYTES) {\n              return fail(\n                new Error(\n                  resultTooLargeMessage(\n                    `Image from \"${def.name}\"`,\n                    bytes,\n                    'Export a smaller region or lower the scale/resolution.'\n                  )\n                )\n              )\n            }\n            return {\n              content: [\n                {\n                  type: 'image' as const,\n                  data: base64,\n                  mimeType: r.mimeType as string\n                }\n              ]\n            }\n          }\n          return ok(r, def.name)\n        } catch (e) {\n          return fail(e)\n        }\n      }\n    )\n  }\n\n  register(\n    'list_documents',\n    {\n      description:\n        'List open OpenPencil documents/tabs with their IDs, file paths, current pages, and pages.',\n      inputSchema: v.strictObject({})\n    },\n    async () => {\n      try {\n        const result = await sendRPC({ command: 'list_documents', args: {} })\n        const res = result as { ok?: boolean; result?: unknown; error?: string }\n        if (res.ok === false) return fail(new Error(res.error))\n        return ok(res.result ?? {})\n      } catch (e) {\n        return fail(e)\n      }\n    }\n  )\n\n  register(\n    'save_file',\n    {\n      description: resolvedRoot\n        ? 'Save the current document to disk. If path is provided, it must be inside the configured MCP root.'\n        : 'Save the current document to disk. Uses the existing file path if available, otherwise prompts for a location.',\n      inputSchema: resolvedRoot\n        ? v.strictObject({\n            path: v.optional(\n              v.pipe(\n                v.string(),\n                v.minLength(1),\n                v.description('Path for the .fig file, absolute or relative to the MCP root')\n              )\n            ),\n            ...automationTargetSchema\n          })\n        : v.strictObject({ ...automationTargetSchema })\n    },\n    async (args: { path?: string; document_id?: string; page_id?: string }) => {\n      try {\n        const safePath =\n          args.path !== undefined && resolvedRoot\n            ? await resolveSafePath(args.path, resolvedRoot)\n            : undefined\n        const { target } = splitAutomationTarget(args)\n        const result = await sendRPC({\n          command: 'save_file',\n          args: { ...target, path: safePath?.realPath }\n        })\n        const res = result as { ok?: boolean; result?: unknown; target?: unknown; error?: string }\n        if (res.ok === false) return fail(new Error(res.error))\n        const response: { saved: true; path?: string; target?: unknown } = { saved: true }\n        if (safePath) response.path = safePath.resolved\n        if (res.target) response.target = res.target\n        return ok(response)\n      } catch (e) {\n        return fail(e)\n      }\n    }\n  )\n\n  if (resolvedRoot) {\n    register(\n      'open_file',\n      {\n        description: 'Open a .fig or .pen file from inside the configured MCP root.',\n        inputSchema: v.strictObject({\n          path: v.pipe(\n            v.string(),\n            v.minLength(1),\n            v.description('Path to the design file, absolute or relative to the MCP root')\n          ),\n          ...automationTargetSchema\n        })\n      },\n      async (args: { path: string; document_id?: string; page_id?: string }) => {\n        try {\n          const safe = await resolveSafePath(args.path, resolvedRoot)\n          const { target } = splitAutomationTarget(args)\n          const result = await sendRPC({\n            command: 'open_file',\n            args: { ...target, path: safe.realPath }\n          })\n          const res = result as { ok?: boolean; result?: unknown; target?: unknown; error?: string }\n          if (res.ok === false) return fail(new Error(res.error))\n          const response: { opened: true; target?: unknown } = { opened: true }\n          if (res.target) response.target = res.target\n          return ok(response)\n        } catch (e) {\n          return fail(e)\n        }\n      }\n    )\n\n    register(\n      'new_document',\n      {\n        description:\n          'Create a new empty document with an optional save path inside the configured MCP root.',\n        inputSchema: v.strictObject({\n          path: v.optional(\n            v.pipe(\n              v.string(),\n              v.minLength(1),\n              v.description('Path for the new file, absolute or relative to the MCP root')\n            )\n          ),\n          ...automationTargetSchema\n        })\n      },\n      async (args: { path?: string; document_id?: string; page_id?: string }) => {\n        try {\n          const safePath =\n            args.path !== undefined ? await resolveSafePath(args.path, resolvedRoot) : undefined\n          const { target } = splitAutomationTarget(args)\n          const result = await sendRPC({\n            command: 'new_document',\n            args: { ...target, path: safePath?.realPath }\n          })\n          const res = result as { ok?: boolean; result?: unknown; target?: unknown; error?: string }\n          if (res.ok === false) return fail(new Error(res.error))\n          const response: { created: true; target?: unknown } = { created: true }\n          if (res.target) response.target = res.target\n          return ok(response)\n        } catch (e) {\n          return fail(e)\n        }\n      }\n    )\n  }\n\n  const closeEntries = {\n    unsaved: v.optional(\n      v.pipe(\n        v.picklist(['error', 'save', 'discard']),\n        v.description(\n          'What to do with unsaved changes: \"error\" (default) fails, \"save\" saves first, \"discard\" drops them'\n        )\n      )\n    ),\n    ...automationTargetSchema\n  }\n\n  register(\n    'close_file',\n    {\n      inputSchema: resolvedRoot\n        ? v.strictObject({\n            ...closeEntries,\n            path: v.optional(\n              v.pipe(\n                v.string(),\n                v.minLength(1),\n                v.description(\n                  'With unsaved \"save\": .fig path for a document never saved, inside the MCP root'\n                )\n              )\n            )\n          })\n        : v.strictObject(closeEntries)\n    },\n    async (args: {\n      unsaved?: 'error' | 'save' | 'discard'\n      path?: string\n      document_id?: string\n      page_id?: string\n    }) => {\n      try {\n        const safePath =\n          args.path !== undefined && resolvedRoot\n            ? await resolveSafePath(args.path, resolvedRoot)\n            : undefined\n        const { target } = splitAutomationTarget(args)\n        const rpcArgs: Record<string, unknown> = { ...target }\n        if (args.unsaved) rpcArgs.unsaved = args.unsaved\n        if (safePath) rpcArgs.path = safePath.realPath\n        const res = await sendCommand(sendRPC, 'close_file', rpcArgs)\n        const closed = (res.result as { closed?: boolean } | undefined)?.closed === true\n        return ok(withTarget({ closed }, res))\n      } catch (e) {\n        return fail(e)\n      }\n    }\n  )\n\n  register(\n    'activate_document',\n    {\n      inputSchema: v.strictObject({\n        document_id: v.pipe(v.string(), v.description('Document/tab ID from list_documents')),\n        page_id: automationTargetSchema.page_id\n      })\n    },\n    async (args: { document_id: string; page_id?: string }) => {\n      try {\n        const { target } = splitAutomationTarget(args)\n        const res = await sendCommand(sendRPC, 'activate_document', target)\n        return ok(withTarget({ activated: true }, res))\n      } catch (e) {\n        return fail(e)\n      }\n    }\n  )\n\n  for (const command of ['undo', 'redo'] as const) {\n    register(\n      command,\n      { inputSchema: v.strictObject({ ...automationTargetSchema }) },\n      async (args) => {\n        try {\n          const { target } = splitAutomationTarget(args)\n          const res = await sendCommand(sendRPC, command, target)\n          return ok(withTarget({ ...(res.result as RPCJSONObject | undefined) }, res))\n        } catch (e) {\n          return fail(e)\n        }\n      }\n    )\n  }\n\n  register('get_settings', { inputSchema: v.strictObject({}) }, async () => {\n    try {\n      const res = await sendCommand(sendRPC, 'get_settings', {})\n      return ok(res.result ?? {})\n    } catch (e) {\n      return fail(e)\n    }\n  })\n\n  register(\n    'update_settings',\n    {\n      inputSchema: v.strictObject({\n        settings: v.pipe(\n          v.record(v.string(), v.unknown()),\n          v.description(\n            'Partial settings, e.g. {\"appearance\":{\"theme\":\"light\"},\"editing\":{\"snapping\":{\"pixelGrid\":false}}}. Unknown keys and invalid values are rejected.'\n          )\n        )\n      })\n    },\n    async (args: { settings: Record<string, unknown> }) => {\n      try {\n        // Echo only the applied patch: with get_settings disabled, writing must not read.\n        await sendCommand(sendRPC, 'update_settings', { settings: args.settings })\n        return ok({ updated: args.settings })\n      } catch (e) {\n        return fail(e)\n      }\n    }\n  )\n\n  register(\n    'get_codegen_prompt',\n    {\n      description:\n        'Get design-to-code generation guidelines. Call before generating frontend code.',\n      inputSchema: v.strictObject({})\n    },\n    async () => ok({ prompt: CODEGEN_PROMPT })\n  )\n}\n","","import { access, chmod, constants, mkdir, readFile, unlink } from 'node:fs/promises'\nimport { createServer } from 'node:http'\nimport type { Server as HttpServer } from 'node:http'\nimport { connect, type Socket } from 'node:net'\nimport { dirname } from 'node:path'\n\nimport { getRequestListener } from '@hono/node-server'\nimport type { Hono } from 'hono'\nimport type { WebSocketServer } from 'ws'\n\nimport type { MCPToolScope } from '#mcp/tool/scope'\nimport {\n  parseDiscoveryInfo,\n  removeDiscoveryFile,\n  removeStaleSocket,\n  writeDiscoveryFile\n} from '#mcp/transport/discovery'\nimport {\n  getDiscoveryPath,\n  getSocketDir,\n  getSocketPath,\n  platformHasUnixSockets\n} from '#mcp/transport/paths'\n\nconst trackedConnections = Symbol('open-pencil-mcp-connections')\n\ntype TrackedHttpServer = HttpServer & {\n  [trackedConnections]?: Set<Socket>\n}\n\n/** Create an HTTP server bound to the Hono app. Each server can listen on one address. */\nexport function createAppServer(app: Hono): HttpServer {\n  const listener = getRequestListener(app.fetch)\n  const server: TrackedHttpServer = createServer((req, res) => {\n    void listener(req, res)\n  })\n  const connections = new Set<Socket>()\n  server[trackedConnections] = connections\n  server.on('connection', (socket) => {\n    connections.add(socket)\n    socket.once('close', () => connections.delete(socket))\n  })\n  return server\n}\n\n/** Wire the HTTP upgrade handler onto a server. Each server needs its own. */\nfunction wireUpgrade(server: HttpServer, wss: WebSocketServer) {\n  server.on('upgrade', (request, socket, head) => {\n    wss.handleUpgrade(request, socket, head, (ws) => {\n      wss.emit('connection', ws, request)\n    })\n  })\n}\n\n/** Start socket listener; returns the resolved socket path, or null if skipped. */\nexport async function startSocketListener(\n  app: Hono,\n  wss: WebSocketServer,\n  socketPathOverride: string | null\n): Promise<{ server: HttpServer; resolvedPath: string } | null> {\n  // Unix domain sockets are not available on Windows — always use TCP there,\n  // even when a socketPath override is provided.\n  if (!platformHasUnixSockets()) return null\n\n  const resolvedPath = socketPathOverride ?? (await getSocketPath())\n  if (socketPathOverride) {\n    // Create the parent directory for the caller-provided socket path.\n    // For newly-created parent directories, request mode 0o700 to restrict\n    // access to the same OS user; existing directories are left unchanged\n    // (mkdir with recursive:true does not chmod existing dirs). The socket\n    // file itself is later chmodded to 0o600, and the auth token provides\n    // the primary defense against unauthorized access.\n    await mkdir(dirname(resolvedPath), { recursive: true, mode: 0o700 })\n  } else {\n    // Ensure the default platform socket directory exists.\n    await getSocketDir()\n  }\n  await removeStaleSocket(resolvedPath)\n\n  // Bun on Linux silently replaces existing Unix socket files on listen().\n  // If removeStaleSocket left the file in place (live server detected),\n  // throw EADDRINUSE to match Node.js/macOS behavior.\n  const socketStillExists = await access(resolvedPath, constants.F_OK)\n    .then(() => true)\n    .catch(() => false)\n  if (socketStillExists) {\n    const err = new Error(\n      `listen EADDRINUSE: address already in use ${resolvedPath}`\n    ) as NodeJS.ErrnoException\n    err.code = 'EADDRINUSE'\n    throw err\n  }\n\n  const server = createAppServer(app)\n  wireUpgrade(server, wss)\n\n  const ss = server\n  ss.on('error', (err) => console.error('[MCP] Socket server error:', err))\n  await new Promise<void>((resolve, reject) => {\n    ss.on('error', reject)\n    ss.listen(resolvedPath, () => {\n      ss.off('error', reject)\n      resolve()\n    })\n  }).catch((err) => {\n    ss.removeAllListeners('error')\n    server.close(() => {\n      void 0\n    })\n    throw err\n  })\n\n  // NOTE: There is a brief TOCTOU window between listen() and chmod() below\n  // where the socket file has default permissions (subject to process umask).\n  // The auth token mitigates this — unauthenticated requests get 401 — but a\n  // same-user process could connect before chmod. This is accepted as a known\n  // limitation; calling process.umask(0o077) would close the window but has\n  // global side effects.\n\n  try {\n    await chmod(resolvedPath, 0o600)\n  } catch (e) {\n    // Fail closed: if we cannot restrict socket permissions, refuse to\n    // serve on this socket. A world-readable socket with auth disabled\n    // (OPENPENCIL_MCP_AUTH_TOKEN=\"\") is a security hole.\n    await closeServer(server).catch(() => undefined)\n    await cleanupSocket(resolvedPath).catch(() => undefined)\n    throw new Error(\n      `Socket chmod failed — refusing to serve with insecure permissions: ${e instanceof Error ? e.message : String(e)}`\n    )\n  }\n\n  return { server, resolvedPath }\n}\n\n/** Start TCP listener; returns the server and actual port, or null if skipped. */\nexport async function startTcpListener(\n  app: Hono,\n  wss: WebSocketServer,\n  httpPort: number\n): Promise<{ server: HttpServer; port: number } | null> {\n  const host = '127.0.0.1'\n  const server = createAppServer(app)\n  wireUpgrade(server, wss)\n\n  const ts = server\n  ts.on('error', (err) => console.error('[MCP] TCP server error:', err))\n  const actualPort = await new Promise<number>((resolve, reject) => {\n    ts.on('error', reject)\n    ts.listen(httpPort, host, () => {\n      ts.off('error', reject)\n      const addr = ts.address()\n      const port = typeof addr === 'object' && addr ? addr.port : httpPort\n      resolve(port)\n    })\n  }).catch((err) => {\n    ts.removeAllListeners('error')\n    server.close(() => {\n      void 0\n    })\n    throw err\n  })\n\n  return { server, port: actualPort }\n}\n\nexport async function writeDiscovery(\n  resolvedSocketPath: string | null,\n  actualHttpPort: number,\n  authToken: string | null,\n  version: string,\n  disabledTools: string[],\n  scope: MCPToolScope\n): Promise<string> {\n  const startedAt = new Date().toISOString()\n  await writeDiscoveryFile({\n    pid: process.pid,\n    socketPath: resolvedSocketPath,\n    httpPort: actualHttpPort,\n    authRequired: authToken !== null,\n    authToken,\n    version,\n    startedAt,\n    disabledTools,\n    scope\n  })\n  return startedAt\n}\n\nexport async function closeServer(srv: HttpServer | null): Promise<void> {\n  if (!srv) return\n  // The WebSocket grace period has already elapsed by the time listener\n  // teardown begins. Close HTTP connections and explicitly destroy upgraded\n  // sockets, which Node's closeAllConnections() intentionally does not cover.\n  srv.closeIdleConnections()\n  srv.closeAllConnections()\n  const connections = (srv as TrackedHttpServer)[trackedConnections]\n  for (const socket of connections ?? []) socket.destroy()\n\n  // Keep a final bound in case a platform-specific connection escapes the\n  // tracked set or arrives during shutdown.\n  const forceClose = setTimeout(() => {\n    for (const socket of connections ?? []) socket.destroy()\n    srv.closeAllConnections()\n    srv.close()\n  }, 5_000).unref()\n  try {\n    await new Promise<void>((resolve) => {\n      srv.close(() => resolve())\n    })\n  } finally {\n    clearTimeout(forceClose)\n  }\n}\n\nexport async function cleanupSocket(socketPath: string | null): Promise<void> {\n  if (!socketPath || !platformHasUnixSockets()) return\n  // Only remove the socket if no other server is listening on it.\n  // A replacement server that won the restart race may have already\n  // bound the same path — unlinking it would kill the replacement.\n  try {\n    const alive = await new Promise<boolean>((resolve) => {\n      let settled = false\n      const finish = (value: boolean) => {\n        if (settled) return\n        settled = true\n        client.destroy()\n        resolve(value)\n      }\n      const client = connect(socketPath)\n        .on('connect', () => finish(true))\n        .on('error', (err: NodeJS.ErrnoException) => {\n          // Only ECONNREFUSED and ENOENT mean \"nobody is listening\".\n          // All other errors (EACCES, ECONNRESET, etc.) are treated as\n          // \"alive\" to avoid unlinking a live replacement server's socket.\n          const isDead = err.code === 'ECONNREFUSED' || err.code === 'ENOENT'\n          finish(!isDead)\n        })\n      // Fail fast — we only need to know if something is listening.\n      // Treat timeout as inconclusive (alive) rather than dead: during\n      // system restarts or high load, a timeout doesn't reliably indicate\n      // nobody is listening. Being conservative prevents unlinking a live\n      // replacement server's socket.\n      client.setTimeout(500, () => finish(true))\n    })\n    if (alive) return // Another server owns this socket — leave it alone.\n  } catch {\n    // Connection test itself failed (unlikely) — fall through to unlink.\n    void 0\n  }\n  try {\n    await unlink(socketPath)\n  } catch (e) {\n    if (e instanceof Error && 'code' in e && (e as NodeJS.ErrnoException).code !== 'ENOENT') {\n      process.stderr.write(`  Socket: cleanup warning (${e.message})\\n`)\n    }\n  }\n}\n\nexport async function cleanupDiscovery(\n  ownAuthToken: string | null,\n  ownSocketPath: string | null,\n  ownHttpPort: number,\n  ownStartedAt: string\n): Promise<void> {\n  // Only remove the discovery file if it was written by this server instance.\n  // Without this guard, closing one of two concurrent servers can delete the\n  // other's discovery file, breaking auto-discovery for the surviving server.\n  // We verify all four identifying fields (authToken, socketPath, httpPort,\n  // startedAt) to handle the case where authToken is null (auth disabled) and\n  // two servers share the same null token. The startedAt timestamp uniquely\n  // identifies the instance since two servers starting at the exact same\n  // millisecond is extremely unlikely.\n  const discoveryPath = await getDiscoveryPath()\n  try {\n    const info = parseDiscoveryInfo(await readFile(discoveryPath, 'utf-8'))\n    if (!info) return\n    if (info.authToken !== ownAuthToken) return\n    if (info.socketPath !== ownSocketPath) return\n    if (info.httpPort !== ownHttpPort) return\n    if (info.startedAt !== ownStartedAt) return\n  } catch {\n    return\n  }\n  await removeDiscoveryFile().catch((e) => {\n    process.stderr.write(`  Discovery: cleanup warning (${e instanceof Error ? e.message : e})\\n`)\n  })\n}\n\nexport interface ListenerState {\n  socketResult: { server: HttpServer; resolvedPath: string } | null\n  tcpResult: { server: HttpServer; port: number } | null\n}\n\nexport async function teardownListeners(state: ListenerState): Promise<void> {\n  if (state.socketResult) {\n    await closeServer(state.socketResult.server)\n    await cleanupSocket(state.socketResult.resolvedPath)\n  }\n  if (state.tcpResult) {\n    await closeServer(state.tcpResult.server)\n  }\n}\n\n/**\n * Close the WebSocket server with a grace period. Prevents new connections,\n * then terminates any lingering clients after 2 seconds to ensure shutdown\n * completes promptly even with unresponsive clients.\n */\nexport async function closeWssGracefully(wss: WebSocketServer): Promise<void> {\n  await new Promise<void>((resolve) => {\n    let settled = false\n    const done = () => {\n      if (settled) return\n      settled = true\n      resolve()\n    }\n    // WebSocketServer.close() stops accepting connections but waits for\n    // existing clients; it does not initiate their closing handshakes.\n    // Ask clients to close first, then terminate only the stragglers.\n    for (const ws of wss.clients) {\n      try {\n        ws.close(1001, 'Server shutting down')\n      } catch (e) {\n        console.warn('[MCP] Failed to close WebSocket client:', e)\n      }\n    }\n    const graceTimer = setTimeout(() => {\n      // Snapshot clients before iterating — terminate() triggers handleClose\n      // which modifies wss.clients mid-iteration via clients.delete(ws).\n      const snapshot = [...wss.clients]\n      for (const ws of snapshot) {\n        try {\n          ws.terminate()\n        } catch (e) {\n          console.warn('[MCP] Failed to terminate WebSocket client:', e)\n        }\n      }\n      done()\n    }, 2_000).unref()\n    wss.close(() => {\n      clearTimeout(graceTimer)\n      done()\n    })\n  })\n}\n\nexport async function tryStartTcp(\n  app: Hono,\n  wss: WebSocketServer,\n  httpPort: number,\n  state: ListenerState\n): Promise<{ server: HttpServer; port: number } | null> {\n  try {\n    return await startTcpListener(app, wss, httpPort)\n  } catch (err) {\n    // If TCP listener fails after the socket listener succeeded, close the\n    // socket listener and clean up the socket file to avoid a leak.\n    await teardownListeners(state)\n    throw err\n  }\n}\n\nexport async function tryWriteDiscovery(\n  resolvedSocketPath: string | null,\n  actualHttpPort: number,\n  authToken: string | null,\n  version: string,\n  disabledTools: string[],\n  scope: MCPToolScope,\n  state: ListenerState\n): Promise<string> {\n  try {\n    return await writeDiscovery(\n      resolvedSocketPath,\n      actualHttpPort,\n      authToken,\n      version,\n      disabledTools,\n      scope\n    )\n  } catch (err) {\n    // If discovery file write fails after both listeners are up, tear down\n    // both listeners so we never advertise a running server that clients\n    // can't find via the discovery file.\n    await teardownListeners(state)\n    throw err\n  }\n}\n","import { randomBytes } from 'node:crypto'\nimport type { Server as HttpServer } from 'node:http'\n\nimport type { McpServer } from '@modelcontextprotocol/server'\nimport { Hono } from 'hono'\nimport { cors } from 'hono/cors'\nimport { resolveCommand } from 'package-manager-detector/commands'\nimport { detect, getUserAgent } from 'package-manager-detector/detect'\nimport { WebSocketServer, type WebSocket } from 'ws'\n\nimport { MCP_AGENT_HEADER } from '@open-pencil/core/constants'\n\nimport { bearerToken, isAuthorized, mcpRequestToken } from '#mcp/auth'\nimport { createBrowserRPCBridge } from '#mcp/browser-rpc'\nimport { MCP_CORS_HEADERS, MCP_CORS_METHODS, MCP_EXPOSED_HEADERS } from '#mcp/http-options'\nimport type { RPCJSONObject } from '#mcp/json'\nimport { createMCPSessionManager } from '#mcp/server/sessions'\nimport { createToolDescriptors } from '#mcp/tool/manifest'\nimport type { ToolDescriptor, ToolPolicy } from '#mcp/tool/metadata'\nimport { applyToolPolicy } from '#mcp/tool/policy'\nimport {\n  MCP_AGENT_KINDS,\n  registerTools,\n  type MCPAgentKind,\n  type MCPAgentSession\n} from '#mcp/tool/registration'\nimport { scopeRPC, type MCPToolScope } from '#mcp/tool/scope'\n\nimport packageJSON from '../package.json' with { type: 'json' }\nimport {\n  type ListenerState,\n  cleanupDiscovery,\n  closeWssGracefully,\n  createAppServer,\n  startSocketListener,\n  teardownListeners,\n  tryStartTcp,\n  tryWriteDiscovery\n} from './server/lifecycle'\n\nexport const MCP_VERSION: string = packageJSON.version\n\nconst HEARTBEAT_INTERVAL_MS = 5_000\n\nlet installCommandPromise: Promise<string> | null = null\n\nasync function resolveMCPInstallCommand(): Promise<string> {\n  const agent =\n    getUserAgent() ??\n    (\n      await detect({\n        strategies: ['install-metadata', 'lockfile', 'packageManager-field', 'devEngines-field']\n      })\n    )?.agent ??\n    'npm'\n  const resolved = resolveCommand(agent, 'global', [`@open-pencil/mcp@${MCP_VERSION}`])\n  if (!resolved) return `npm install -g @open-pencil/mcp@${MCP_VERSION}`\n  return [resolved.command, ...resolved.args].join(' ')\n}\n\nfunction mcpInstallCommand(): Promise<string> {\n  installCommandPromise ??= resolveMCPInstallCommand()\n  return installCommandPromise\n}\n\nexport { fail, ok, type MCPContent, type MCPResult } from '#mcp/result'\n\nexport {\n  MCP_AGENT_KINDS,\n  registerTools,\n  type MCPAgentKind,\n  type MCPAgentSession,\n  type RegisterToolsOptions,\n  type RPCSender\n} from '#mcp/tool/registration'\n\nfunction agentKindFromHeader(value: string | undefined): MCPAgentKind {\n  return MCP_AGENT_KINDS.find((kind) => kind === value) ?? 'mcp'\n}\n\nexport interface ServerOptions {\n  /** TCP port for the HTTP + WebSocket server. Ignored when `withTcp` is false. When set to 0 with `withTcp: true`, binds to an ephemeral port. Defaults to 7600. */\n  httpPort?: number\n  /** Path to the Unix domain socket. Auto-resolved if omitted. */\n  socketPath?: string | null\n  /** Whether to also listen on TCP (in addition to the socket). API default is `false`; the CLI passes `true` by default (derived from PORT, default 7600). */\n  withTcp?: boolean\n  enableEval?: boolean\n  /** Tool names omitted from every MCP session. */\n  disabledTools?: Iterable<string>\n  /** What clients can reach: the whole document (default) or only the user's selection. */\n  scope?: MCPToolScope\n  mcpRoot?: string | null\n  /** Auth token for /mcp and /rpc endpoints. Auto-generated (32-hex) when omitted. Pass null explicitly to disable auth. */\n  authToken?: string | null\n  corsOrigin?: string | readonly string[] | null\n  /**\n   * If set, the server starts a grace-period timer while no app is attached.\n   * The timer closes the server and removes its discovery file unless an app\n   * registers before it expires. A later app disconnect starts a new grace\n   * period, which lets app-spawned servers survive brief reloads while still\n   * cleaning up after renderer or process crashes. Undefined/0 disables the\n   * watchdog — the default, since a bare CLI invocation for manual testing\n   * should not self-terminate just because nobody connected yet. The desktop\n   * app opts in when it spawns the server.\n   */\n  appAttachTimeoutMs?: number\n  /**\n   * How long a tool call waits for the app to register before it fails with\n   * APP_NOT_CONNECTED. Defaults to 10 s so a stdio bridge started slightly\n   * before the desktop app still succeeds; tests shorten it.\n   */\n  appWaitTimeoutMs?: number\n}\n\nexport interface ServerHandle {\n  /** The Hono app (routes) */\n  app: Hono\n  /** The primary Node.js HTTP server (socket listener if present, otherwise TCP) */\n  server: HttpServer\n  /** Resolved socket path (null if not listening on socket) */\n  socketPath: string | null\n  /** TCP port the server is listening on (0 if TCP is disabled) */\n  httpPort: number\n  /** Shut down the server: close listeners, remove socket and discovery files */\n  close: () => Promise<void>\n}\n\n/** Set up Hono routes: /health, /rpc, /mcp */\nfunction createHonoApp(options: {\n  authToken: string | null\n  corsOrigin: string | readonly string[] | null\n  browserRPC: ReturnType<typeof createBrowserRPCBridge>\n  mcpSessions: ReturnType<typeof createMCPSessionManager>\n  sendToBrowser: (msg: RPCJSONObject) => Promise<unknown>\n  toolDescriptors: ToolDescriptor[]\n}): Hono {\n  const { authToken, corsOrigin, browserRPC, mcpSessions, sendToBrowser, toolDescriptors } = options\n\n  const app = new Hono()\n\n  if (corsOrigin) {\n    app.use(\n      '*',\n      cors({\n        origin: typeof corsOrigin === 'string' ? corsOrigin : [...corsOrigin],\n        allowMethods: MCP_CORS_METHODS,\n        allowHeaders: MCP_CORS_HEADERS,\n        exposeHeaders: MCP_EXPOSED_HEADERS\n      })\n    )\n  }\n\n  app.get('/health', async (c) => {\n    const provided = bearerToken(c.req.header('authorization'))\n    const canInspectConfiguration = authToken === null || isAuthorized(provided, authToken)\n    return c.json({\n      status: browserRPC.isConnected() ? 'ok' : 'no_app',\n      version: MCP_VERSION,\n      installCommand: await mcpInstallCommand(),\n      authRequired: authToken !== null,\n      ...(canInspectConfiguration ? { tools: toolDescriptors } : {})\n    })\n  })\n\n  app.use('/rpc', async (c, next) => {\n    // When authToken is null (operator explicitly disabled auth), skip token check —\n    // the Unix socket or localhost TCP already restricts access to local processes.\n    if (authToken !== null) {\n      const provided = bearerToken(c.req.header('authorization'))\n      if (!isAuthorized(provided, authToken)) {\n        return c.json({ error: 'Unauthorized' }, 401)\n      }\n    }\n    return next()\n  })\n\n  // Historical note: before the isConnected() guard was removed, a disconnected\n  // app returned 503 here. Now errors from sendToBrowser surface as 502. This\n  // is a semantic shift from 503 → 502; callers that distinguished 503 may\n  // need to handle 502 equivalently.\n  app.post('/rpc', async (c) => {\n    const body = await c.req.json().catch(() => null)\n    if (!body || typeof body !== 'object' || Array.isArray(body)) {\n      return c.json({ error: 'Invalid request body' }, 400)\n    }\n    try {\n      const result = await sendToBrowser(body as RPCJSONObject)\n      return c.json(result)\n    } catch (e) {\n      const msg = e instanceof Error ? e.message : String(e)\n      return c.json({ ok: false, error: msg }, 502)\n    }\n  })\n\n  app.all('/mcp', async (c) => {\n    if (authToken !== null) {\n      const token = mcpRequestToken(c.req.header('authorization'), c.req.header('x-mcp-token'))\n      if (!isAuthorized(token, authToken)) {\n        return c.json({ error: 'Unauthorized' }, 401)\n      }\n    }\n    const sessionId = c.req.header('mcp-session-id') ?? undefined\n    // Reject anonymous DELETE requests before they allocate a session.\n    // Without this guard, a DELETE without a session ID would call\n    // resolveTransport(undefined), which creates a fresh session just\n    // to no-op on deleteSession(undefined) — burning a session slot.\n    if (c.req.method === 'DELETE' && !sessionId) {\n      return c.json({ error: 'Missing MCP session id' }, 400)\n    }\n    // For DELETE, look up the existing session without creating a new one.\n    // resolveTransport() would allocate a fresh session for an unknown ID,\n    // only to immediately delete it — wasting a session slot.\n    if (c.req.method === 'DELETE' && sessionId) {\n      const existing = mcpSessions.getExistingTransport(sessionId)\n      if ('error' in existing) {\n        if (existing.error === 'closed') {\n          return c.json({ error: 'MCP server is shutting down' }, 503)\n        }\n        return c.json({ error: 'MCP session not found' }, 404)\n      }\n      try {\n        return await existing.handleRequest(c.req.raw)\n      } finally {\n        mcpSessions.deleteSession(sessionId)\n      }\n    }\n    if (sessionId) {\n      const existing = mcpSessions.getExistingTransport(sessionId)\n      if ('error' in existing) {\n        if (existing.error === 'closed') {\n          return c.json({ error: 'MCP server is shutting down' }, 503)\n        }\n        return c.json({ error: 'MCP session not found' }, 404)\n      }\n      mcpSessions.touch(sessionId, existing)\n      return existing.handleRequest(c.req.raw)\n    }\n    const transport = await mcpSessions.resolveTransport(\n      undefined,\n      agentKindFromHeader(c.req.header(MCP_AGENT_HEADER))\n    )\n    if ('error' in transport) {\n      if (transport.error === 'closed') {\n        return c.json({ error: 'MCP server is shutting down' }, 503)\n      }\n      return c.json(\n        { error: 'Too many active MCP sessions' },\n        { status: 503, headers: { 'Retry-After': '5' } }\n      )\n    }\n    mcpSessions.touch(undefined, transport)\n    return transport.handleRequest(c.req.raw)\n  })\n\n  return app\n}\n\n/** Set up shared WebSocket connection handling and heartbeat. Call once. */\nfunction wireConnectionHandling(\n  wss: WebSocketServer,\n  browserRPC: ReturnType<typeof createBrowserRPCBridge>\n) {\n  const alive = new WeakMap<WebSocket, boolean>()\n\n  wss.on('connection', (ws: WebSocket) => {\n    alive.set(ws, true)\n    browserRPC.handleConnection(ws)\n\n    ws.on('pong', () => alive.set(ws, true))\n    ws.on('message', (raw) => {\n      alive.set(ws, true)\n      const data = typeof raw === 'string' ? raw : Buffer.from(raw as Buffer).toString('utf-8')\n      browserRPC.handleMessage(data, ws)\n    })\n\n    ws.on('close', () => {\n      browserRPC.handleClose(ws)\n    })\n\n    ws.on('error', () => {\n      try {\n        ws.terminate()\n      } catch {\n        alive.delete(ws)\n      }\n    })\n  })\n\n  const heartbeat = setInterval(() => {\n    for (const ws of wss.clients) {\n      if (alive.get(ws) === false) {\n        try {\n          ws.terminate()\n        } catch {\n          continue\n        }\n        continue\n      }\n      alive.set(ws, false)\n      try {\n        ws.ping()\n      } catch {\n        continue\n      }\n    }\n  }, HEARTBEAT_INTERVAL_MS)\n  heartbeat.unref()\n  wss.on('close', () => clearInterval(heartbeat))\n}\n\nfunction buildServerContext(options: ServerOptions) {\n  const httpPort = options.httpPort ?? 7600\n  const toolPolicy: ToolPolicy = {\n    allowEval: options.enableEval ?? false,\n    disabledTools: [...new Set(options.disabledTools)],\n    scope: options.scope ?? 'document'\n  }\n  const mcpRoot = options.mcpRoot ?? null\n  // Auto-generated so all transports require auth by default. Override via OPENPENCIL_MCP_AUTH_TOKEN or authToken option.\n  // Pass authToken: null explicitly to disable auth entirely.\n  const authToken =\n    options.authToken === undefined ? randomBytes(16).toString('hex') : options.authToken\n  const corsOrigin = options.corsOrigin ?? null\n  const withTcp = options.withTcp ?? false\n\n  // Warn if auth is disabled while TCP is active — any local process can\n  // interact with the server without authentication. Socket-only transport\n  // (PORT=0) is safer because 0o600 permissions restrict access to the\n  // same OS user.\n  if (authToken === null && withTcp) {\n    process.stderr.write(\n      `WARNING: MCP server is running without authentication on TCP port ${httpPort}. ` +\n        'Any local process can interact with the server. ' +\n        'Set OPENPENCIL_MCP_AUTH_TOKEN to enable auth, or use PORT=0 for socket-only transport.\\n'\n    )\n  }\n\n  const mcpSessions = createMCPSessionManager({\n    serverVersion: MCP_VERSION,\n    registerTools: (mcpServer: McpServer, agentSession: MCPAgentSession) =>\n      registerTools(mcpServer, {\n        policy: toolPolicy,\n        mcpRoot,\n        sendRPC: sendToBrowser,\n        agentSession\n      }),\n    onSessionClosed: (sessionId) => {\n      // Best-effort: the app may be gone already, and then it shows no agents anyway.\n      sendToBrowser({ command: 'agent_session_closed', args: { session: sessionId } }).catch(\n        () => undefined\n      )\n    }\n  })\n  const browserRPC = createBrowserRPCBridge({\n    authToken,\n    onConnectionChange: mcpSessions.notifyToolsChanged,\n    appWaitTimeoutMs: options.appWaitTimeoutMs\n  })\n  // Every call into the app, from MCP sessions and /rpc alike, stays within the scope.\n  const sendToBrowser = scopeRPC(browserRPC.sendRPC, toolPolicy.scope)\n  const toolDescriptors = applyToolPolicy(createToolDescriptors(mcpRoot !== null), toolPolicy)\n\n  const app = createHonoApp({\n    authToken,\n    corsOrigin,\n    browserRPC,\n    mcpSessions,\n    sendToBrowser,\n    toolDescriptors\n  })\n  const wss = new WebSocketServer({ noServer: true })\n\n  return {\n    httpPort,\n    withTcp,\n    mcpSessions,\n    browserRPC,\n    sendToBrowser,\n    app,\n    wss,\n    authToken,\n    disabledTools: toolPolicy.disabledTools,\n    scope: toolPolicy.scope\n  }\n}\n\n/**\n * Unified runtime shutdown: closes browserRPC, clears MCP sessions,\n * terminates WebSocket clients, closes the WSS, and tears down HTTP\n * listeners. Used by both the startup catch block and ServerHandle.close()\n * to ensure no runtime resources (WebSocket, browserRPC, mcpSessions) are\n * left alive.\n */\nasync function shutdownRuntime(\n  browserRPC: ReturnType<typeof createBrowserRPCBridge>,\n  mcpSessions: ReturnType<typeof createMCPSessionManager>,\n  wss: WebSocketServer,\n  state: ListenerState\n): Promise<void> {\n  const errors: unknown[] = []\n  try {\n    browserRPC.close()\n  } catch (e) {\n    errors.push(e)\n  }\n  try {\n    await mcpSessions.clear()\n  } catch (e) {\n    errors.push(e)\n  }\n  try {\n    await closeWssGracefully(wss)\n  } catch (e) {\n    errors.push(e)\n  }\n  try {\n    await teardownListeners(state)\n  } catch (e) {\n    errors.push(e)\n  }\n  if (errors.length === 1) throw errors[0]\n  if (errors.length > 1) throw new AggregateError(errors, 'Multiple shutdown errors')\n}\n\nfunction buildHandle(\n  app: Hono,\n  wss: WebSocketServer,\n  browserRPC: ReturnType<typeof createBrowserRPCBridge>,\n  mcpSessions: ReturnType<typeof createMCPSessionManager>,\n  state: ListenerState,\n  resolvedSocketPath: string | null,\n  actualHttpPort: number,\n  authToken: string | null,\n  startedAt: string\n): ServerHandle {\n  // Promise-based lock ensures idempotency even under concurrent calls:\n  // the first call creates the teardown promise; subsequent calls return\n  // the same promise. JavaScript's event loop guarantees no preemption\n  // between the guard check and the assignment.\n  let closePromise: Promise<void> | null = null\n\n  async function close() {\n    if (closePromise) return closePromise\n    closePromise = (async () => {\n      const errors: unknown[] = []\n      try {\n        // Stop advertising this process before the slower WebSocket grace\n        // period. This also makes signal-driven shutdown resilient when a\n        // parent process exits before all clients finish closing.\n        await cleanupDiscovery(authToken, resolvedSocketPath, actualHttpPort, startedAt)\n      } catch (error) {\n        errors.push(error)\n      }\n      try {\n        await shutdownRuntime(browserRPC, mcpSessions, wss, state)\n      } catch (error) {\n        errors.push(error)\n      }\n      if (errors.length === 1) throw errors[0]\n      if (errors.length > 1) throw new AggregateError(errors, 'Multiple shutdown errors')\n    })()\n    return closePromise\n  }\n\n  const primary = state.socketResult?.server ?? state.tcpResult?.server ?? createAppServer(app)\n  return {\n    app,\n    server: primary,\n    socketPath: resolvedSocketPath,\n    httpPort: actualHttpPort,\n    close\n  }\n}\n\nexport async function startServer(options: ServerOptions = {}): Promise<ServerHandle> {\n  validateTimeoutOption('appAttachTimeoutMs', options.appAttachTimeoutMs)\n  validateTimeoutOption('appWaitTimeoutMs', options.appWaitTimeoutMs)\n  const ctx = buildServerContext(options)\n\n  // Wire shared connection handling BEFORE starting listeners so that\n  // any client connecting during startup is handled immediately.\n  wireConnectionHandling(ctx.wss, ctx.browserRPC)\n\n  const state: ListenerState = { socketResult: null, tcpResult: null }\n  let startedAt: string\n  try {\n    state.socketResult = await startSocketListener(ctx.app, ctx.wss, options.socketPath ?? null)\n    state.tcpResult = ctx.withTcp ? await tryStartTcp(ctx.app, ctx.wss, ctx.httpPort, state) : null\n    const resolvedSocketPath = state.socketResult?.resolvedPath ?? null\n    const actualHttpPort = state.tcpResult?.port ?? 0\n\n    if (!resolvedSocketPath && !actualHttpPort) {\n      throw new Error(\n        'MCP server has no active listeners (both socket and TCP are unavailable). ' +\n          'Ensure Unix domain sockets are supported on this platform or enable TCP with withTcp: true.'\n      )\n    }\n\n    startedAt = await tryWriteDiscovery(\n      resolvedSocketPath,\n      actualHttpPort,\n      ctx.authToken,\n      MCP_VERSION,\n      ctx.disabledTools,\n      ctx.scope,\n      state\n    )\n  } catch (err) {\n    // Tear down any listeners that started before the failure, then close\n    // all resources so nothing leaks when startServer rejects.\n    await shutdownRuntime(ctx.browserRPC, ctx.mcpSessions, ctx.wss, state).catch(() => undefined)\n    throw err\n  }\n\n  const resolvedSocketPath = state.socketResult?.resolvedPath ?? null\n  const actualHttpPort = state.tcpResult?.port ?? 0\n\n  const handle = buildHandle(\n    ctx.app,\n    ctx.wss,\n    ctx.browserRPC,\n    ctx.mcpSessions,\n    state,\n    resolvedSocketPath,\n    actualHttpPort,\n    ctx.authToken,\n    startedAt\n  )\n\n  armAppAttachWatchdog(options.appAttachTimeoutMs, ctx.browserRPC, handle)\n\n  return handle\n}\n\nconst MAX_TIMER_MS = 2_147_483_647\n\nfunction validateTimeoutOption(name: string, timeoutMs: number | undefined): void {\n  if (timeoutMs === undefined) return\n  if (!Number.isSafeInteger(timeoutMs)) {\n    throw new RangeError(`${name} must be a safe integer`)\n  }\n  if (timeoutMs < 0 || timeoutMs > MAX_TIMER_MS) {\n    throw new RangeError(`${name} must be in the range 0–${MAX_TIMER_MS}`)\n  }\n}\n\n/**\n * Closes an app-spawned server after it remains unattached for the configured\n * grace period. Registering an app cancels the pending shutdown; disconnecting\n * starts a fresh grace period so renderer reloads can reconnect without\n * leaving a permanently orphaned process behind.\n */\nfunction armAppAttachWatchdog(\n  timeoutMs: number | undefined,\n  browserRPC: ReturnType<typeof createBrowserRPCBridge>,\n  handle: ServerHandle\n): void {\n  if (timeoutMs === undefined || timeoutMs === 0) return\n\n  let timer: ReturnType<typeof setTimeout> | null = null\n  let closing = false\n  const clearTimer = () => {\n    if (!timer) return\n    clearTimeout(timer)\n    timer = null\n  }\n  const armTimer = () => {\n    clearTimer()\n    timer = setTimeout(() => {\n      timer = null\n      if (browserRPC.isConnected() || closing) return\n      closing = true\n      unsubscribe()\n      void handle.close().catch((e) => {\n        console.error('[MCP] Watchdog: failed to close orphaned (no_app) server:', e)\n      })\n    }, timeoutMs)\n    timer.unref()\n  }\n  const unsubscribe = browserRPC.subscribeConnectionChange((connected) => {\n    if (connected) clearTimer()\n    else armTimer()\n  })\n}\n"],"mappings":"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"}