@ggui-ai/mcp-server 0.1.0-rc.3 → 0.2.0-alpha.1
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.
- package/README.md +1 -1
- package/dist/build-mcp.d.ts +8 -8
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +53 -12
- package/dist/console-auth.d.ts +7 -7
- package/dist/console-auth.d.ts.map +1 -1
- package/dist/console-auth.js +4 -4
- package/dist/console-cache.d.ts +2 -2
- package/dist/console-cache.d.ts.map +1 -1
- package/dist/console-cache.js +10 -10
- package/dist/console-headers.d.ts +2 -2
- package/dist/console-headers.js +2 -2
- package/dist/console-payloads.d.ts +3 -3
- package/dist/console-payloads.d.ts.map +1 -1
- package/dist/console-payloads.js +10 -10
- package/dist/console-timeline.d.ts +12 -30
- package/dist/console-timeline.d.ts.map +1 -1
- package/dist/console-timeline.js +44 -45
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/instructions-presets.d.ts +1 -1
- package/dist/instructions-presets.d.ts.map +1 -1
- package/dist/instructions-presets.js +40 -22
- package/dist/llm-backed-negotiator.d.ts +6 -6
- package/dist/llm-backed-negotiator.d.ts.map +1 -1
- package/dist/llm-backed-negotiator.js +92 -91
- package/dist/mcp-apps-inbound.d.ts +3 -3
- package/dist/mcp-apps-inbound.d.ts.map +1 -1
- package/dist/mcp-apps-inbound.js +28 -23
- package/dist/mcp-apps-outbound.d.ts +136 -121
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +384 -414
- package/dist/mcp-mounts.d.ts +21 -20
- package/dist/mcp-mounts.d.ts.map +1 -1
- package/dist/mcp-mounts.js +25 -29
- package/dist/{session-channel.d.ts → render-channel.d.ts} +145 -102
- package/dist/render-channel.d.ts.map +1 -0
- package/dist/{session-channel.js → render-channel.js} +481 -462
- package/dist/schema-compat.d.ts +11 -11
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +6 -6
- package/dist/server.d.ts +224 -219
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1498 -1963
- package/dist/storage.d.ts +7 -7
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +12 -12
- package/package.json +12 -11
- package/dist/render-gate.d.ts +0 -87
- package/dist/render-gate.d.ts.map +0 -1
- package/dist/render-gate.js +0 -77
- package/dist/render-rate-limit.d.ts +0 -59
- package/dist/render-rate-limit.d.ts.map +0 -1
- package/dist/render-rate-limit.js +0 -73
- package/dist/render-signing.d.ts +0 -98
- package/dist/render-signing.d.ts.map +0 -1
- package/dist/render-signing.js +0 -113
- package/dist/session-channel.d.ts.map +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Apps outbound wiring — the server-side half of the
|
|
3
|
-
* `
|
|
3
|
+
* `ggui_render -> ui://ggui/render -> iframe -> live channel` delivery path.
|
|
4
4
|
*
|
|
5
5
|
* Responsibilities of this module (and nothing else):
|
|
6
6
|
*
|
|
7
7
|
* 1. Advertise the `io.modelcontextprotocol/ui` capability on every
|
|
8
8
|
* fresh `McpServer` instance so MCP Apps hosts know the server
|
|
9
9
|
* speaks the UI extension.
|
|
10
|
-
* 2. Register `ui://ggui/
|
|
10
|
+
* 2. Register `ui://ggui/render` as a static resource servable via
|
|
11
11
|
* MCP `resources/read`. The resource body is the thin-shell HTML
|
|
12
12
|
* hosts sandbox-render when they see `_meta.ui.resourceUri` on a
|
|
13
|
-
* `
|
|
13
|
+
* `ggui_render` result.
|
|
14
14
|
*
|
|
15
15
|
* Boundary discipline:
|
|
16
16
|
*
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
* build target — per the design lock, the shell is served by the
|
|
32
32
|
* same `@ggui-ai/mcp-server` instance that mints the bootstrap.
|
|
33
33
|
*/
|
|
34
|
-
import {
|
|
35
|
-
import
|
|
36
|
-
import { type
|
|
37
|
-
export declare const GGUI_SESSION_SHELL_HTML = "<!doctype html>\n<html lang=\"en\" style=\"height:100%\"><head><meta charset=\"utf-8\"><title>ggui session</title></head>\n<body style=\"margin:0;height:100%;min-height:480px\"><div id=\"ggui-root\" data-ggui-shell=\"thin\" style=\"height:100%;min-height:480px\"></div>\n<script>\n(function(){'use strict';\n// MCP Apps shell. Speaks the canonical postMessage protocol from\n// @modelcontextprotocol/ext-apps:\n// 1. iframe -> host: ui/initialize\n// 2. iframe -> host: ui/notifications/initialized\n// 3. host -> iframe: ui/notifications/tool-result (per CallToolResult)\n// On tool-result, derive base URL + shortCode from structuredContent,\n// fetch JSON bootstrap (componentCode + ids + runtimeUrl), set\n// window.__GGUI_BOOTSTRAP__, fetch runtime as blob, inject as script.\n// Runtime auto-mounts inline. No nested iframe (claudemcpcontent.com\n// CSP frame-src forbids cross-origin frames). State machine matches\n// buildSelfContainedShell so the same runtime mounts both paths.\nvar rpcId=1,pending={};\nvar rootEl=document.getElementById('ggui-root');\nrootEl.style.cssText='display:flex;flex-direction:column;height:100%;min-height:300px;margin:0';\nvar mounted=false;\nfunction setOverlay(text){\n if(mounted)return;\n rootEl.innerHTML='<div style=\"font:14px system-ui,sans-serif;padding:24px;color:#666\">'+text+'</div>';\n}\nfunction postNotification(method,params){\n try{window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*');}catch(e){}\n}\nfunction postRpc(method,params){\n return new Promise(function(res,rej){\n var id=rpcId++;pending[id]={res:res,rej:rej};\n try{window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');}\n catch(e){delete pending[id];rej(e);}\n });\n}\nfunction readToolResult(p){\n if(!p)return null;\n var sc=p.structuredContent;\n if(sc&&typeof sc.url==='string'&&typeof sc.shortCode==='string'){\n return {url:sc.url,shortCode:sc.shortCode};\n }\n if(Array.isArray(p.content)){\n for(var i=0;i<p.content.length;i++){\n var c=p.content[i];\n if(c&&c.type==='text'&&typeof c.text==='string'){\n try{var j=JSON.parse(c.text);\n if(j&&typeof j.url==='string'&&typeof j.shortCode==='string'){\n return {url:j.url,shortCode:j.shortCode};\n }\n }catch(e){}\n }\n }\n }\n return null;\n}\nfunction deriveBase(url){\n try{var u=new URL(url);return u.origin;}catch(e){return null;}\n}\nfunction postBootstrapFailed(reason,message){\n // Surface every shell-layer bootstrap-failure path as a typed\n // RendererBootFailedMessage envelope so hosts pinning the C9\n // error pane (McpAppIframe onError, IframeErrorPane) see the\n // failure instead of staring at the inert overlay until the test\n // times out. Reason codes match BootstrapFailureReason.\n try{window.parent.postMessage({type:'ggui:bootstrap-failed',reason:reason,message:message},'*');}catch(e){}\n}\nasync function mountFromBootstrap(bootstrap){\n if(mounted)return;\n // runtimeUrl is the only load-bearing field at the shell layer \u2014\n // it tells us which iframe-runtime bundle to fetch. componentCode\n // is OPTIONAL: present in self-contained-mode bootstraps (the\n // runtime renders the inlined code without WS), absent in\n // GguiBootstrapMeta-shaped bootstraps (the runtime opens the WS\n // subscription via wsUrl+token to receive the stack). Either is\n // valid; the runtime decides at boot time. Rejecting on missing\n // componentCode silently breaks every Path B GguiBootstrapMeta\n // delivery, which is the whole point of the inline-bootstrap path.\n if(!bootstrap||typeof bootstrap.runtimeUrl!=='string'){\n setOverlay('Bootstrap payload malformed.');\n postBootstrapFailed('BOOTSTRAP_MALFORMED','Bootstrap payload malformed.');\n return;\n }\n setOverlay('Loading UI\u2026');\n window.__GGUI_BOOTSTRAP__=bootstrap;\n // Load the runtime bundle via a direct cross-origin script tag\n // (governed by CSP script-src) instead of fetch + Blob (governed by\n // CSP connect-src). claude.ai's claudemcpcontent.com iframe CSP\n // forbids cross-origin connect-src so fetch throws TypeError\n // 'Failed to fetch', but allows cross-origin script-src when the\n // bundle responds with the right CORS headers (the iframe-runtime\n // mount sets them). The self-contained shell already uses this\n // pattern; legacy postMessage shell now matches it.\n try{\n var s=document.createElement('script');\n s.type='module';\n // crossorigin=anonymous opts into CORS-mode error reporting.\n // Without it, cross-origin script tags get a sanitized\n // \"script error\" with no details, masking the real cause\n // (CSP block, CORS reject, module-evaluation throw). With it,\n // the error event surfaces the actual message in the iframe\n // console -- the bundle ships ACAO=* so credentialed mode is\n // unnecessary.\n s.crossOrigin='anonymous';\n s.src=bootstrap.runtimeUrl;\n s.onload=function(){mounted=true;};\n s.onerror=function(e){\n var msg='Runtime bundle failed to load: '+(e&&e.message||'script error');\n setOverlay(msg);\n postBootstrapFailed('BUNDLE_FETCH_FAILED',msg);\n };\n rootEl.innerHTML='';\n document.body.appendChild(s);\n }catch(e){\n var msg='Runtime bundle failed to load: '+(e&&e.message||e);\n setOverlay(msg);\n postBootstrapFailed('BUNDLE_FETCH_FAILED',msg);\n }\n}\nasync function mount(toolResult){\n if(mounted)return;\n var base=deriveBase(toolResult.url);\n if(!base){setOverlay('Invalid URL in tool result.');return;}\n setOverlay('Loading UI\u2026');\n var bootstrap;\n try{\n var bRes=await fetch(base+'/api/bootstrap/'+encodeURIComponent(toolResult.shortCode),{\n cache:'no-store',\n headers:{accept:'application/json'},\n });\n if(!bRes.ok){setOverlay('Bootstrap fetch failed: HTTP '+bRes.status);return;}\n bootstrap=await bRes.json();\n }catch(e){setOverlay('Bootstrap fetch error: '+(e&&e.message||e));return;}\n return mountFromBootstrap(bootstrap);\n}\nfunction readBootstrapFromInitResult(result){\n if(!result||typeof result!=='object')return null;\n var toolOutput=result.toolOutput;\n if(!toolOutput||typeof toolOutput!=='object')return null;\n var meta=toolOutput._meta;\n if(!meta||typeof meta!=='object')return null;\n var ggui=meta.ggui;\n if(!ggui||typeof ggui!=='object')return null;\n var b=ggui.bootstrap;\n if(!b||typeof b!=='object')return null;\n // Same loosening as mountFromBootstrap: only runtimeUrl is required\n // here. GguiBootstrapMeta forwarded by first-party McpAppIframe\n // hosts omits componentCode by design (the runtime fetches the\n // stack via\n // wsUrl+token). Requiring componentCode at the shell layer turned\n // every Path B inline-bootstrap delivery into a silent reject.\n if(typeof b.runtimeUrl!=='string')return null;\n return b;\n}\nfunction hasGguiMetaPlaceholder(result){\n // Detect the protocol-violation case where the host signaled\n // \"I tried to deliver bootstrap\" (toolOutput._meta.ggui exists)\n // but the bootstrap field itself is absent or malformed. In that\n // shape, we fail-fast with BOOTSTRAP_META_MISSING rather than\n // sitting in the Path-A waiting state forever.\n if(!result||typeof result!=='object')return false;\n var toolOutput=result.toolOutput;\n if(!toolOutput||typeof toolOutput!=='object')return false;\n var meta=toolOutput._meta;\n if(!meta||typeof meta!=='object')return false;\n var ggui=meta.ggui;\n return !!ggui&&typeof ggui==='object';\n}\nfunction readBootstrapFromCallToolResult(params){\n // MCP Apps spec (specification/2026-01-26/apps.mdx:1145-1155):\n // ui/notifications/tool-result\n // params: CallToolResult // Standard MCP type\n // So params IS the CallToolResult and _meta lives at the top\n // level (NOT under params.toolOutput, which is where the\n // first-party McpAppIframe convention wraps it). Spec-compliant\n // hosts (Claude Desktop, claude.ai Connector, Claude Code) deliver\n // bootstrap material here.\n if(!params||typeof params!=='object')return null;\n var meta=params._meta;\n if(!meta||typeof meta!=='object')return null;\n var ggui=meta.ggui;\n if(!ggui||typeof ggui!=='object')return null;\n var b=ggui.bootstrap;\n if(!b||typeof b!=='object')return null;\n // Same loosening as readBootstrapFromInitResult / mountFromBootstrap:\n // only runtimeUrl is required at the shell layer. componentCode is\n // optional (absent for GguiBootstrapMeta-shaped bootstraps that\n // open WS via wsUrl+token).\n if(typeof b.runtimeUrl!=='string')return null;\n return b;\n}\nwindow.addEventListener('message',function(ev){\n var m=ev&&ev.data;\n if(!m||m.jsonrpc!=='2.0')return;\n if(m.id!=null&&pending[m.id]){\n var p=pending[m.id];delete pending[m.id];\n if(m.error)p.rej(m.error);else p.res(m.result);\n return;\n }\n if(m.method==='ui/notifications/tool-result'){\n // Spec-compliant hosts: m.params IS the CallToolResult; _meta is\n // at the top level. Try this FIRST so Claude Desktop / claude.ai\n // Connector / Claude Code land here.\n var specB=readBootstrapFromCallToolResult(m.params);\n if(specB){mountFromBootstrap(specB);return;}\n // First-party McpAppIframe convention: bootstrap nested under\n // params.toolOutput._meta. First-party hosts (Studio, Portal,\n // console) use this shape for both init-response and post-init\n // notification.\n var bb=readBootstrapFromInitResult(m.params);\n if(bb){mountFromBootstrap(bb);return;}\n // Path A (last-resort fallback): host posts structuredContent.\n // {url, shortCode}; shell fetches /api/bootstrap/<shortCode>\n // over HTTP. The OSS server doesn't mount that endpoint, so this\n // path is effectively dead unless a hosted operator wires it.\n var tr=readToolResult(m.params);\n if(tr)mount(tr);\n }\n});\nsetOverlay('Initializing\u2026');\nvar initTimer=setTimeout(function(){\n setOverlay('Host did not respond to ui/initialize within 3s.');\n},3000);\npostRpc('ui/initialize',{\n appCapabilities:{},\n appInfo:{name:'ggui-session',version:'1.0.0'},\n protocolVersion:'2026-01-26'\n}).then(function(result){\n clearTimeout(initTimer);\n postNotification('ui/notifications/initialized',{});\n // Path B: bootstrap inline in ui/initialize result. Hosts using\n // <McpAppIframe>'s first-party dispatch deliver bootstrap meta here\n // and never send a separate ui/notifications/tool-result.\n var b=readBootstrapFromInitResult(result);\n if(b){mountFromBootstrap(b);return;}\n // Protocol-violation surface: host signalled an attempt\n // (toolOutput._meta.ggui exists) but bootstrap is missing or\n // malformed. Fail fast so hosts pinning a typed error envelope\n // don't sit on the Path-A waiting overlay forever. Only fires\n // when the meta placeholder is present \u2014 a fully absent _meta.ggui\n // still falls through to Path A waiting.\n if(hasGguiMetaPlaceholder(result)){\n var msg='ui/initialize result missing _meta.ggui.bootstrap or runtimeUrl';\n setOverlay(msg);\n postBootstrapFailed('BOOTSTRAP_META_MISSING',msg);\n return;\n }\n // Path A: wait for the host to send ui/notifications/tool-result\n // with structuredContent.{url, shortCode}. MCP Apps hosts that don't\n // implement the reading-B inline-bootstrap convention land here.\n setOverlay('Waiting for tool result\u2026');\n}).catch(function(e){\n clearTimeout(initTimer);\n setOverlay('ui/initialize failed: '+(e&&e.message||JSON.stringify(e)));\n});\n})();\n</script></body></html>";
|
|
34
|
+
import type { RenderStore, VectorStore } from "@ggui-ai/mcp-server-core";
|
|
35
|
+
import { type McpAppAiGguiRenderMeta } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
36
|
+
import { type McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
37
|
+
export declare const GGUI_RENDER_SHELL_HTML = "<!doctype html>\n<html lang=\"en\" style=\"height:100%\"><head><meta charset=\"utf-8\"><title>ggui render</title></head>\n<body style=\"margin:0;height:100%;min-height:480px\"><div id=\"ggui-root\" data-ggui-shell=\"thin\" style=\"height:100%;min-height:480px\"></div>\n<script>\n(function(){'use strict';\n// MCP Apps shell. Speaks the canonical postMessage protocol from\n// @modelcontextprotocol/ext-apps:\n// 1. iframe -> host: ui/initialize\n// 2. iframe -> host: ui/notifications/initialized\n// 3. host -> iframe: ui/notifications/tool-result (per CallToolResult)\n// On tool-result, read the slice envelope from _meta (spec-canonical\n// CallToolResult _meta at the top level, or the first-party\n// params.toolOutput._meta shape), set window.__GGUI_META__ to the\n// envelope, fetch runtime as blob, inject as script.\n// Runtime auto-mounts inline. No nested iframe (claudemcpcontent.com\n// CSP frame-src forbids cross-origin frames). State machine matches\n// buildSelfContainedShell so the same runtime mounts both paths.\n//\n// R5 (2026-05-26): the historic /r/<shortCode> HTTP fallback was\n// removed along with the bearer-by-obscurity model -- hosts that strip\n// _meta no longer have a recovery path here. Spec-canonical hosts\n// deliver _meta inline and are unaffected.\n//\n// Phase B (2026-05-27): the historic two-slice envelope\n// (`ai.ggui/session` + `ai.ggui/stack-item`) collapsed to ONE flat\n// `ai.ggui/render` slice. runtimeUrl now lives directly on the\n// render slice; renderId is the canonical identity.\nvar rpcId=1,pending={};\nvar rootEl=document.getElementById('ggui-root');\nrootEl.style.cssText='display:flex;flex-direction:column;height:100%;min-height:300px;margin:0';\nvar mounted=false;\nfunction setOverlay(text){\n if(mounted)return;\n rootEl.innerHTML='<div style=\"font:14px system-ui,sans-serif;padding:24px;color:#666\">'+text+'</div>';\n}\nfunction postNotification(method,params){\n try{window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*');}catch(e){}\n}\nfunction postRpc(method,params){\n return new Promise(function(res,rej){\n var id=rpcId++;pending[id]={res:res,rej:rej};\n try{window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');}\n catch(e){delete pending[id];rej(e);}\n });\n}\nfunction postBootstrapFailed(reason,message){\n // Surface every shell-layer bootstrap-failure path as a typed\n // RendererBootFailedMessage envelope so hosts pinning the C9\n // error pane (McpAppIframe onError, IframeErrorPane) see the\n // failure instead of staring at the inert overlay until the test\n // times out. Reason codes match BootstrapFailureReason.\n try{window.parent.postMessage({type:'ggui:bootstrap-failed',reason:reason,message:message},'*');}catch(e){}\n}\nasync function mountFromMeta(envelope){\n if(mounted)return;\n // Slice envelope shape (Phase B): { \"ai.ggui/render\": { renderId,\n // appId, runtimeUrl, ... } }. runtimeUrl on the render slice is\n // the only load-bearing field at the shell layer \u2014 it tells us\n // which iframe-runtime bundle to fetch. Everything else is\n // optional; the runtime decides at boot time based on the meta\n // it reads from window.__GGUI_META__.\n var renderSlice=envelope&&envelope['ai.ggui/render'];\n var runtimeUrl=renderSlice&&renderSlice.runtimeUrl;\n if(!envelope||typeof runtimeUrl!=='string'){\n setOverlay('Bootstrap payload malformed.');\n postBootstrapFailed('BOOTSTRAP_MALFORMED','Bootstrap payload malformed.');\n return;\n }\n setOverlay('Loading UI\u2026');\n window.__GGUI_META__=envelope;\n // Load the runtime bundle via a direct cross-origin script tag\n // (governed by CSP script-src) instead of fetch + Blob (governed by\n // CSP connect-src). claude.ai's claudemcpcontent.com iframe CSP\n // forbids cross-origin connect-src so fetch throws TypeError\n // 'Failed to fetch', but allows cross-origin script-src when the\n // bundle responds with the right CORS headers (the iframe-runtime\n // mount sets them). The self-contained shell already uses this\n // pattern; legacy postMessage shell now matches it.\n try{\n var s=document.createElement('script');\n s.type='module';\n // crossorigin=anonymous opts into CORS-mode error reporting.\n // Without it, cross-origin script tags get a sanitized\n // \"script error\" with no details, masking the real cause\n // (CSP block, CORS reject, module-evaluation throw). With it,\n // the error event surfaces the actual message in the iframe\n // console -- the bundle ships ACAO=* so credentialed mode is\n // unnecessary.\n s.crossOrigin='anonymous';\n s.src=runtimeUrl;\n s.onload=function(){mounted=true;};\n s.onerror=function(e){\n var msg='Runtime bundle failed to load: '+(e&&e.message||'script error');\n setOverlay(msg);\n postBootstrapFailed('BUNDLE_FETCH_FAILED',msg);\n };\n rootEl.innerHTML='';\n document.body.appendChild(s);\n }catch(e){\n var msg='Runtime bundle failed to load: '+(e&&e.message||e);\n setOverlay(msg);\n postBootstrapFailed('BUNDLE_FETCH_FAILED',msg);\n }\n}\nfunction readMetaFromInitResult(result){\n if(!result||typeof result!=='object')return null;\n var toolOutput=result.toolOutput;\n if(!toolOutput||typeof toolOutput!=='object')return null;\n var meta=toolOutput._meta;\n if(!meta||typeof meta!=='object')return null;\n // Single slice-envelope key (Phase B: ai.ggui/render). Only the\n // render slice's runtimeUrl is load-bearing at the shell layer;\n // the runtime reads everything else off window.__GGUI_META__\n // after we set it.\n var renderSlice=meta['ai.ggui/render'];\n if(!renderSlice||typeof renderSlice!=='object')return null;\n if(typeof renderSlice.runtimeUrl!=='string')return null;\n return meta;\n}\nfunction hasAiGguiMetaPlaceholder(result){\n // Detect the protocol-violation case where the host signaled\n // \"I tried to deliver meta\" (toolOutput._meta carries an\n // ai.ggui/render key) but it's malformed. Fail-fast with\n // BOOTSTRAP_META_MISSING rather than waiting forever.\n if(!result||typeof result!=='object')return false;\n var toolOutput=result.toolOutput;\n if(!toolOutput||typeof toolOutput!=='object')return false;\n var meta=toolOutput._meta;\n if(!meta||typeof meta!=='object')return false;\n return 'ai.ggui/render' in meta;\n}\nfunction readMetaFromCallToolResult(params){\n // MCP Apps spec (specification/2026-01-26/apps.mdx:1145-1155):\n // ui/notifications/tool-result\n // params: CallToolResult // Standard MCP type\n // So params IS the CallToolResult and _meta lives at the top\n // level (NOT under params.toolOutput, which is where the\n // first-party McpAppIframe convention wraps it). Spec-compliant\n // hosts (Claude Desktop, claude.ai Connector, Claude Code) deliver\n // slice-envelope material here.\n if(!params||typeof params!=='object')return null;\n var meta=params._meta;\n if(!meta||typeof meta!=='object')return null;\n var renderSlice=meta['ai.ggui/render'];\n if(!renderSlice||typeof renderSlice!=='object')return null;\n if(typeof renderSlice.runtimeUrl!=='string')return null;\n return meta;\n}\nwindow.addEventListener('message',function(ev){\n var m=ev&&ev.data;\n if(!m||m.jsonrpc!=='2.0')return;\n if(m.id!=null&&pending[m.id]){\n var p=pending[m.id];delete pending[m.id];\n if(m.error)p.rej(m.error);else p.res(m.result);\n return;\n }\n if(m.method==='ui/notifications/tool-result'){\n // Spec-compliant hosts: m.params IS the CallToolResult; _meta is\n // at the top level. Try this FIRST so Claude Desktop / claude.ai\n // Connector / Claude Code land here.\n var specB=readMetaFromCallToolResult(m.params);\n if(specB){mountFromMeta(specB);return;}\n // First-party McpAppIframe convention: slice envelope nested under\n // params.toolOutput._meta. First-party hosts (Studio, Portal,\n // console) use this shape for both init-response and post-init\n // notification.\n var bb=readMetaFromInitResult(m.params);\n if(bb){mountFromMeta(bb);return;}\n // R5 (2026-05-26) -- the /r/<shortCode> HTTP fallback was removed\n // along with the bearer-by-obscurity model. Hosts that strip\n // _meta on the tool-result wire have no fallback path here;\n // spec-canonical hosts deliver meta inline and land in the two\n // branches above.\n }\n});\nsetOverlay('Initializing\u2026');\nvar initTimer=setTimeout(function(){\n setOverlay('Host did not respond to ui/initialize within 3s.');\n},3000);\npostRpc('ui/initialize',{\n appCapabilities:{},\n appInfo:{name:'ggui-session',version:'1.0.0'},\n protocolVersion:'2026-01-26'\n}).then(function(result){\n clearTimeout(initTimer);\n postNotification('ui/notifications/initialized',{});\n // Path B: slice envelope inline in ui/initialize result. Hosts\n // using <McpAppIframe>'s first-party dispatch deliver meta here\n // and never send a separate ui/notifications/tool-result.\n var b=readMetaFromInitResult(result);\n if(b){mountFromMeta(b);return;}\n // Protocol-violation surface: host signalled an attempt\n // (toolOutput._meta carries ai.ggui/render) but it's malformed.\n // Fail fast so hosts pinning a typed error envelope don't sit on\n // the Path-A waiting overlay forever.\n if(hasAiGguiMetaPlaceholder(result)){\n var msg='ui/initialize result missing valid ai.ggui/render.runtimeUrl';\n setOverlay(msg);\n postBootstrapFailed('BOOTSTRAP_META_MISSING',msg);\n return;\n }\n // Path A: wait for the host to send ui/notifications/tool-result\n // with structuredContent.{url, shortCode}. MCP Apps hosts that don't\n // implement the reading-B inline-meta convention land here.\n setOverlay('Waiting for tool result\u2026');\n}).catch(function(e){\n clearTimeout(initTimer);\n setOverlay('ui/initialize failed: '+(e&&e.message||JSON.stringify(e)));\n});\n})();\n</script></body></html>";
|
|
38
38
|
/**
|
|
39
39
|
* CSP `script-src` source expression that authorises the inline
|
|
40
|
-
* `<script>` block of {@link
|
|
40
|
+
* `<script>` block of {@link GGUI_RENDER_SHELL_HTML} when it executes
|
|
41
41
|
* inside an iframe whose CSP is inherited from a parent host.
|
|
42
42
|
*
|
|
43
43
|
* # Why this exists
|
|
@@ -74,8 +74,8 @@ export declare const GGUI_SESSION_SHELL_HTML = "<!doctype html>\n<html lang=\"en
|
|
|
74
74
|
* path where the host is `<McpAppIframe>` and the parent SPA owns
|
|
75
75
|
* the CSP it inherits.
|
|
76
76
|
*/
|
|
77
|
-
export declare const
|
|
78
|
-
export declare function
|
|
77
|
+
export declare const GGUI_RENDER_SHELL_SCRIPT_HASH: string;
|
|
78
|
+
export declare function registerGguiRenderResource(server: McpServer, shellHtml?: string, publicBaseUrl?: string): void;
|
|
79
79
|
/**
|
|
80
80
|
* Advertise the `io.modelcontextprotocol/ui` extension capability on
|
|
81
81
|
* an `McpServer`'s underlying `Server`. Idempotent - calling twice on
|
|
@@ -94,9 +94,9 @@ export declare function advertiseMcpAppsUiCapability(server: McpServer): void;
|
|
|
94
94
|
* @public
|
|
95
95
|
*/
|
|
96
96
|
export interface SelfContainedShellInputs {
|
|
97
|
-
/**
|
|
98
|
-
readonly
|
|
99
|
-
/** App / tenant id the
|
|
97
|
+
/** Render id whose visible-bits surface is being inlined. */
|
|
98
|
+
readonly renderId: string;
|
|
99
|
+
/** App / tenant id the render is scoped to. */
|
|
100
100
|
readonly appId: string;
|
|
101
101
|
/**
|
|
102
102
|
* Content-addressable URL the iframe fetches the compiled ES module
|
|
@@ -124,22 +124,11 @@ export interface SelfContainedShellInputs {
|
|
|
124
124
|
* `<script type="module" src=...>`. MUST be absolute (or root-
|
|
125
125
|
* relative resolvable from the host's iframe origin) — `srcdoc`
|
|
126
126
|
* iframes have `about:srcdoc` as their URL so a bare relative path
|
|
127
|
-
* cannot resolve. Also inlined on the `
|
|
127
|
+
* cannot resolve. Also inlined on the `__GGUI_META__` global so
|
|
128
128
|
* the iframe-runtime's bootstrap validator (which requires
|
|
129
129
|
* `runtimeUrl` across all modes) accepts the envelope.
|
|
130
130
|
*/
|
|
131
131
|
readonly runtimeUrl: string;
|
|
132
|
-
/** Optional stack-item id forwarded to the renderer for parity
|
|
133
|
-
* with single-item-mode bootstrap selectors. */
|
|
134
|
-
readonly stackItemId?: string;
|
|
135
|
-
/**
|
|
136
|
-
* Canvas-mode flag. When `true`, the
|
|
137
|
-
* bootstrap is built as session-scoped (no stackItemId, no
|
|
138
|
-
* codeUrl/systemKind — live-mode trio MUST be supplied) and the
|
|
139
|
-
* iframe-runtime mounts {@link CanvasShell} instead of a single
|
|
140
|
-
* stack item. Mutually exclusive with {@link stackItemId}.
|
|
141
|
-
*/
|
|
142
|
-
readonly canvasMode?: boolean;
|
|
143
132
|
/** Optional theme id forwarded to the renderer. */
|
|
144
133
|
readonly themeId?: string;
|
|
145
134
|
/**
|
|
@@ -150,7 +139,7 @@ export interface SelfContainedShellInputs {
|
|
|
150
139
|
* `LoadedTheme.mode` for preset/file forms; default-source themes
|
|
151
140
|
* omit this field entirely.
|
|
152
141
|
*/
|
|
153
|
-
readonly themeMode?:
|
|
142
|
+
readonly themeMode?: "light" | "dark";
|
|
154
143
|
/**
|
|
155
144
|
* Optional pre-serialized props (must be a JSON string) forwarded
|
|
156
145
|
* to the renderer. Server inlines the string verbatim — the
|
|
@@ -159,7 +148,7 @@ export interface SelfContainedShellInputs {
|
|
|
159
148
|
readonly propsJson?: string;
|
|
160
149
|
/**
|
|
161
150
|
* Names of same-server tools whose `_meta.ui.visibility` includes
|
|
162
|
-
* `"app"`, mirrored from {@link
|
|
151
|
+
* `"app"`, mirrored from {@link McpAppAiGguiRenderMeta.appCallableTools}.
|
|
163
152
|
* Forwarded to the iframe-runtime so its dispatch closure can choose
|
|
164
153
|
* Pattern α (direct `tools/call`) over Pattern β (3-message bridge)
|
|
165
154
|
* per wired action — even on the self-contained `/r/<shortCode>`
|
|
@@ -169,23 +158,23 @@ export interface SelfContainedShellInputs {
|
|
|
169
158
|
* Empty / absent → no fallout: the runtime's dispatch routes every
|
|
170
159
|
* action through Pattern β.
|
|
171
160
|
*/
|
|
172
|
-
readonly appCallableTools?:
|
|
161
|
+
readonly appCallableTools?: McpAppAiGguiRenderMeta["appCallableTools"];
|
|
173
162
|
/**
|
|
174
|
-
* Per-action wired-tool mapping for the active
|
|
175
|
-
* from {@link
|
|
163
|
+
* Per-action wired-tool mapping for the active render, mirrored
|
|
164
|
+
* from {@link McpAppAiGguiRenderMeta.actionNextSteps}.
|
|
176
165
|
*/
|
|
177
|
-
readonly actionNextSteps?:
|
|
166
|
+
readonly actionNextSteps?: McpAppAiGguiRenderMeta["actionNextSteps"];
|
|
178
167
|
/**
|
|
179
|
-
* Per-slot data for the active
|
|
180
|
-
* from {@link
|
|
168
|
+
* Per-slot data for the active render's `contextSpec`, mirrored
|
|
169
|
+
* from {@link McpAppAiGguiRenderMeta.contextSlots}. The runtime
|
|
181
170
|
* synthesizes one `React.createContext(default)` per entry at boot.
|
|
182
171
|
* Without this field, contextSpec UIs render with un-seeded
|
|
183
172
|
* Providers.
|
|
184
173
|
*/
|
|
185
|
-
readonly contextSlots?:
|
|
174
|
+
readonly contextSlots?: McpAppAiGguiRenderMeta["contextSlots"];
|
|
186
175
|
/**
|
|
187
|
-
* Permissions-Policy directive list derived from the active
|
|
188
|
-
*
|
|
176
|
+
* Permissions-Policy directive list derived from the active render's
|
|
177
|
+
* `clientCapabilities.gadgets[*].permission`.
|
|
189
178
|
* When present (non-empty), inlined onto the bootstrap as
|
|
190
179
|
* `permissionsPolicy` so the iframe-runtime can surface the gate set
|
|
191
180
|
* to in-iframe consumers (debug overlay, permission-aware UI). The
|
|
@@ -199,33 +188,38 @@ export interface SelfContainedShellInputs {
|
|
|
199
188
|
/**
|
|
200
189
|
* Resolved gadget catalog the iframe runtime dynamically imports at
|
|
201
190
|
* boot. Each entry is `{hook,
|
|
202
|
-
* package?, bundleUrl?}`. The MCP-Apps `_meta
|
|
203
|
-
*
|
|
191
|
+
* package?, bundleUrl?}`. The MCP-Apps `_meta` slice channel already
|
|
192
|
+
* forwards this via the render-mutation handler; this field is the
|
|
204
193
|
* symmetric forward for the self-contained shell so `/r/<shortCode>`
|
|
205
|
-
* and `resources/read` iframes don't render as STDLIB-only when
|
|
206
|
-
*
|
|
194
|
+
* and `resources/read` iframes don't render as STDLIB-only when the
|
|
195
|
+
* contract declares wrappers.
|
|
196
|
+
*/
|
|
197
|
+
readonly gadgets?: McpAppAiGguiRenderMeta["gadgets"];
|
|
198
|
+
/**
|
|
199
|
+
* Content-addressable hash for the active render's compiled
|
|
200
|
+
* contract validators, mirrored from
|
|
201
|
+
* {@link McpAppAiGguiRenderMeta.contractHash}. The iframe-runtime
|
|
202
|
+
* resolves validators via `fetch({@link validatorsUrl})` + dynamic
|
|
203
|
+
* import. Paired with {@link validatorsUrl} — present together or
|
|
204
|
+
* absent together.
|
|
207
205
|
*/
|
|
208
|
-
readonly
|
|
206
|
+
readonly contractHash?: McpAppAiGguiRenderMeta["contractHash"];
|
|
209
207
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* the iframe loads these precompiled modules via `blob:` import
|
|
216
|
-
* instead. Symmetric forward for the self-contained shell so
|
|
217
|
-
* `/r/<shortCode>` and `resources/read` iframes validate wire
|
|
218
|
-
* traffic exactly as the MCP-Apps postMessage path does.
|
|
208
|
+
* URL serving the content-addressable contract-validator bundle,
|
|
209
|
+
* mirrored from {@link McpAppAiGguiRenderMeta.validatorsUrl}.
|
|
210
|
+
* Symmetric forward for the self-contained shell so `/r/<shortCode>`
|
|
211
|
+
* and `resources/read` iframes resolve validators exactly as the
|
|
212
|
+
* MCP-Apps postMessage path does.
|
|
219
213
|
*/
|
|
220
|
-
readonly
|
|
214
|
+
readonly validatorsUrl?: McpAppAiGguiRenderMeta["validatorsUrl"];
|
|
221
215
|
/**
|
|
222
216
|
* Server-filtered public env values that declared wrappers'
|
|
223
217
|
* `requires` cover (minimum-disclosure subset of `App.publicEnv`).
|
|
224
|
-
* Symmetric with the `
|
|
225
|
-
* transport that produces
|
|
226
|
-
*
|
|
218
|
+
* Symmetric with the `ai.ggui/render` slice channel — every
|
|
219
|
+
* transport that produces the meta MUST forward this field so
|
|
220
|
+
* wrappers' `getPublicEnv()` reads land.
|
|
227
221
|
*/
|
|
228
|
-
readonly publicEnv?:
|
|
222
|
+
readonly publicEnv?: McpAppAiGguiRenderMeta["publicEnv"];
|
|
229
223
|
/**
|
|
230
224
|
* Live-mode WebSocket URL the iframe-runtime opens to receive
|
|
231
225
|
* `props_update` / `stack_*` frames. When set alongside `token` +
|
|
@@ -238,7 +232,9 @@ export interface SelfContainedShellInputs {
|
|
|
238
232
|
/**
|
|
239
233
|
* Single-use bootstrap token authorising the WS subscribe. Paired
|
|
240
234
|
* with {@link wsUrl} — half-live envelopes (one without the other)
|
|
241
|
-
* are rejected as MALFORMED by
|
|
235
|
+
* are rejected as MALFORMED by the iframe-runtime slice-meta
|
|
236
|
+
* extractors (`parseMetaFromGlobal`, `parseMetaFromToolResult`).
|
|
237
|
+
* Server-minted via
|
|
242
238
|
* the same `mintBootstrap` minter the JSON `/api/bootstrap/<shortCode>`
|
|
243
239
|
* route uses, so both transports share replay-cache state.
|
|
244
240
|
*/
|
|
@@ -249,12 +245,32 @@ export interface SelfContainedShellInputs {
|
|
|
249
245
|
* live updates silently no-op until a fresh push refreshes creds.
|
|
250
246
|
*/
|
|
251
247
|
readonly expiresAt?: string;
|
|
248
|
+
/**
|
|
249
|
+
* Monotonic RenderEvent ledger cursor at emit time, mirrored from
|
|
250
|
+
* {@link McpAppAiGguiRenderMeta.lastSequence}. Polling clients
|
|
251
|
+
* initialize the R7 `/events?sinceSequence=N` cursor from this.
|
|
252
|
+
* Absent in pre-R7 envelopes (back-compat); post-R7 it MUST be
|
|
253
|
+
* present.
|
|
254
|
+
*/
|
|
255
|
+
readonly lastSequence?: McpAppAiGguiRenderMeta["lastSequence"];
|
|
256
|
+
/**
|
|
257
|
+
* Wire-stamped polling fallback URL — `${base}/api/renders/<id>/events?wsToken=<...>`.
|
|
258
|
+
* When the iframe-runtime's WS transport reaches `'failed'` (CSP
|
|
259
|
+
* blocks `ws://`, corporate firewall, etc.), `@ggui-ai/live-channel`
|
|
260
|
+
* fails over to cursor-based event polling against this URL using
|
|
261
|
+
* `{@link lastSequence}` as the initial cursor.
|
|
262
|
+
*
|
|
263
|
+
* Absent ⇒ runtime stays in WS-only mode. Operators that want the
|
|
264
|
+
* fallback path lit up MUST thread this through (the canvas/inline
|
|
265
|
+
* shell builders do; callers composing their own shells SHOULD).
|
|
266
|
+
*/
|
|
267
|
+
readonly pollingUrl?: McpAppAiGguiRenderMeta["pollingUrl"];
|
|
252
268
|
}
|
|
253
269
|
/**
|
|
254
270
|
* Build the self-contained shell HTML for a given session.
|
|
255
271
|
*
|
|
256
272
|
* The returned HTML is a complete, standalone document: it inlines the
|
|
257
|
-
* compiled component (base64) + session ids in a `window.
|
|
273
|
+
* compiled component (base64) + session ids in a `window.__GGUI_META__`
|
|
258
274
|
* global, then loads the iframe-runtime bundle via `<script type="module"
|
|
259
275
|
* src={runtimeUrl}>`. The runtime takes over synchronously on import,
|
|
260
276
|
* mounts the component, and the iframe paints WITHOUT any further server
|
|
@@ -276,40 +292,40 @@ export interface SelfContainedShellInputs {
|
|
|
276
292
|
*/
|
|
277
293
|
export declare function buildSelfContainedShell(opts: SelfContainedShellInputs): string;
|
|
278
294
|
/**
|
|
279
|
-
* Minimal "loading" HTML served when a per-
|
|
280
|
-
* for a
|
|
295
|
+
* Minimal "loading" HTML served when a per-render resource is fetched
|
|
296
|
+
* for a render whose visible-bits surface has no componentCode yet
|
|
281
297
|
* (placeholder, generation in flight). Renders a tiny status surface
|
|
282
298
|
* so hosts that pin lifecycle selectors don't see a blank document.
|
|
283
299
|
*
|
|
284
|
-
* Hosts SHOULD re-fetch when they observe additional `
|
|
285
|
-
* results on the same
|
|
286
|
-
* value stays stable across
|
|
300
|
+
* Hosts SHOULD re-fetch when they observe additional `ggui_render`
|
|
301
|
+
* results on the same render — the per-call `_meta.ui.resourceUri`
|
|
302
|
+
* value stays stable across commits for a render, so re-fetching the
|
|
287
303
|
* same URI returns fresher HTML on the second try.
|
|
288
304
|
*
|
|
289
305
|
* @public
|
|
290
306
|
*/
|
|
291
|
-
export declare function buildSelfContainedLoadingShell(
|
|
307
|
+
export declare function buildSelfContainedLoadingShell(renderId: string): string;
|
|
292
308
|
/**
|
|
293
|
-
* Options for {@link
|
|
309
|
+
* Options for {@link registerGguiRenderResourceTemplate}.
|
|
294
310
|
*
|
|
295
311
|
* @public
|
|
296
312
|
*/
|
|
297
|
-
export interface
|
|
298
|
-
/**
|
|
299
|
-
*
|
|
300
|
-
readonly
|
|
313
|
+
export interface GguiRenderResourceTemplateOptions {
|
|
314
|
+
/** RenderStore the template handler reads to find the render's
|
|
315
|
+
* componentCode. */
|
|
316
|
+
readonly renderStore: RenderStore;
|
|
301
317
|
/** Absolute URL of the iframe-runtime bundle inlined in the shell. */
|
|
302
318
|
readonly runtimeUrl: string;
|
|
303
319
|
/**
|
|
304
320
|
* Content-addressable code-blob store. When wired alongside
|
|
305
|
-
* {@link codeBaseUrl}, the resource template hashes the
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
321
|
+
* {@link codeBaseUrl}, the resource template hashes the render's
|
|
322
|
+
* componentCode, writes it to the store, and inlines the resulting
|
|
323
|
+
* `codeUrl` into the shell bootstrap. Without these deps the
|
|
324
|
+
* resource path emits the loading shell when the render is a
|
|
309
325
|
* compiled component (the static-component channel cannot deliver
|
|
310
326
|
* without a URL).
|
|
311
327
|
*/
|
|
312
|
-
readonly codeStore?: import(
|
|
328
|
+
readonly codeStore?: import("@ggui-ai/mcp-server-core").CodeStore;
|
|
313
329
|
/**
|
|
314
330
|
* Base URL the code-blob route resolves to. Paired with {@link codeStore}.
|
|
315
331
|
*/
|
|
@@ -323,7 +339,7 @@ export interface GguiSessionResourceTemplateOptions {
|
|
|
323
339
|
*/
|
|
324
340
|
readonly themeId?: string;
|
|
325
341
|
/** Theme color mode resolved from `ggui.json#theme.mode`. */
|
|
326
|
-
readonly themeMode?:
|
|
342
|
+
readonly themeMode?: "light" | "dark";
|
|
327
343
|
/**
|
|
328
344
|
* Per-app metadata store the resource handler reads to resolve
|
|
329
345
|
* `App.publicEnv` for the bootstrap projection.
|
|
@@ -332,17 +348,17 @@ export interface GguiSessionResourceTemplateOptions {
|
|
|
332
348
|
* resource-served bootstrap (wrappers calling `getPublicEnv` throw
|
|
333
349
|
* at hook-mount with a clear "not provided" message).
|
|
334
350
|
*/
|
|
335
|
-
readonly appMetadataStore?: import(
|
|
351
|
+
readonly appMetadataStore?: import("@ggui-ai/mcp-server-core").AppMetadataStore;
|
|
336
352
|
/**
|
|
337
353
|
* Vector store backing the blueprint registry. When wired alongside
|
|
338
354
|
* `defaultAppIdFallback`, the resource handler runs a registry-only
|
|
339
355
|
* rehydrate fallback for the two-segment URI shape
|
|
340
|
-
* (`ui://ggui/
|
|
356
|
+
* (`ui://ggui/render/{renderId}/{blueprintKey}`): if the render is
|
|
341
357
|
* gone but the blueprint registry still holds the entry, return the
|
|
342
358
|
* static initial render (default props + default context) instead of
|
|
343
359
|
* the dead "Generating UI…" loading shell. Single-tenant OSS sees
|
|
344
360
|
* meaningful improvement; multi-tenant deployments leave both options
|
|
345
|
-
* undefined to keep the loading-shell behavior on
|
|
361
|
+
* undefined to keep the loading-shell behavior on render miss.
|
|
346
362
|
*/
|
|
347
363
|
readonly vectorStore?: VectorStore;
|
|
348
364
|
/**
|
|
@@ -354,72 +370,72 @@ export interface GguiSessionResourceTemplateOptions {
|
|
|
354
370
|
* and rehydrate works across session expiry / process restart.
|
|
355
371
|
*/
|
|
356
372
|
readonly defaultAppIdFallback?: string;
|
|
357
|
-
/**
|
|
358
|
-
* Bootstrap minter for canvas-mode
|
|
359
|
-
* sessions. Supplied by the deployment (cloud wires it to the
|
|
360
|
-
* AppSync-bound minter; OSS wires it to the in-process minter
|
|
361
|
-
* already used by /api/bootstrap/<shortCode>).
|
|
362
|
-
*
|
|
363
|
-
* When the resource handler observes `session.mcpAppsMode ===
|
|
364
|
-
* 'canvas'`, it calls this to obtain `wsUrl` + `token` + `expiresAt`
|
|
365
|
-
* and inlines them on the canvas shell's bootstrap. Without this
|
|
366
|
-
* dep wired, the handler falls back to the legacy single-item path
|
|
367
|
-
* (no canvas).
|
|
368
|
-
*
|
|
369
|
-
* Returning `null` / throwing here degrades to the legacy path
|
|
370
|
-
* silently — the caller has no way to deliver a working canvas
|
|
371
|
-
* without WS credentials, but pinning a stackItem render is
|
|
372
|
-
* strictly better than serving a dead-loading shell.
|
|
373
|
-
*/
|
|
374
|
-
readonly mintBootstrap?: (sessionId: string, appId: string) => Promise<{
|
|
375
|
-
readonly wsUrl: string;
|
|
376
|
-
readonly token: string;
|
|
377
|
-
readonly expiresAt: string;
|
|
378
|
-
} | null>;
|
|
379
373
|
/**
|
|
380
374
|
* Operator-supplied public origin. When present, every
|
|
381
375
|
* `resources/read` response from this template carries
|
|
382
376
|
* `_meta.ui.csp.{connectDomains,resourceDomains}` so claude.ai's
|
|
383
377
|
* cross-origin iframe CSP allows the runtime bundle, codeUrl
|
|
384
378
|
* fetches, and the live-channel WebSocket. Symmetric with the
|
|
385
|
-
* declaration on the static `ui://ggui/
|
|
379
|
+
* declaration on the static `ui://ggui/render` resource. Absent ⇒
|
|
386
380
|
* `_meta.ui.csp` omitted and the host's default CSP applies
|
|
387
381
|
* (`connect-src 'none'` in claude.ai — runtime bundle fails to
|
|
388
382
|
* load with a generic "script error").
|
|
389
383
|
*/
|
|
390
384
|
readonly publicBaseUrl?: string;
|
|
385
|
+
/**
|
|
386
|
+
* Live-channel WebSocket bootstrap minter. When wired, every
|
|
387
|
+
* `resources/read` response embeds `{wsUrl, wsToken}` in the shell
|
|
388
|
+
* so the iframe-runtime opens a WebSocket immediately on mount and
|
|
389
|
+
* receives `props_update` frames for in-place re-renders.
|
|
390
|
+
*
|
|
391
|
+
* Without this, the per-render resource shell mounts in
|
|
392
|
+
* static-component mode (codeUrl only) — initial render works but
|
|
393
|
+
* server-side state mutations (`ggui_update`) never visibly update
|
|
394
|
+
* the iframe. Spec-compliant MCP-Apps hosts can still re-fetch
|
|
395
|
+
* `resources/read` per-tool-result to get fresh HTML, but in-place
|
|
396
|
+
* live updates require the WS pipe.
|
|
397
|
+
*
|
|
398
|
+
* Mirrors the `mintWsToken` plumbed into the handler-side render
|
|
399
|
+
* machinery in `server.ts`; the resource template owns its own
|
|
400
|
+
* call here because it runs OUTSIDE the per-tool-call context.
|
|
401
|
+
*/
|
|
402
|
+
readonly mintWsToken?: (renderId: string, appId: string) => {
|
|
403
|
+
readonly wsUrl: string;
|
|
404
|
+
readonly token: string;
|
|
405
|
+
readonly expiresAt: string;
|
|
406
|
+
};
|
|
391
407
|
}
|
|
392
408
|
/**
|
|
393
|
-
* Register a `ui://ggui/
|
|
394
|
-
* `resources/read` request is resolved by looking up the
|
|
395
|
-
* store
|
|
396
|
-
*
|
|
409
|
+
* Register a `ui://ggui/render/{renderId}` resource template. Each
|
|
410
|
+
* `resources/read` request is resolved by looking up the render in the
|
|
411
|
+
* store and returning the self-contained shell with that
|
|
412
|
+
* componentCode inlined.
|
|
397
413
|
*
|
|
398
|
-
* Per-call `_meta.ui.resourceUri` (stamped by `
|
|
399
|
-
* pins the URI to a specific
|
|
400
|
-
* than the static `ui://ggui/
|
|
414
|
+
* Per-call `_meta.ui.resourceUri` (stamped by `ggui_render.resultMeta`)
|
|
415
|
+
* pins the URI to a specific renderId; hosts fetch THAT URI rather
|
|
416
|
+
* than the static `ui://ggui/render` one. Both registrations co-exist:
|
|
401
417
|
* legacy postMessage shell at the static URI, self-contained shell at
|
|
402
418
|
* the templated URI.
|
|
403
419
|
*
|
|
404
420
|
* Failure modes:
|
|
405
|
-
* -
|
|
406
|
-
*
|
|
407
|
-
* -
|
|
408
|
-
* -
|
|
421
|
+
* - Render not found → loading shell (host re-fetches; absent
|
|
422
|
+
* render is a transient state immediately after `ggui_render`).
|
|
423
|
+
* - Render found, no componentCode yet → loading shell.
|
|
424
|
+
* - Render found, componentCode present → self-contained shell.
|
|
409
425
|
*
|
|
410
426
|
* Returns nothing; mutates the server in place.
|
|
411
427
|
*
|
|
412
428
|
* @public
|
|
413
429
|
*/
|
|
414
|
-
export declare function
|
|
430
|
+
export declare function registerGguiRenderResourceTemplate(server: McpServer, opts: GguiRenderResourceTemplateOptions): void;
|
|
415
431
|
/**
|
|
416
432
|
* Apply the full MCP Apps outbound wiring to a fresh `McpServer` - both
|
|
417
|
-
* the capability advertisement and the `ui://ggui/
|
|
433
|
+
* the capability advertisement and the `ui://ggui/render` resource. The
|
|
418
434
|
* single entry-point `build-mcp.ts` calls so request-path wiring stays
|
|
419
435
|
* one line.
|
|
420
436
|
*
|
|
421
|
-
* When `selfContained` is supplied, ALSO registers the per-
|
|
422
|
-
* `ui://ggui/
|
|
437
|
+
* When `selfContained` is supplied, ALSO registers the per-render
|
|
438
|
+
* `ui://ggui/render/{renderId}` resource template that serves the
|
|
423
439
|
* self-contained shell (the path third-party MCP Apps hosts use). The
|
|
424
440
|
* legacy static URI registration is unconditional — first-party hosts
|
|
425
441
|
* (Studio, Portal, console) still rely on the postMessage path.
|
|
@@ -427,16 +443,15 @@ export declare function registerGguiSessionResourceTemplate(server: McpServer, o
|
|
|
427
443
|
export declare function installMcpAppsOutbound(server: McpServer, opts?: {
|
|
428
444
|
readonly shellHtml?: string;
|
|
429
445
|
/**
|
|
430
|
-
* Per-
|
|
431
|
-
* `ui://ggui/
|
|
446
|
+
* Per-render self-contained shell registration. When supplied,
|
|
447
|
+
* `ui://ggui/render/{renderId}` becomes a readable resource
|
|
432
448
|
* template whose body inlines the compiled componentCode from the
|
|
433
|
-
*
|
|
434
|
-
* postMessage shell is registered.
|
|
449
|
+
* render. Absent → only the legacy postMessage shell is registered.
|
|
435
450
|
*/
|
|
436
|
-
readonly selfContained?:
|
|
451
|
+
readonly selfContained?: GguiRenderResourceTemplateOptions;
|
|
437
452
|
/**
|
|
438
453
|
* Public origin the server is reachable at — forwarded to
|
|
439
|
-
* `
|
|
454
|
+
* `registerGguiRenderResource` so the static `ui://ggui/render`
|
|
440
455
|
* resource carries `_meta.ui.csp.{connectDomains,resourceDomains}`
|
|
441
456
|
* authorising the iframe to fetch the runtime bundle and open a
|
|
442
457
|
* WebSocket. Omit when running same-origin behind a first-party
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;
|
|
1
|
+
{"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"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"}
|