@nimblebrain/synapse 0.17.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +20 -18
  2. package/dist/chunk-54W6IAHM.js +24 -0
  3. package/dist/chunk-54W6IAHM.js.map +1 -0
  4. package/dist/{chunk-UPDBDXQB.js → chunk-DRLVEHCD.js} +6 -13
  5. package/dist/chunk-DRLVEHCD.js.map +1 -0
  6. package/dist/chunk-NP7ROSH2.cjs +33 -0
  7. package/dist/chunk-NP7ROSH2.cjs.map +1 -0
  8. package/dist/{chunk-SRQNQ26I.js → chunk-OYLZ5OCK.js} +4 -4
  9. package/dist/chunk-OYLZ5OCK.js.map +1 -0
  10. package/dist/chunk-P5DBJFSQ.js +8459 -0
  11. package/dist/chunk-P5DBJFSQ.js.map +1 -0
  12. package/dist/chunk-PZ5AY32C.js +9 -0
  13. package/dist/chunk-PZ5AY32C.js.map +1 -0
  14. package/dist/chunk-Q7SFCCGT.cjs +11 -0
  15. package/dist/chunk-Q7SFCCGT.cjs.map +1 -0
  16. package/dist/chunk-QXDCA7QW.cjs +8466 -0
  17. package/dist/chunk-QXDCA7QW.cjs.map +1 -0
  18. package/dist/{chunk-EHLQWCYV.cjs → chunk-RU64F5RS.cjs} +7 -7
  19. package/dist/chunk-RU64F5RS.cjs.map +1 -0
  20. package/dist/{chunk-TXJMGQYF.cjs → chunk-SZWHFTXV.cjs} +5 -13
  21. package/dist/chunk-SZWHFTXV.cjs.map +1 -0
  22. package/dist/codegen/cli.cjs +4 -4
  23. package/dist/codegen/cli.js +4 -4
  24. package/dist/codegen/index.cjs +1 -0
  25. package/dist/codegen/index.d.cts +1 -1
  26. package/dist/codegen/index.d.ts +1 -1
  27. package/dist/codegen/index.js +1 -0
  28. package/dist/connect.iife.global.js +66 -43
  29. package/dist/{detect-Bf8Q_0dK.d.ts → detect-DBwc8a21.d.ts} +2 -2
  30. package/dist/{detect-BHYg26_d.d.cts → detect-DZMJsyvZ.d.cts} +2 -2
  31. package/dist/host/index.cjs +15 -14
  32. package/dist/host/index.d.cts +3 -3
  33. package/dist/host/index.d.ts +3 -3
  34. package/dist/host/index.js +3 -2
  35. package/dist/index.cjs +42 -18
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +40 -13
  38. package/dist/index.d.ts +40 -13
  39. package/dist/index.js +35 -3
  40. package/dist/index.js.map +1 -1
  41. package/dist/react/index.cjs +12 -10
  42. package/dist/react/index.cjs.map +1 -1
  43. package/dist/react/index.d.cts +14 -5
  44. package/dist/react/index.d.ts +14 -5
  45. package/dist/react/index.js +7 -5
  46. package/dist/react/index.js.map +1 -1
  47. package/dist/schema-reader-GM4A4L44.js +4 -0
  48. package/dist/{schema-reader-776246MJ.js.map → schema-reader-GM4A4L44.js.map} +1 -1
  49. package/dist/{schema-reader-B6LTGBMJ.cjs → schema-reader-MWBRQ2TQ.cjs} +3 -2
  50. package/dist/{schema-reader-B6LTGBMJ.cjs.map → schema-reader-MWBRQ2TQ.cjs.map} +1 -1
  51. package/dist/{server-QIYIJHD5.cjs → server-LR6T6C44.cjs} +32 -20
  52. package/dist/server-LR6T6C44.cjs.map +1 -0
  53. package/dist/{server-5N76YCWC.js → server-UHPEE55X.js} +32 -21
  54. package/dist/server-UHPEE55X.js.map +1 -0
  55. package/dist/synapse-runtime.iife.global.js +66 -43
  56. package/dist/synapse-ui.iife.global.js +2 -2
  57. package/dist/{type-generator-UA3L4OIA.cjs → type-generator-YTPFWLVG.cjs} +3 -2
  58. package/dist/{type-generator-UA3L4OIA.cjs.map → type-generator-YTPFWLVG.cjs.map} +1 -1
  59. package/dist/type-generator-ZYSVFK3X.js +4 -0
  60. package/dist/{type-generator-MZ4X4DE5.js.map → type-generator-ZYSVFK3X.js.map} +1 -1
  61. package/dist/{types-uEO4VFJ2.d.ts → types-CG08aO82.d.cts} +14 -83
  62. package/dist/{types-uEO4VFJ2.d.cts → types-CG08aO82.d.ts} +14 -83
  63. package/dist/ui/base.cjs +1 -0
  64. package/dist/ui/base.cjs.map +1 -1
  65. package/dist/ui/base.js +1 -0
  66. package/dist/ui/base.js.map +1 -1
  67. package/dist/ui/index.cjs +314 -87
  68. package/dist/ui/index.cjs.map +1 -1
  69. package/dist/ui/index.d.cts +29 -2
  70. package/dist/ui/index.d.ts +29 -2
  71. package/dist/ui/index.js +315 -89
  72. package/dist/ui/index.js.map +1 -1
  73. package/dist/vite/index.cjs +74 -30
  74. package/dist/vite/index.cjs.map +1 -1
  75. package/dist/vite/index.d.cts +5 -1
  76. package/dist/vite/index.d.ts +5 -1
  77. package/dist/vite/index.js +74 -30
  78. package/dist/vite/index.js.map +1 -1
  79. package/dist/{writer-NDK4U6C5.cjs → writer-HDRCQSX2.cjs} +3 -3
  80. package/dist/{writer-NDK4U6C5.cjs.map → writer-HDRCQSX2.cjs.map} +1 -1
  81. package/dist/{writer-ZNBXYUYC.js → writer-QNDPMEYD.js} +3 -3
  82. package/dist/{writer-ZNBXYUYC.js.map → writer-QNDPMEYD.js.map} +1 -1
  83. package/package.json +8 -5
  84. package/dist/chunk-EHLQWCYV.cjs.map +0 -1
  85. package/dist/chunk-FDMRJZKT.cjs +0 -788
  86. package/dist/chunk-FDMRJZKT.cjs.map +0 -1
  87. package/dist/chunk-SRQNQ26I.js.map +0 -1
  88. package/dist/chunk-TXJMGQYF.cjs.map +0 -1
  89. package/dist/chunk-UPDBDXQB.js.map +0 -1
  90. package/dist/chunk-VYR6V47N.js +0 -781
  91. package/dist/chunk-VYR6V47N.js.map +0 -1
  92. package/dist/schema-reader-776246MJ.js +0 -3
  93. package/dist/server-5N76YCWC.js.map +0 -1
  94. package/dist/server-QIYIJHD5.cjs.map +0 -1
  95. package/dist/type-generator-MZ4X4DE5.js +0 -3
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vite/plugin.ts"],"names":["resolve"],"mappings":"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","file":"index.js","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\n\nexport interface SynapseVitePluginOptions {\n /** App name. If omitted, reads from ../manifest.json */\n appName?: string;\n /** Path to manifest.json. Default: ../manifest.json (relative to ui/) */\n manifest?: string;\n /**\n * Shell command to start the MCP server. If omitted, derived from manifest.\n * The server runs in stdio mode — stdin/stdout JSON-RPC.\n */\n serverCmd?: string;\n /** Set to false to disable the preview host page at /__preview */\n preview?: boolean;\n}\n\ninterface Manifest {\n name: string;\n version?: string;\n server?: {\n type?: string;\n entry_point?: string;\n mcp_config?: {\n command?: string;\n args?: string[];\n };\n };\n}\n\n/**\n * Synapse Vite plugin — full local dev experience for MCP apps.\n *\n * What it does:\n * - Reads ../manifest.json to get app name and server config\n * - Spawns the MCP server as a child process (stdio mode)\n * - Serves a preview host page at /__preview that iframes your app\n * - Proxies tool calls from the iframe through POST /__mcp to the server\n * - Handles the ext-apps handshake so Synapse hooks work\n * - HMR works inside the iframe — edit .tsx, see changes instantly\n *\n * Usage in vite.config.ts:\n * import { synapseVite } from \"@nimblebrain/synapse/vite\";\n * export default { plugins: [react(), viteSingleFile(), synapseVite()] };\n *\n * Then: cd ui && npm run dev && open http://localhost:5173/__preview\n */\nexport function synapseVite(options: SynapseVitePluginOptions = {}): Plugin {\n const enablePreview = options.preview !== false;\n let manifest: Manifest | null = null;\n let appName = options.appName ?? \"app\";\n let serverProcess: ChildProcess | null = null;\n const pendingRequests = new Map<\n string,\n { resolve: (v: unknown) => void; reject: (e: Error) => void }\n >();\n let serverBuffer = \"\";\n\n function loadManifest(root: string): Manifest | null {\n const manifestPath = options.manifest\n ? resolve(options.manifest)\n : resolve(root, \"..\", \"manifest.json\");\n\n if (!existsSync(manifestPath)) return null;\n try {\n return JSON.parse(readFileSync(manifestPath, \"utf-8\"));\n } catch {\n return null;\n }\n }\n\n function deriveServerCmd(m: Manifest, root: string): string | null {\n if (options.serverCmd) return options.serverCmd;\n const cfg = m.server?.mcp_config;\n if (!cfg?.command) return null;\n\n const serverDir = resolve(root, \"..\");\n let cmd = cfg.command;\n const args = cfg.args ?? [];\n\n // Python projects: use `uv run` if pyproject.toml exists\n if (cmd === \"python\" && existsSync(join(serverDir, \"pyproject.toml\"))) {\n cmd = \"uv run python\";\n }\n\n return `cd ${JSON.stringify(serverDir)} && ${cmd} ${args.join(\" \")}`;\n }\n\n function startServer(cmd: string): void {\n serverProcess = spawn(cmd, {\n shell: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n serverProcess.stderr?.on(\"data\", (d: Buffer) => {\n process.stderr.write(` [mcp] ${d}`);\n });\n\n serverProcess.stdout?.on(\"data\", (d: Buffer) => {\n serverBuffer += d.toString();\n // Parse line-delimited JSON-RPC responses\n const lines = serverBuffer.split(\"\\n\");\n serverBuffer = lines.pop() ?? \"\"; // keep incomplete line\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.id && pendingRequests.has(msg.id)) {\n const p = pendingRequests.get(msg.id);\n pendingRequests.delete(msg.id);\n p?.resolve(msg);\n }\n } catch {\n // Not JSON — log it\n process.stderr.write(` [mcp] ${line}\\n`);\n }\n }\n });\n\n serverProcess.on(\"exit\", (code) => {\n if (code !== null && code !== 0) {\n console.error(` [mcp] Server exited with code ${code}`);\n }\n serverProcess = null;\n });\n\n // Send initialize\n sendToServer({\n jsonrpc: \"2.0\",\n id: \"init-1\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"synapse-preview\", version: \"0.1.0\" },\n },\n });\n }\n\n function sendToServer(msg: Record<string, unknown>): void {\n if (!serverProcess?.stdin?.writable) return;\n serverProcess.stdin.write(`${JSON.stringify(msg)}\\n`);\n }\n\n function callServerTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const id = `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n pendingRequests.set(id, { resolve, reject });\n sendToServer({\n jsonrpc: \"2.0\",\n id,\n method: \"tools/call\",\n params: { name, arguments: args },\n });\n setTimeout(() => {\n if (pendingRequests.has(id)) {\n pendingRequests.delete(id);\n reject(new Error(\"Tool call timed out (10s)\"));\n }\n }, 10000);\n });\n }\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configResolved(config) {\n manifest = loadManifest(config.root);\n if (manifest?.name) {\n appName = options.appName ?? manifest.name;\n }\n },\n\n configureServer(server: ViteDevServer) {\n // Start MCP server\n if (enablePreview && manifest) {\n const cmd = deriveServerCmd(manifest, server.config.root);\n if (cmd) {\n console.log(`\\n [synapse] Starting MCP server: ${cmd}\\n`);\n startServer(cmd);\n }\n }\n\n server.middlewares.use((req, res, next) => {\n // CORS for iframe communication\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n\n // /__preview — bridge host page\n if (req.url === \"/__preview\" || req.url === \"/__preview/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(previewHostHtml(appName));\n return;\n }\n\n // POST /__mcp — tool call proxy\n if (req.method === \"POST\" && req.url === \"/__mcp\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on(\"end\", async () => {\n try {\n const msg = JSON.parse(body);\n const result = await callServerTool(msg.params.name, msg.params.arguments || {});\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: JSON.parse(body).id,\n error: { code: -32000, message: (err as Error).message },\n }),\n );\n }\n });\n return;\n }\n\n next();\n });\n },\n\n buildEnd() {\n // Kill server on build end (for production builds)\n if (serverProcess) {\n serverProcess.kill(\"SIGTERM\");\n serverProcess = null;\n }\n },\n };\n}\n\nfunction previewHostHtml(appName: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>${appName} — Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 10px 16px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 10px; font-size: 13px; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .name { font-weight: 600; }\n header .spacer { flex: 1; }\n header button { background: #334155; border: none; color: #e2e8f0; padding: 3px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n header .url { color: #64748b; font-size: 11px; font-family: monospace; }\n iframe { width: 100%; height: calc(100vh - 41px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <span class=\"name\">${appName}</span>\n <span class=\"spacer\"></span>\n <button id=\"toggle\">Toggle Theme</button>\n <span class=\"url\">Synapse Preview</span>\n </header>\n <iframe id=\"app\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var dark = true;\n\n function getTokens(d) {\n return d ? {\n \"--color-background-primary\":\"#0f172a\",\"--color-background-secondary\":\"#1e293b\",\n \"--color-background-tertiary\":\"#2a374a\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-text-secondary\":\"#94a3b8\",\"--color-text-tertiary\":\"#64748b\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-border-primary\":\"#334155\",\"--color-border-secondary\":\"#475569\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n } : {\n \"--color-background-primary\":\"#ffffff\",\"--color-background-secondary\":\"#f8fafc\",\n \"--color-background-tertiary\":\"#f1f5f9\",\"--color-text-primary\":\"#0f172a\",\n \"--color-text-secondary\":\"#64748b\",\"--color-text-tertiary\":\"#94a3b8\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-border-primary\":\"#e2e8f0\",\"--color-border-secondary\":\"#cbd5e1\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n };\n }\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n window.addEventListener(\"message\", async function(e) {\n if (e.source !== iframe.contentWindow) return;\n var msg = e.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n if (msg.method === \"ui/initialize\" && msg.id) {\n post({ jsonrpc:\"2.0\", id:msg.id, result: {\n protocolVersion:\"2026-01-26\",\n serverInfo:{name:\"nimblebrain\",version:\"preview\"},\n capabilities:{openLinks:{},serverTools:{}},\n hostContext:{theme:dark?\"dark\":\"light\",styles:{variables:getTokens(dark)}}\n }});\n return;\n }\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Tool calls — proxy via Vite middleware\n if (msg.method === \"tools/call\" && msg.id) {\n var originalId = msg.id;\n try {\n var r = await fetch(\"/__mcp\", {\n method:\"POST\", headers:{\"Content-Type\":\"application/json\"},\n body: JSON.stringify({jsonrpc:\"2.0\",id:msg.id,method:\"tools/call\",params:{name:msg.params.name,arguments:msg.params.arguments||{}}})\n });\n var response = await r.json();\n response.id = originalId;\n post(response);\n // Note: we intentionally do NOT emit synapse/data-changed here.\n // data-changed signals that an *external actor* (the agent) mutated\n // server state, prompting the UI to re-fetch. Tool calls originating\n // from the UI itself should not trigger this — doing so creates an\n // infinite loop (UI calls tool → data-changed → useDataSync re-fetches\n // → calls tool → data-changed → ...). In production, the host only\n // emits data-changed for agent-initiated tool calls.\n } catch(err) {\n post({jsonrpc:\"2.0\",id:originalId,error:{code:-32000,message:err.message}});\n }\n return;\n }\n\n // Log other messages\n if (msg.method === \"synapse/chat\") console.log(\"[chat]\", msg.params?.message);\n else if (msg.method === \"synapse/action\") console.log(\"[action]\", msg.params?.action, msg.params);\n else if (msg.method === \"ui/update-model-context\") { console.log(\"[model-context]\", msg.params?.structuredContent); if (msg.id) post({jsonrpc:\"2.0\",id:msg.id,result:{}}); }\n else if (msg.method === \"synapse/keydown\") { /* ignore */ }\n else if (msg.method) console.log(\"[bridge]\", msg.method, msg);\n });\n\n document.getElementById(\"toggle\").onclick = function() {\n dark = !dark;\n document.body.style.background = dark ? \"#0f172a\" : \"#f1f5f9\";\n post({jsonrpc:\"2.0\",method:\"ui/notifications/host-context-changed\",params:{theme:dark?\"dark\":\"light\",styles:{variables:getTokens(dark)}}});\n };\n\n // Load the iframe AFTER the message listener is attached to avoid\n // a race where the app sends ui/initialize before the bridge is ready.\n iframe.src = \"/\";\n </script>\n</body>\n</html>`;\n}\n"]}
1
+ {"version":3,"sources":["../../src/vite/plugin.ts"],"names":["resolve"],"mappings":"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","file":"index.js","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport type { ServerResponse } from \"node:http\";\nimport { join, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport {\n LIST_RESOURCES_METHOD,\n READ_RESOURCE_METHOD,\n RESOURCE_LIST_CHANGED_METHOD,\n TOOLS_CALL_METHOD,\n} from \"../event-map.js\";\n\n/**\n * The server-bound requests this host proxies, which is exactly what its\n * handshake announces: `serverTools` for the tool call, `serverResources` for\n * the two resource methods. An allowlist in both directions — the page sends\n * nothing else to `/__mcp`, and `/__mcp` forwards nothing else to the server,\n * so a method the host never announced cannot reach the server through it.\n *\n * Adding a method here without adding the matching capability below, or the\n * reverse, is the bug this list exists to make obvious: a host that answers\n * what it does not announce is unusable, and one that announces what it does\n * not answer leaves the caller waiting on a reply nobody will send.\n */\nconst PROXIED_SERVER_METHODS: readonly string[] = [\n TOOLS_CALL_METHOD,\n READ_RESOURCE_METHOD,\n LIST_RESOURCES_METHOD,\n];\n\nexport interface SynapseVitePluginOptions {\n /** App name. If omitted, reads from ../manifest.json */\n appName?: string;\n /** Path to manifest.json. Default: ../manifest.json (relative to ui/) */\n manifest?: string;\n /**\n * Shell command to start the MCP server. If omitted, derived from manifest.\n * The server runs in stdio mode — stdin/stdout JSON-RPC.\n */\n serverCmd?: string;\n /** Set to false to disable the preview host page at /__preview */\n preview?: boolean;\n}\n\ninterface Manifest {\n name: string;\n version?: string;\n server?: {\n type?: string;\n entry_point?: string;\n mcp_config?: {\n command?: string;\n args?: string[];\n };\n };\n}\n\n/**\n * Synapse Vite plugin — full local dev experience for MCP apps.\n *\n * What it does:\n * - Reads ../manifest.json to get app name and server config\n * - Spawns the MCP server as a child process (stdio mode)\n * - Serves a preview host page at /__preview that iframes your app\n * - Proxies the iframe's tool calls and resource reads through POST /__mcp to\n * the server, the pair its handshake announces as `serverTools` and\n * `serverResources`\n * - Forwards the server's `notifications/resources/list_changed` to the iframe\n * (over GET /__events), as an MCP Apps host does, so `useDataSync` fires\n * - Handles the ext-apps handshake so Synapse hooks work\n * - HMR works inside the iframe — edit .tsx, see changes instantly\n *\n * Usage in vite.config.ts:\n * import { synapseVite } from \"@nimblebrain/synapse/vite\";\n * export default { plugins: [react(), viteSingleFile(), synapseVite()] };\n *\n * Then: cd ui && npm run dev && open http://localhost:5173/__preview\n */\nexport function synapseVite(options: SynapseVitePluginOptions = {}): Plugin {\n const enablePreview = options.preview !== false;\n let manifest: Manifest | null = null;\n let appName = options.appName ?? \"app\";\n let serverProcess: ChildProcess | null = null;\n const pendingRequests = new Map<\n string,\n { resolve: (v: unknown) => void; reject: (e: Error) => void }\n >();\n // Preview pages listening on GET /__events for the server's notifications.\n const eventClients = new Set<ServerResponse>();\n\n /**\n * Hand a server notification to every open preview page, which posts it into\n * the app iframe. Only the one an MCP Apps host forwards to a view is passed\n * on; anything else the server says stays between it and the dev server.\n */\n function forwardNotification(msg: { method?: unknown; params?: unknown }): void {\n if (msg.method !== RESOURCE_LIST_CHANGED_METHOD) return;\n const frame = `data: ${JSON.stringify({ method: msg.method, params: msg.params ?? {} })}\\n\\n`;\n for (const client of eventClients) client.write(frame);\n }\n let serverBuffer = \"\";\n\n function loadManifest(root: string): Manifest | null {\n const manifestPath = options.manifest\n ? resolve(options.manifest)\n : resolve(root, \"..\", \"manifest.json\");\n\n if (!existsSync(manifestPath)) return null;\n try {\n return JSON.parse(readFileSync(manifestPath, \"utf-8\"));\n } catch {\n return null;\n }\n }\n\n function deriveServerCmd(m: Manifest, root: string): string | null {\n if (options.serverCmd) return options.serverCmd;\n const cfg = m.server?.mcp_config;\n if (!cfg?.command) return null;\n\n const serverDir = resolve(root, \"..\");\n let cmd = cfg.command;\n const args = cfg.args ?? [];\n\n // Python projects: use `uv run` if pyproject.toml exists\n if (cmd === \"python\" && existsSync(join(serverDir, \"pyproject.toml\"))) {\n cmd = \"uv run python\";\n }\n\n return `cd ${JSON.stringify(serverDir)} && ${cmd} ${args.join(\" \")}`;\n }\n\n function startServer(cmd: string): void {\n serverProcess = spawn(cmd, {\n shell: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n serverProcess.stderr?.on(\"data\", (d: Buffer) => {\n process.stderr.write(` [mcp] ${d}`);\n });\n\n serverProcess.stdout?.on(\"data\", (d: Buffer) => {\n serverBuffer += d.toString();\n // Parse line-delimited JSON-RPC responses\n const lines = serverBuffer.split(\"\\n\");\n serverBuffer = lines.pop() ?? \"\"; // keep incomplete line\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.id && pendingRequests.has(msg.id)) {\n const p = pendingRequests.get(msg.id);\n pendingRequests.delete(msg.id);\n p?.resolve(msg);\n } else if (msg.id === undefined && typeof msg.method === \"string\") {\n forwardNotification(msg);\n }\n } catch {\n // Not JSON — log it\n process.stderr.write(` [mcp] ${line}\\n`);\n }\n }\n });\n\n serverProcess.on(\"exit\", (code) => {\n if (code !== null && code !== 0) {\n console.error(` [mcp] Server exited with code ${code}`);\n }\n serverProcess = null;\n });\n\n // Send initialize\n sendToServer({\n jsonrpc: \"2.0\",\n id: \"init-1\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"synapse-preview\", version: \"0.1.0\" },\n },\n });\n }\n\n function sendToServer(msg: Record<string, unknown>): void {\n if (!serverProcess?.stdin?.writable) return;\n serverProcess.stdin.write(`${JSON.stringify(msg)}\\n`);\n }\n\n function callServer(method: string, params: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const id = `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n pendingRequests.set(id, { resolve, reject });\n sendToServer({ jsonrpc: \"2.0\", id, method, params });\n setTimeout(() => {\n if (pendingRequests.has(id)) {\n pendingRequests.delete(id);\n reject(new Error(`${method} timed out (10s)`));\n }\n }, 10000);\n });\n }\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configResolved(config) {\n manifest = loadManifest(config.root);\n if (manifest?.name) {\n appName = options.appName ?? manifest.name;\n }\n },\n\n configureServer(server: ViteDevServer) {\n // Start MCP server\n if (enablePreview && manifest) {\n const cmd = deriveServerCmd(manifest, server.config.root);\n if (cmd) {\n console.log(`\\n [synapse] Starting MCP server: ${cmd}\\n`);\n startServer(cmd);\n }\n }\n\n server.middlewares.use((req, res, next) => {\n // CORS for iframe communication\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n\n // /__preview — bridge host page\n if (req.url === \"/__preview\" || req.url === \"/__preview/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(vitePreviewHostHtml(appName));\n return;\n }\n\n // GET /__events — the server's notifications, pushed to the preview page\n if (req.method === \"GET\" && req.url === \"/__events\") {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n res.write(\": open\\n\\n\");\n eventClients.add(res);\n req.on(\"close\", () => {\n eventClients.delete(res);\n });\n return;\n }\n\n // POST /__mcp — the server-bound request proxy\n if (req.method === \"POST\" && req.url === \"/__mcp\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on(\"end\", async () => {\n try {\n const msg = JSON.parse(body);\n if (!PROXIED_SERVER_METHODS.includes(msg.method)) {\n throw new Error(`${msg.method} is not a method this host proxies`);\n }\n const result = await callServer(msg.method, msg.params ?? {});\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: JSON.parse(body).id,\n error: { code: -32000, message: (err as Error).message },\n }),\n );\n }\n });\n return;\n }\n\n next();\n });\n },\n\n buildEnd() {\n // Kill server on build end (for production builds)\n if (serverProcess) {\n serverProcess.kill(\"SIGTERM\");\n serverProcess = null;\n }\n },\n };\n}\n\n/**\n * The bridge host page the dev server serves at \\`/__preview\\`.\n *\n * Named apart from \\`previewHostHtml\\` in \\`src/preview/server.ts\\`, which is the\n * standalone \\`synapse preview\\` harness: this package has two hand-written\n * hosts, and the conformance suite drives both, so one name for two pages would\n * be a name that resolves to whichever was imported last.\n */\nexport function vitePreviewHostHtml(appName: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>${appName} — Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 10px 16px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 10px; font-size: 13px; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .name { font-weight: 600; }\n header .spacer { flex: 1; }\n header button { background: #334155; border: none; color: #e2e8f0; padding: 3px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n header .url { color: #64748b; font-size: 11px; font-family: monospace; }\n iframe { width: 100%; height: calc(100vh - 41px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <span class=\"name\">${appName}</span>\n <span class=\"spacer\"></span>\n <button id=\"toggle\">Toggle Theme</button>\n <span class=\"url\">Synapse Preview</span>\n </header>\n <iframe id=\"app\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var dark = true;\n var PROXIED = ${JSON.stringify(PROXIED_SERVER_METHODS)};\n\n // Every key here is one the spec's style-variable enum names. That enum is\n // a strict record, so a single extra key makes a spec client reject the\n // whole \\`ui/initialize\\` result and refuse to connect — not just ignore the\n // key. The kit's own neutral defaults back anything a host leaves out.\n function getTokens(d) {\n return d ? {\n \"--color-background-primary\":\"#0f172a\",\"--color-background-secondary\":\"#1e293b\",\n \"--color-background-tertiary\":\"#2a374a\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-text-secondary\":\"#94a3b8\",\"--color-text-tertiary\":\"#64748b\",\n \"--color-border-primary\":\"#334155\",\"--color-border-secondary\":\"#475569\",\n \"--color-ring-primary\":\"#6366f1\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n } : {\n \"--color-background-primary\":\"#ffffff\",\"--color-background-secondary\":\"#f8fafc\",\n \"--color-background-tertiary\":\"#f1f5f9\",\"--color-text-primary\":\"#0f172a\",\n \"--color-text-secondary\":\"#64748b\",\"--color-text-tertiary\":\"#94a3b8\",\n \"--color-border-primary\":\"#e2e8f0\",\"--color-border-secondary\":\"#cbd5e1\",\n \"--color-ring-primary\":\"#6366f1\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n };\n }\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n // A request is a frame carrying an id — and \\`0\\` is a perfectly good id.\n // The MCP SDK numbers from zero, so a truthiness test drops the first\n // request every spec client ever sends, which is its \\`ui/initialize\\`.\n function isRequest(msg) { return msg.id !== undefined && msg.id !== null; }\n\n window.addEventListener(\"message\", async function(e) {\n if (e.source !== iframe.contentWindow) return;\n var msg = e.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n // \\`hostInfo\\`/\\`hostCapabilities\\` are the spec's field names, and a spec\n // client validates the result against them — the official ext-apps\n // \\`App\\` refuses to connect to anything else.\n if (msg.method === \"ui/initialize\" && isRequest(msg)) {\n post({ jsonrpc:\"2.0\", id:msg.id, result: {\n protocolVersion:\"2026-01-26\",\n hostInfo:{name:\"nimblebrain\",version:\"preview\"},\n hostCapabilities:{openLinks:{},serverTools:{},serverResources:{listChanged:true}},\n hostContext:{theme:dark?\"dark\":\"light\",styles:{variables:getTokens(dark)}}\n }});\n return;\n }\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Server-bound requests — proxied via the Vite middleware. The set is\n // exactly what \\`serverTools\\` and \\`serverResources\\` above announce, so\n // neither is declared without something on the other end.\n if (PROXIED.indexOf(msg.method) !== -1 && isRequest(msg)) {\n var originalId = msg.id;\n try {\n var r = await fetch(\"/__mcp\", {\n method:\"POST\", headers:{\"Content-Type\":\"application/json\"},\n body: JSON.stringify({jsonrpc:\"2.0\",id:msg.id,method:msg.method,params:msg.params||{}})\n });\n var response = await r.json();\n response.id = originalId;\n post(response);\n // The page announces no data change of its own. A change is the\n // server's to announce, and that reaches the app over /__events. One\n // fired here on the app's own call would loop: the call fires it, the\n // app re-fetches, and the re-fetch fires it again.\n } catch(err) {\n post({jsonrpc:\"2.0\",id:originalId,error:{code:-32000,message:err.message}});\n }\n return;\n }\n\n // Log other messages\n if (msg.method === \"synapse/chat\") console.log(\"[chat]\", msg.params?.message);\n else if (msg.method === \"synapse/action\") console.log(\"[action]\", msg.params?.action, msg.params);\n else if (msg.method === \"ui/update-model-context\") { console.log(\"[model-context]\", msg.params?.structuredContent); if (isRequest(msg)) post({jsonrpc:\"2.0\",id:msg.id,result:{}}); }\n else if (msg.method === \"synapse/keydown\") { /* ignore */ }\n else if (msg.method) console.log(\"[bridge]\", msg.method, msg);\n });\n\n document.getElementById(\"toggle\").onclick = function() {\n dark = !dark;\n document.body.style.background = dark ? \"#0f172a\" : \"#f1f5f9\";\n post({jsonrpc:\"2.0\",method:\"ui/notifications/host-context-changed\",params:{theme:dark?\"dark\":\"light\",styles:{variables:getTokens(dark)}}});\n };\n\n // The server's own notifications, forwarded into the app as an MCP Apps\n // host forwards them. The dev server passes on only the ones a host would.\n new EventSource(\"/__events\").onmessage = function(e) {\n var n = JSON.parse(e.data);\n post({jsonrpc:\"2.0\",method:n.method,params:n.params});\n };\n\n // Load the iframe AFTER the message listener is attached to avoid\n // a race where the app sends ui/initialize before the bridge is ready.\n iframe.src = \"/\";\n </script>\n</body>\n</html>`;\n}\n"]}
@@ -1,14 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ require('./chunk-Q7SFCCGT.cjs');
3
4
  var fs = require('fs');
4
5
  var path = require('path');
5
6
 
6
- // src/codegen/writer.ts
7
7
  function writeOutput(content, outputPath) {
8
8
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
9
9
  fs.writeFileSync(outputPath, content, "utf-8");
10
10
  }
11
11
 
12
12
  exports.writeOutput = writeOutput;
13
- //# sourceMappingURL=writer-NDK4U6C5.cjs.map
14
- //# sourceMappingURL=writer-NDK4U6C5.cjs.map
13
+ //# sourceMappingURL=writer-HDRCQSX2.cjs.map
14
+ //# sourceMappingURL=writer-HDRCQSX2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codegen/writer.ts"],"names":["mkdirSync","dirname","writeFileSync"],"mappings":";;;;;;AAMO,SAAS,WAAA,CAAY,SAAiB,UAAA,EAA0B;AACrE,EAAAA,YAAA,CAAUC,aAAQ,UAAU,CAAA,EAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAClD,EAAAC,gBAAA,CAAc,UAAA,EAAY,SAAS,OAAO,CAAA;AAC5C","file":"writer-NDK4U6C5.cjs","sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\n/**\n * Write generated TypeScript to a file, creating parent directories as needed.\n */\nexport function writeOutput(content: string, outputPath: string): void {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, content, \"utf-8\");\n}\n"]}
1
+ {"version":3,"sources":["../src/codegen/writer.ts"],"names":["mkdirSync","dirname","writeFileSync"],"mappings":";;;;;;AAMO,SAAS,WAAA,CAAY,SAAiB,UAAA,EAA0B;AACrE,EAAAA,YAAA,CAAUC,aAAQ,UAAU,CAAA,EAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAClD,EAAAC,gBAAA,CAAc,UAAA,EAAY,SAAS,OAAO,CAAA;AAC5C","file":"writer-HDRCQSX2.cjs","sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\n/**\n * Write generated TypeScript to a file, creating parent directories as needed.\n */\nexport function writeOutput(content: string, outputPath: string): void {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, content, \"utf-8\");\n}\n"]}
@@ -1,12 +1,12 @@
1
+ import './chunk-PZ5AY32C.js';
1
2
  import { mkdirSync, writeFileSync } from 'fs';
2
3
  import { dirname } from 'path';
3
4
 
4
- // src/codegen/writer.ts
5
5
  function writeOutput(content, outputPath) {
6
6
  mkdirSync(dirname(outputPath), { recursive: true });
7
7
  writeFileSync(outputPath, content, "utf-8");
8
8
  }
9
9
 
10
10
  export { writeOutput };
11
- //# sourceMappingURL=writer-ZNBXYUYC.js.map
12
- //# sourceMappingURL=writer-ZNBXYUYC.js.map
11
+ //# sourceMappingURL=writer-QNDPMEYD.js.map
12
+ //# sourceMappingURL=writer-QNDPMEYD.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codegen/writer.ts"],"names":[],"mappings":";;;;AAMO,SAAS,WAAA,CAAY,SAAiB,UAAA,EAA0B;AACrE,EAAA,SAAA,CAAU,QAAQ,UAAU,CAAA,EAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAClD,EAAA,aAAA,CAAc,UAAA,EAAY,SAAS,OAAO,CAAA;AAC5C","file":"writer-ZNBXYUYC.js","sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\n/**\n * Write generated TypeScript to a file, creating parent directories as needed.\n */\nexport function writeOutput(content: string, outputPath: string): void {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, content, \"utf-8\");\n}\n"]}
1
+ {"version":3,"sources":["../src/codegen/writer.ts"],"names":[],"mappings":";;;;AAMO,SAAS,WAAA,CAAY,SAAiB,UAAA,EAA0B;AACrE,EAAA,SAAA,CAAU,QAAQ,UAAU,CAAA,EAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAClD,EAAA,aAAA,CAAc,UAAA,EAAY,SAAS,OAAO,CAAA;AAC5C","file":"writer-QNDPMEYD.js","sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\n/**\n * Write generated TypeScript to a file, creating parent directories as needed.\n */\nexport function writeOutput(content: string, outputPath: string): void {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, content, \"utf-8\");\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nimblebrain/synapse",
3
- "version": "0.17.1",
3
+ "version": "0.19.0",
4
4
  "description": "Agent-aware app SDK for the MCP ext-apps protocol",
5
5
  "type": "module",
6
6
  "exports": {
@@ -56,12 +56,13 @@
56
56
  "build": "tsup",
57
57
  "test": "vitest run",
58
58
  "test:watch": "vitest",
59
- "typecheck": "tsc --noEmit",
59
+ "typecheck": "tsc --noEmit && tsc --noEmit -p conformance/tsconfig.json",
60
60
  "lint": "biome check .",
61
61
  "lint:fix": "biome check --write .",
62
62
  "format": "biome format --write .",
63
63
  "ci": "npm run lint && npm run typecheck && npm run build && npm run test",
64
- "prepublishOnly": "npm run build"
64
+ "prepublishOnly": "npm run build",
65
+ "conformance": "node conformance/run.mjs"
65
66
  },
66
67
  "repository": {
67
68
  "type": "git",
@@ -73,7 +74,7 @@
73
74
  },
74
75
  "author": "NimbleBrain Inc.",
75
76
  "peerDependencies": {
76
- "@modelcontextprotocol/ext-apps": "^1.3.1",
77
+ "@modelcontextprotocol/ext-apps": "^1.7.5",
77
78
  "react": "^18.0.0 || ^19.0.0"
78
79
  },
79
80
  "peerDependenciesMeta": {
@@ -83,11 +84,13 @@
83
84
  },
84
85
  "devDependencies": {
85
86
  "@biomejs/biome": "^2.4.9",
86
- "@modelcontextprotocol/ext-apps": "^1.3.1",
87
+ "@modelcontextprotocol/ext-apps": "^1.7.5",
87
88
  "@testing-library/react": "^16.3.2",
88
89
  "@types/node": "^25.5.0",
89
90
  "@types/react": "^19.2.14",
91
+ "esbuild": "^0.28.2",
90
92
  "happy-dom": "^20.8.8",
93
+ "playwright": "^1.58.0",
91
94
  "react": "^19.2.4",
92
95
  "tsup": "^8.5.1",
93
96
  "typescript": "^5.8.3",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/host/theme.ts","../src/host/types.ts","../src/host/adapters/chatgpt.ts","../src/host/data.ts","../src/host/adapters/inline.ts","../src/host/adapters/mcpapps.ts","../src/host/detect.ts","../src/host/connect.ts"],"names":["applyTheme","foldFontFaces","fontFacesKey"],"mappings":"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","file":"chunk-EHLQWCYV.cjs","sourcesContent":["import { applyTheme } from \"../theme-defaults.js\";\nimport type { SynapseUITheme } from \"./types.js\";\n\n/**\n * Apply a resolved theme to the DOM for the cross-host client.\n *\n * Two conventions coexist across Synapse components, so the client drives both:\n *\n * - `document.documentElement[data-theme=\"light\"|\"dark\"]` — how self-contained\n * HTML components (Bassethound's report) gate their `--var` palettes, and the\n * lever a host's light/dark signal actually flips.\n * - CSS custom properties via {@link applyTheme} — how the\n * `@nimblebrain/synapse/ui` token components consume theme, backed by the\n * SDK's neutral defaults so every referenced var resolves in both modes.\n *\n * Setting both means an app can use either convention (or a host that supplies\n * only a mode string, like the OpenAI Apps SDK, still themes correctly). SSR-safe.\n *\n * Any `fontFaces` the host supplies are loaded here too — a token can name a\n * family but not load it, so the two travel together. A host that sends none\n * leaves the web-safe fallbacks in force.\n */\nexport function applyHostTheme(theme: SynapseUITheme): void {\n if (typeof document !== \"undefined\") {\n document.documentElement.setAttribute(\"data-theme\", theme.mode);\n }\n applyTheme(theme.mode, theme.tokens, theme.fontFaces);\n}\n\n/** Read the OS-level color scheme as a sane default for hosts that don't push a\n * theme until later (mcp-ui) or ever (standalone). */\nexport function preferredMode(win: Window | undefined): \"light\" | \"dark\" {\n try {\n if (win?.matchMedia?.(\"(prefers-color-scheme: dark)\").matches) return \"dark\";\n } catch {\n // matchMedia unavailable (older test envs) — fall through to light.\n }\n return \"light\";\n}\n\n/** Coerce an arbitrary host-supplied theme signal to a mode. Accepts the string\n * form (`\"dark\"`) both hosts use; anything else falls back. */\nexport function coerceMode(value: unknown, fallback: \"light\" | \"dark\"): \"light\" | \"dark\" {\n return value === \"light\" || value === \"dark\" ? value : fallback;\n}\n","/**\n * Cross-host UI client — types and wire constants.\n *\n * The cross-host client (`connectUI`) renders one Synapse-authored component in\n * hosts that each speak a different bridge: ChatGPT (OpenAI Apps SDK), Claude\n * (mcp-ui), and plain/standalone. Apps code against `synapse.*` and never touch\n * a host protocol. This is a **push-first** surface: the tool output that spawned\n * the widget is delivered at render (`data()` / `onData()`); `callTool()` is the\n * pull escape hatch, advertised per host via `capabilities()`.\n *\n * This layer intentionally has ZERO dependency on `@modelcontextprotocol/*` — the\n * ChatGPT / mcp-ui / inline bridges are pure `window.openai` + `postMessage`, so\n * the IIFE that apps inline stays tiny (no Zod, no ext-apps schemas).\n */\n\n/**\n * The host the client resolved to, as reported by `synapse.host()`. An escape\n * hatch — apps should rarely branch on it; `capabilities()` is the supported way\n * to feature-detect. `\"nimblebrain\"` is reserved for the runtime adapter (P3).\n */\nimport type { FontFaceDescriptor } from \"../types.js\";\n\nexport type HostKind = \"chatgpt\" | \"claude\" | \"nimblebrain\" | \"generic\";\n\n/** Resolved theme. `mode` always resolves to light or dark. `tokens` are CSS\n * custom properties the host publishes — the MCP Apps adapter reads them from\n * `hostContext.styles.variables`; where a host sends none they stay empty and the\n * SDK's neutral defaults back them. */\nexport interface SynapseUITheme {\n mode: \"light\" | \"dark\";\n tokens: Record<string, string>;\n /** Font faces the host wants loaded. Absent where the host sends none, which\n * leaves the SDK's web-safe fallbacks in force. See `FontFaceDescriptor`. */\n fontFaces?: FontFaceDescriptor[];\n}\n\n/** What the active host actually supports. `data()`/`onData()`/`theme()`/\n * `resize()` work everywhere; these three vary. */\nexport interface HostCapabilities {\n /** `callTool()` can reach the server (widget→server fetch). */\n pull: boolean;\n /** `sendPrompt()` reaches the agent conversation. */\n sendPrompt: boolean;\n /** `openLink()` opens an external URL through the host. */\n openLink: boolean;\n}\n\n/** Thrown by `callTool()` when the active host offers no widget→server call. */\nexport class HostUnsupportedError extends Error {\n constructor(feature: string, host: HostKind) {\n super(`\"${feature}\" is not supported by the \"${host}\" host`);\n this.name = \"HostUnsupportedError\";\n }\n}\n\nexport interface ConnectUIOptions {\n /** App name — informational; forwarded to hosts that accept an appInfo. */\n name?: string;\n /** App semver — informational. */\n version?: string;\n /**\n * Force a host adapter instead of auto-detecting. Used by preview harnesses,\n * SSR, and tests; production apps omit it and let the SDK feature-detect.\n * `\"claude\"` → MCP Apps standard adapter, `\"chatgpt\"` → OpenAI Apps adapter,\n * `\"generic\"` → inline adapter.\n */\n host?: HostKind;\n /**\n * `id` of the `<script type=\"application/json\">` element carrying pushed data\n * baked into the HTML (the mcp-ui / SSR path). Defaults to\n * {@link SYNAPSE_DATA_ELEMENT_ID}.\n */\n dataElementId?: string;\n /**\n * Auto-report content height to the host on layout changes (mcp-ui only).\n * Defaults to `true`. Set `false` to size manually via `resize()`.\n */\n autoResize?: boolean;\n /** Window to bind to. Defaults to the global `window`. Injectable for tests. */\n window?: Window & typeof globalThis;\n}\n\n/**\n * The public cross-host client. Bound to `synapse` by convention.\n *\n * ```ts\n * const synapse = connectUI();\n * synapse.onData(render); // future pushes/updates\n * render(synapse.data()); // current value (may be null → empty state)\n * ```\n */\nexport interface SynapseUIClient {\n /** The current pushed data, or `null` before anything has been delivered. */\n data<T = unknown>(): T | null;\n /** Subscribe to data updates (NOT replayed — read `data()` for the current\n * value). Returns an unsubscribe. */\n onData<T = unknown>(cb: (data: T) => void): () => void;\n\n /** The current resolved theme. */\n theme(): SynapseUITheme;\n /** Subscribe to theme changes. The client already applies the theme to the DOM\n * (`data-theme` attribute + CSS variables) before this fires. */\n onTheme(cb: (theme: SynapseUITheme) => void): () => void;\n\n /** Widget→server tool call. Rejects with {@link HostUnsupportedError} where the\n * host advertises no pull (`capabilities().pull === false`). */\n callTool<O = unknown>(name: string, args?: Record<string, unknown>): Promise<O>;\n /** Send a follow-up message to the agent conversation. No-op where unsupported. */\n sendPrompt(text: string): void;\n /** Open an external URL through the host (falls back to `window.open`). */\n openLink(url: string): void;\n /** Report content height to the host. Omit `height` to measure `document.body`. */\n resize(height?: number): void;\n\n /** What the active host supports. */\n capabilities(): HostCapabilities;\n /** The resolved host — an escape hatch; prefer `capabilities()`. */\n host(): HostKind;\n /** Tear down listeners/observers. */\n destroy(): void;\n}\n\n/**\n * Internal adapter contract. One per host bridge; the client is a thin façade\n * over the selected adapter.\n */\nexport interface HostAdapter {\n readonly host: HostKind;\n getData<T = unknown>(): T | null;\n onData<T = unknown>(cb: (data: T) => void): () => void;\n getTheme(): SynapseUITheme;\n onTheme(cb: (theme: SynapseUITheme) => void): () => void;\n callTool<O = unknown>(name: string, args?: Record<string, unknown>): Promise<O>;\n sendPrompt(text: string): void;\n openLink(url: string): void;\n resize(height?: number): void;\n capabilities(): HostCapabilities;\n /** Begin listening / send the ready handshake / read baked-in data. Called once\n * by `connectUI` synchronously so `getData()` is populated on return. */\n start(): void;\n destroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Wire constants — the bridge message shapes each host speaks. Centralized so a\n// host protocol tweak is a one-line change, not a grep-and-pray.\n// ---------------------------------------------------------------------------\n\n/** Default `id` of the baked-in data `<script type=\"application/json\">`. */\nexport const SYNAPSE_DATA_ELEMENT_ID = \"synapse-ui-data\";\n\n/** mcp-ui iframe-lifecycle messages (child → host, host → child). */\nexport const MCPUI_READY = \"ui-lifecycle-iframe-ready\";\nexport const MCPUI_RENDER_DATA = \"ui-lifecycle-iframe-render-data\";\nexport const MCPUI_SIZE_CHANGE = \"ui-size-change\";\n/** mcp-ui action messages (child → host). */\nexport const MCPUI_LINK = \"link\";\nexport const MCPUI_PROMPT = \"prompt\";\n\n/** OpenAI Apps SDK globals-broadcast event (host → child). */\nexport const OPENAI_SET_GLOBALS = \"openai:set_globals\";\n\n// ---------------------------------------------------------------------------\n// MCP Apps standard (SEP-1865) — the convergence bridge, primary for Claude\n// Desktop and the NimbleBrain runtime. The View iframe is an MCP client and the\n// host an MCP server; they exchange raw JSON-RPC 2.0 objects over `postMessage`\n// (no wrapper envelope). Method names are canonical to the ext-apps spec.\n// ---------------------------------------------------------------------------\n\n/** Protocol version exchanged in the `ui/initialize` handshake. */\nexport const MCPAPP_PROTOCOL_VERSION = \"2026-01-26\";\n/** View → host handshake request; the result carries the host context. */\nexport const MCPAPP_INITIALIZE = \"ui/initialize\";\n/** View → host notification sent once after the init result — the host holds all\n * pushes until it arrives. */\nexport const MCPAPP_INITIALIZED = \"ui/notifications/initialized\";\n/** Host → view notification carrying the tool output. `params` IS the\n * `CallToolResult` (data at `params.structuredContent`) — no `result` wrapper. */\nexport const MCPAPP_TOOL_RESULT = \"ui/notifications/tool-result\";\n/** Host → view notification carrying a partial host context (theme, styles, …). */\nexport const MCPAPP_HOST_CONTEXT_CHANGED = \"ui/notifications/host-context-changed\";\n/** View → host notification reporting intrinsic size (`{ width?, height? }`). A\n * host may keep the frame hidden until it receives one, so it is sent promptly. */\nexport const MCPAPP_SIZE_CHANGED = \"ui/notifications/size-changed\";\n/** View → host request: open an external URL (`{ url }`). */\nexport const MCPAPP_OPEN_LINK = \"ui/open-link\";\n/** View → host request: send a follow-up to the conversation\n * (`{ role: \"user\", content: ContentBlock[] }`). */\nexport const MCPAPP_MESSAGE = \"ui/message\";\n/** Host → view request: graceful teardown; the view replies `{}`. */\nexport const MCPAPP_TEARDOWN = \"ui/resource-teardown\";\n/** Standard MCP method the View may call over the same channel (pull). */\nexport const MCP_TOOLS_CALL = \"tools/call\";\n","import { coerceMode } from \"../theme.js\";\nimport {\n type ConnectUIOptions,\n type HostAdapter,\n type HostCapabilities,\n HostUnsupportedError,\n OPENAI_SET_GLOBALS,\n type SynapseUITheme,\n} from \"../types.js\";\n\n/**\n * The subset of the OpenAI Apps SDK `window.openai` surface this adapter uses.\n * All members are optional — the SDK version a host ships may not carry every\n * one, so each call is guarded and degraded.\n */\ninterface OpenAiHost {\n toolOutput?: unknown;\n theme?: unknown;\n callTool?: (name: string, args?: Record<string, unknown>) => Promise<unknown>;\n sendFollowUpMessage?: (arg: { prompt: string }) => void;\n /** Older casing seen in the wild — tried as a fallback. */\n sendFollowupMessage?: (arg: { prompt: string }) => void;\n openExternal?: (arg: { href: string }) => void;\n}\n\ninterface SetGlobalsDetail {\n globals?: { toolOutput?: unknown; theme?: unknown };\n}\n\n/**\n * ChatGPT (OpenAI Apps SDK) adapter.\n *\n * Data arrives on `window.openai.toolOutput` (available synchronously before the\n * widget script runs) and updates via the `openai:set_globals` event. Theme is a\n * mode string on the same surface. The host auto-sizes the iframe, so `resize()`\n * is a no-op. `callTool` is available when the host exposes `window.openai.callTool`.\n */\nexport function createChatGPTAdapter(\n win: Window & typeof globalThis,\n _options: ConnectUIOptions,\n): HostAdapter {\n const openai = () => (win as unknown as { openai?: OpenAiHost }).openai;\n\n let currentData: unknown = openai()?.toolOutput ?? null;\n let currentTheme: SynapseUITheme = {\n mode: coerceMode(openai()?.theme, \"light\"),\n tokens: {},\n };\n\n const dataCbs = new Set<(d: unknown) => void>();\n const themeCbs = new Set<(t: SynapseUITheme) => void>();\n let destroyed = false;\n\n const onSetGlobals = (event: Event) => {\n if (destroyed) return;\n const globals = (event as CustomEvent<SetGlobalsDetail>).detail?.globals;\n if (!globals) return;\n if (\"toolOutput\" in globals && globals.toolOutput != null) {\n currentData = globals.toolOutput;\n for (const cb of dataCbs) cb(currentData);\n }\n if (\"theme\" in globals && globals.theme != null) {\n const mode = coerceMode(globals.theme, currentTheme.mode);\n if (mode !== currentTheme.mode) {\n currentTheme = { mode, tokens: {} };\n for (const cb of themeCbs) cb(currentTheme);\n }\n }\n };\n\n return {\n host: \"chatgpt\",\n getData: <T>() => currentData as T | null,\n onData(cb) {\n dataCbs.add(cb as (d: unknown) => void);\n return () => dataCbs.delete(cb as (d: unknown) => void);\n },\n getTheme: () => currentTheme,\n onTheme(cb) {\n themeCbs.add(cb);\n return () => themeCbs.delete(cb);\n },\n async callTool<O>(name: string, args?: Record<string, unknown>): Promise<O> {\n const call = openai()?.callTool;\n if (!call) throw new HostUnsupportedError(\"callTool\", \"chatgpt\");\n return (await call(name, args ?? {})) as O;\n },\n sendPrompt(text: string) {\n const o = openai();\n const send = o?.sendFollowUpMessage ?? o?.sendFollowupMessage;\n send?.({ prompt: text });\n },\n openLink(url: string) {\n const open = openai()?.openExternal;\n if (open) open({ href: url });\n else win.open(url, \"_blank\", \"noopener,noreferrer\");\n },\n resize() {\n // ChatGPT auto-sizes the widget iframe — nothing to report.\n },\n capabilities(): HostCapabilities {\n const o = openai();\n return {\n pull: typeof o?.callTool === \"function\",\n sendPrompt:\n typeof o?.sendFollowUpMessage === \"function\" ||\n typeof o?.sendFollowupMessage === \"function\",\n openLink: true,\n };\n },\n start() {\n win.addEventListener(OPENAI_SET_GLOBALS, onSetGlobals as EventListener, { passive: true });\n // Re-read in case the host mutated globals between construction and start.\n currentData = openai()?.toolOutput ?? currentData;\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n win.removeEventListener(OPENAI_SET_GLOBALS, onSetGlobals as EventListener);\n dataCbs.clear();\n themeCbs.clear();\n },\n };\n}\n","/**\n * Read data baked into the HTML as `<script type=\"application/json\" id=…>`.\n *\n * This is how the mcp-ui embedded-resource path and SSR/standalone deliver the\n * pushed tool output with no round-trip: the server helper escapes the payload\n * and substitutes it into the element (see the Python `SynapseUI.render_html`).\n * A data-free template leaves the marker comment in place, which fails\n * `JSON.parse` and reads back as `null` — exactly the \"no data yet\" state.\n */\nexport function readInlineData<T = unknown>(\n doc: Document | undefined,\n elementId: string,\n): T | null {\n if (!doc) return null;\n const el = doc.getElementById(elementId);\n const text = el?.textContent;\n if (!text) return null;\n try {\n const parsed = JSON.parse(text);\n return (parsed ?? null) as T | null;\n } catch {\n // Unreplaced marker or malformed blob — treat as no data.\n return null;\n }\n}\n\n/**\n * Pull the app payload out of an arbitrary render-data envelope. Hosts wrap the\n * tool output differently (`renderData`, `toolOutput`, `structuredContent`, or\n * the bare object), so unwrap the known envelope keys, else pass the object\n * through. Kept framework-generic — no app-specific keys.\n */\nexport function unwrapRenderData<T = unknown>(payload: unknown): T | null {\n if (payload == null || typeof payload !== \"object\") return null;\n const rec = payload as Record<string, unknown>;\n const nested = rec.renderData;\n const source =\n nested != null && typeof nested === \"object\" ? (nested as Record<string, unknown>) : rec;\n if (source.toolOutput != null) return source.toolOutput as T;\n if (source.structuredContent != null) return source.structuredContent as T;\n // No bare `data` envelope key — too ambiguous with an app's own `data` field.\n return source as T;\n}\n","import { readInlineData } from \"../data.js\";\nimport { coerceMode, preferredMode } from \"../theme.js\";\nimport {\n type ConnectUIOptions,\n type HostAdapter,\n type HostCapabilities,\n HostUnsupportedError,\n SYNAPSE_DATA_ELEMENT_ID,\n type SynapseUITheme,\n} from \"../types.js\";\n\n/**\n * Inline / standalone fallback adapter.\n *\n * No live host bridge — used for SSR, previews, and a plain browser render. Data\n * comes only from the baked-in `<script type=\"application/json\">` blob; there is\n * nothing to push, so `onData` never fires after `start()`. Theme follows the OS\n * color scheme and tracks `prefers-color-scheme` changes. `openLink` opens a new\n * tab; `sendPrompt`/`callTool` have no destination.\n */\nexport function createInlineAdapter(\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n const dataElementId = options.dataElementId ?? SYNAPSE_DATA_ELEMENT_ID;\n\n let currentData: unknown = null;\n let currentTheme: SynapseUITheme = { mode: preferredMode(win), tokens: {} };\n\n const themeCbs = new Set<(t: SynapseUITheme) => void>();\n let destroyed = false;\n let media: MediaQueryList | null = null;\n\n const onSchemeChange = (event: MediaQueryListEvent) => {\n if (destroyed) return;\n const mode = coerceMode(event.matches ? \"dark\" : \"light\", currentTheme.mode);\n if (mode === currentTheme.mode) return;\n currentTheme = { mode, tokens: {} };\n for (const cb of themeCbs) cb(currentTheme);\n };\n\n return {\n host: \"generic\",\n getData: <T>() => currentData as T | null,\n onData() {\n // Static render — no updates after the initial baked-in read.\n return () => {};\n },\n getTheme: () => currentTheme,\n onTheme(cb) {\n themeCbs.add(cb);\n return () => themeCbs.delete(cb);\n },\n async callTool<O>(_name: string): Promise<O> {\n throw new HostUnsupportedError(\"callTool\", \"generic\");\n },\n sendPrompt() {\n // No agent to reach in a standalone render.\n },\n openLink(url: string) {\n win.open(url, \"_blank\", \"noopener,noreferrer\");\n },\n resize() {\n // No host to size for.\n },\n capabilities(): HostCapabilities {\n return { pull: false, sendPrompt: false, openLink: true };\n },\n start() {\n currentData = readInlineData(win.document, dataElementId);\n try {\n media = win.matchMedia?.(\"(prefers-color-scheme: dark)\") ?? null;\n media?.addEventListener?.(\"change\", onSchemeChange);\n } catch {\n media = null;\n }\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n media?.removeEventListener?.(\"change\", onSchemeChange);\n media = null;\n themeCbs.clear();\n },\n };\n}\n","// `detection` and `theme-defaults` are type-only against `@modelcontextprotocol/*`,\n// so importing them keeps this adapter free of the ext-apps runtime (and Zod)\n// that the lean `window.SynapseUI` IIFE deliberately excludes.\nimport { foldFontFaces } from \"../../detection.js\";\nimport { fontFacesKey } from \"../../theme-defaults.js\";\nimport { readInlineData, unwrapRenderData } from \"../data.js\";\nimport { coerceMode, preferredMode } from \"../theme.js\";\nimport {\n type ConnectUIOptions,\n type HostAdapter,\n type HostCapabilities,\n MCP_TOOLS_CALL,\n MCPAPP_HOST_CONTEXT_CHANGED,\n MCPAPP_INITIALIZE,\n MCPAPP_INITIALIZED,\n MCPAPP_MESSAGE,\n MCPAPP_OPEN_LINK,\n MCPAPP_PROTOCOL_VERSION,\n MCPAPP_SIZE_CHANGED,\n MCPAPP_TEARDOWN,\n MCPAPP_TOOL_RESULT,\n MCPUI_LINK,\n MCPUI_PROMPT,\n MCPUI_READY,\n MCPUI_RENDER_DATA,\n MCPUI_SIZE_CHANGE,\n SYNAPSE_DATA_ELEMENT_ID,\n type SynapseUITheme,\n} from \"../types.js\";\n\n/** How long a widget→host request (`callTool`, `openLink`, `sendPrompt`) waits\n * for its response before rejecting, so a silent host never hangs a promise. */\nconst REQUEST_TIMEOUT_MS = 30_000;\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * MCP Apps standard (SEP-1865) adapter — the convergence bridge for Claude\n * Desktop and other MCP Apps hosts.\n *\n * The View iframe is an MCP client speaking JSON-RPC 2.0 to `window.parent`:\n *\n * 1. posts `ui/initialize` and awaits the host context (theme, style variables);\n * 2. posts `ui/notifications/initialized`, then a `size-changed` — a host keeps\n * the frame hidden until it has both the handshake and a size;\n * 3. receives data via `ui/notifications/tool-result` (`params` IS the\n * `CallToolResult`, so data is at `params.structuredContent`) and theme via\n * `ui/notifications/host-context-changed`.\n *\n * Actions go up as requests: `ui/open-link`, `ui/message` (follow-up), and\n * `tools/call` (pull). The legacy mcp-ui `ui-lifecycle-*` messages are sent and\n * accepted alongside so a pre-standard host still renders — a standard host drops\n * the non-JSON-RPC frames, and a legacy host ignores the JSON-RPC ones.\n */\nexport function createMcpAppsAdapter(\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n const dataElementId = options.dataElementId ?? SYNAPSE_DATA_ELEMENT_ID;\n const autoResize = options.autoResize !== false;\n\n let currentData: unknown = null;\n let currentTheme: SynapseUITheme = { mode: preferredMode(win), tokens: {} };\n\n const dataCbs = new Set<(d: unknown) => void>();\n const themeCbs = new Set<(t: SynapseUITheme) => void>();\n let destroyed = false;\n // Set once `ui/initialize` resolves — proof we're on an MCP Apps standard host,\n // which lets us stop mirroring actions to the legacy shim.\n let standardConfirmed = false;\n let nextId = 1;\n const pending = new Map<number, PendingRequest>();\n let lastReportedHeight = -1;\n let resizeObserver: ResizeObserver | null = null;\n let onWindowResize: (() => void) | null = null;\n\n const parent = () => win.parent ?? win;\n\n function post(message: Record<string, unknown>): void {\n parent().postMessage(message, \"*\");\n }\n\n // The legacy mcp-ui frames (render-data in, size/link/prompt out) are a\n // transitional shim for the one host that still speaks it — the NimbleBrain\n // runtime, which shares this adapter via the `nimblebrain` kind until the P3\n // `nimblebrain` adapter lands and this shim is removed. Once the handshake\n // confirms a standard host we stop mirroring, so a host that understood both\n // dialects never acts on an action twice.\n function postLegacy(message: Record<string, unknown>): void {\n if (standardConfirmed) return;\n post(message);\n }\n\n function notify(method: string, params?: Record<string, unknown>): void {\n post({ jsonrpc: \"2.0\", method, params: params ?? {} });\n }\n\n function request<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> {\n const id = nextId++;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject(new Error(`\"${method}\" timed out`));\n }, REQUEST_TIMEOUT_MS);\n pending.set(id, { resolve: resolve as (v: unknown) => void, reject, timer });\n post({ jsonrpc: \"2.0\", id, method, params: params ?? {} });\n });\n }\n\n function emitData(next: unknown): void {\n if (next == null) return;\n currentData = next;\n for (const cb of dataCbs) cb(next);\n }\n\n /** Merge a full or partial host context into the resolved theme (mode, tokens,\n * font faces). Fonts ride the `synapse/fontFaces` extension: absent means\n * unchanged, an explicit (possibly empty) list replaces. ChatGPT supplies\n * only a mode string, so this is the adapter where host typography arrives. */\n function applyHostContext(ctx: Record<string, unknown> | null | undefined): void {\n if (!ctx || typeof ctx !== \"object\") return;\n let { mode, tokens, fontFaces } = currentTheme;\n let changed = false;\n if (ctx.theme != null) {\n const next = coerceMode(ctx.theme, mode);\n if (next !== mode) {\n mode = next;\n changed = true;\n }\n }\n const styles = ctx.styles as { variables?: Record<string, string> } | undefined;\n if (styles?.variables && typeof styles.variables === \"object\") {\n tokens = { ...tokens, ...styles.variables };\n changed = true;\n }\n const nextFaces = foldFontFaces(fontFaces, ctx);\n if (fontFacesKey(nextFaces) !== fontFacesKey(fontFaces)) {\n fontFaces = nextFaces;\n changed = true;\n }\n if (changed) {\n currentTheme = { mode, tokens, ...(fontFaces ? { fontFaces } : {}) };\n for (const cb of themeCbs) cb(currentTheme);\n }\n }\n\n function reportSize(height?: number): void {\n if (destroyed) return;\n const h = typeof height === \"number\" ? height : Math.ceil(win.document.body.scrollHeight);\n if (h === lastReportedHeight) return;\n lastReportedHeight = h;\n notify(MCPAPP_SIZE_CHANGED, { height: h });\n postLegacy({ type: MCPUI_SIZE_CHANGE, payload: { height: h } });\n }\n\n function handleResponse(d: Record<string, unknown>): void {\n // Normalize the echoed id: JSON-RPC requires a same-type echo, but a lax host\n // that returned \"1\" for 1 would otherwise miss the numeric-keyed pending map.\n const id = Number(d.id);\n const p = pending.get(id);\n if (!p) return;\n pending.delete(id);\n clearTimeout(p.timer);\n if (d.error != null) {\n const err = d.error as { message?: string };\n p.reject(new Error(err.message ?? \"request failed\"));\n } else {\n p.resolve(d.result);\n }\n }\n\n function handleNotification(method: string, params: Record<string, unknown>): void {\n if (method === MCPAPP_TOOL_RESULT) {\n // `params` is the CallToolResult: the render data lives at structuredContent.\n const structured = params.structuredContent;\n emitData(structured != null ? structured : unwrapRenderData(params));\n } else if (method === MCPAPP_HOST_CONTEXT_CHANGED) {\n applyHostContext(params);\n }\n }\n\n function handleRequest(d: Record<string, unknown>): void {\n // The one host→view request we honor: acknowledge teardown so the host can\n // dispose the frame cleanly.\n if (d.method === MCPAPP_TEARDOWN) {\n post({ jsonrpc: \"2.0\", id: d.id, result: {} });\n }\n }\n\n /** Legacy mcp-ui render-data (non-JSON-RPC) — a pre-standard host's data path. */\n function handleLegacy(d: Record<string, unknown>): void {\n if (d.type === MCPUI_RENDER_DATA || d.type === \"renderData\") {\n const { theme, ...rest } = (d.payload ?? {}) as Record<string, unknown>;\n if (theme != null) applyHostContext({ theme });\n if (Object.keys(rest).length > 0) emitData(unwrapRenderData(rest));\n }\n }\n\n const onMessage = (event: MessageEvent) => {\n if (destroyed) return;\n // Accept only frames from the host window when the browser sets a source.\n if (event.source && event.source !== parent()) return;\n const d = event.data as Record<string, unknown> | null | undefined;\n if (!d || typeof d !== \"object\") return;\n if (d.jsonrpc !== \"2.0\") {\n handleLegacy(d);\n return;\n }\n if (d.id != null && (\"result\" in d || \"error\" in d)) {\n handleResponse(d);\n } else if (typeof d.method === \"string\") {\n if (d.id != null) handleRequest(d);\n else handleNotification(d.method, (d.params ?? {}) as Record<string, unknown>);\n }\n };\n\n function setupResize(): void {\n onWindowResize = () => reportSize();\n win.addEventListener(\"resize\", onWindowResize);\n if (typeof win.ResizeObserver !== \"undefined\") {\n resizeObserver = new win.ResizeObserver(() => reportSize());\n resizeObserver.observe(win.document.body);\n }\n }\n\n return {\n host: \"claude\",\n getData: <T>() => currentData as T | null,\n onData(cb) {\n dataCbs.add(cb as (d: unknown) => void);\n return () => dataCbs.delete(cb as (d: unknown) => void);\n },\n getTheme: () => currentTheme,\n onTheme(cb) {\n themeCbs.add(cb);\n return () => themeCbs.delete(cb);\n },\n async callTool<O>(name: string, args?: Record<string, unknown>): Promise<O> {\n return (await request(MCP_TOOLS_CALL, { name, arguments: args ?? {} })) as O;\n },\n sendPrompt(text: string) {\n // Standard follow-up (ack ignored); legacy mirror for a pre-standard host.\n void request(MCPAPP_MESSAGE, { role: \"user\", content: [{ type: \"text\", text }] }).catch(\n () => {},\n );\n postLegacy({ type: MCPUI_PROMPT, payload: { prompt: text } });\n },\n openLink(url: string) {\n void request(MCPAPP_OPEN_LINK, { url }).catch(() => {});\n postLegacy({ type: MCPUI_LINK, payload: { url } });\n },\n resize(height?: number) {\n reportSize(height);\n },\n capabilities(): HostCapabilities {\n // The MCP Apps standard host answers `tools/call` over this bridge, so pull\n // is advertised. A legacy-only host that shares this adapter (nimblebrain,\n // pre-P3) does not, so there callTool rejects only after REQUEST_TIMEOUT_MS\n // rather than failing fast.\n return { pull: true, sendPrompt: true, openLink: true };\n },\n start() {\n win.addEventListener(\"message\", onMessage as EventListener);\n\n // Baked-in data → available on first paint, before the handshake resolves.\n currentData = readInlineData(win.document, dataElementId);\n\n if (autoResize) setupResize();\n\n // Legacy mcp-ui ready (a standard host drops this non-JSON-RPC frame).\n post({ type: MCPUI_READY });\n\n // MCP Apps standard handshake. A legacy-only host never answers, so the\n // legacy render-data path (handleLegacy) still feeds the widget.\n request<{ hostContext?: Record<string, unknown> }>(MCPAPP_INITIALIZE, {\n appInfo: { name: options.name ?? \"synapse-ui\", version: options.version ?? \"0.0.0\" },\n appCapabilities: { availableDisplayModes: [\"inline\"] },\n protocolVersion: MCPAPP_PROTOCOL_VERSION,\n })\n .then((result) => {\n if (destroyed) return;\n standardConfirmed = true;\n applyHostContext(result?.hostContext);\n notify(MCPAPP_INITIALIZED, {});\n // A host keeps the frame hidden until it gets a size after init — force one.\n lastReportedHeight = -1;\n reportSize();\n })\n .catch(() => {\n // Not an MCP Apps host (or it was slow) — the legacy path covers data.\n });\n\n reportSize();\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n win.removeEventListener(\"message\", onMessage as EventListener);\n if (onWindowResize) win.removeEventListener(\"resize\", onWindowResize);\n onWindowResize = null;\n resizeObserver?.disconnect();\n resizeObserver = null;\n for (const p of pending.values()) {\n clearTimeout(p.timer);\n p.reject(new Error(\"adapter destroyed\"));\n }\n pending.clear();\n dataCbs.clear();\n themeCbs.clear();\n },\n };\n}\n","import { createChatGPTAdapter } from \"./adapters/chatgpt.js\";\nimport { createInlineAdapter } from \"./adapters/inline.js\";\nimport { createMcpAppsAdapter } from \"./adapters/mcpapps.js\";\nimport type { ConnectUIOptions, HostAdapter, HostKind } from \"./types.js\";\n\n/**\n * Minimal window shape the detector inspects. Kept structural so detection is\n * unit-testable with a plain object — no full `Window` fake required.\n */\ninterface DetectableWindow {\n openai?: unknown;\n parent?: unknown;\n self?: unknown;\n}\n\n/**\n * Feature-detect the host from the browsing context.\n *\n * - `window.openai` present → **chatgpt** (OpenAI Apps SDK).\n * - otherwise, a nested browsing context (`parent !== self`, or a cross-origin\n * access that throws) → **claude** (MCP Apps standard postMessage host).\n * - top-level document → **generic** (inline / standalone).\n *\n * Returns the host *kind*; `chooseAdapter` maps it to a concrete adapter and\n * honors an explicit `options.host` override for previews/SSR/tests.\n */\nexport function detectHostKind(win: DetectableWindow): HostKind {\n if (win.openai != null) return \"chatgpt\";\n try {\n if (win.parent != null && win.parent !== win) return \"claude\";\n } catch {\n // Cross-origin parent access throws → we are framed by another origin.\n return \"claude\";\n }\n return \"generic\";\n}\n\n/** Build the adapter for an explicit host kind (`options.host` or a detected one). */\nexport function adapterForKind(\n kind: HostKind,\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n switch (kind) {\n case \"chatgpt\":\n return createChatGPTAdapter(win, options);\n case \"claude\":\n case \"nimblebrain\":\n // Both speak the MCP Apps standard (SEP-1865). `nimblebrain` shares it for\n // now; a dedicated adapter over the `synapse/*` extension lands in P3.\n return createMcpAppsAdapter(win, options);\n default:\n return createInlineAdapter(win, options);\n }\n}\n\n/** Select and construct the host adapter, honoring `options.host` when set. */\nexport function selectAdapter(\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n const kind = options.host ?? detectHostKind(win as unknown as DetectableWindow);\n return adapterForKind(kind, win, options);\n}\n","import { selectAdapter } from \"./detect.js\";\nimport { applyHostTheme } from \"./theme.js\";\nimport type { ConnectUIOptions, SynapseUIClient } from \"./types.js\";\n\n/**\n * Connect a Synapse-authored component to whatever host it renders in — ChatGPT\n * (OpenAI Apps SDK), Claude (MCP Apps standard), or a plain/standalone page — behind one\n * push-first API. Feature-detects the host, selects an adapter, and applies the\n * host theme to the DOM before returning.\n *\n * Synchronous: `data()` is populated from baked-in data (where present) on\n * return, and pushed updates arrive via `onData`. Bind the result to `synapse`:\n *\n * ```ts\n * const synapse = connectUI({ name: \"my-widget\", version: \"1.0.0\" });\n * synapse.onData(render); // future pushes/updates\n * render(synapse.data()); // current value (null → empty state)\n * ```\n */\nexport function connectUI(options: ConnectUIOptions = {}): SynapseUIClient {\n const win = options.window ?? (globalThis as unknown as Window & typeof globalThis);\n const adapter = selectAdapter(win, options);\n\n // The client owns theme application: adapters resolve mode/tokens; this puts\n // them on the DOM (data-theme + CSS vars) so apps never wire theme by hand.\n applyHostTheme(adapter.getTheme());\n const unsubTheme = adapter.onTheme(applyHostTheme);\n\n adapter.start();\n\n let destroyed = false;\n\n return {\n data: <T>() => adapter.getData<T>(),\n onData: <T>(cb: (data: T) => void) => adapter.onData<T>(cb),\n theme: () => adapter.getTheme(),\n onTheme: (cb) => adapter.onTheme(cb),\n callTool: <O>(name: string, args?: Record<string, unknown>) => adapter.callTool<O>(name, args),\n sendPrompt: (text: string) => adapter.sendPrompt(text),\n openLink: (url: string) => adapter.openLink(url),\n resize: (height?: number) => adapter.resize(height),\n capabilities: () => adapter.capabilities(),\n host: () => adapter.host,\n destroy() {\n if (destroyed) return;\n destroyed = true;\n unsubTheme();\n adapter.destroy();\n },\n };\n}\n"]}