@ggui-ai/mcp-server 0.6.3 → 0.8.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 (42) hide show
  1. package/dist/api-renders-routes.d.ts.map +1 -1
  2. package/dist/api-renders-routes.js +9 -1
  3. package/dist/browser-cors.d.ts +29 -0
  4. package/dist/browser-cors.d.ts.map +1 -0
  5. package/dist/browser-cors.js +64 -0
  6. package/dist/build-mcp.d.ts.map +1 -1
  7. package/dist/build-mcp.js +5 -1
  8. package/dist/code-store-fs.d.ts +3 -0
  9. package/dist/code-store-fs.d.ts.map +1 -1
  10. package/dist/code-store-fs.js +27 -3
  11. package/dist/console-session-routes.d.ts +10 -5
  12. package/dist/console-session-routes.d.ts.map +1 -1
  13. package/dist/console-session-routes.js +10 -5
  14. package/dist/ggui-session-channel/outbound.d.ts.map +1 -1
  15. package/dist/ggui-session-channel/outbound.js +12 -0
  16. package/dist/ggui-session-channel/socket-router.d.ts +33 -0
  17. package/dist/ggui-session-channel/socket-router.d.ts.map +1 -1
  18. package/dist/ggui-session-channel/socket-router.js +155 -0
  19. package/dist/ggui-session-channel/subscribe.d.ts.map +1 -1
  20. package/dist/ggui-session-channel/subscribe.js +43 -2
  21. package/dist/ggui-session-channel.d.ts +114 -0
  22. package/dist/ggui-session-channel.d.ts.map +1 -1
  23. package/dist/ggui-session-channel.js +77 -2
  24. package/dist/health-routes.d.ts +3 -0
  25. package/dist/health-routes.d.ts.map +1 -1
  26. package/dist/health-routes.js +11 -1
  27. package/dist/mcp-apps-outbound.d.ts +236 -31
  28. package/dist/mcp-apps-outbound.d.ts.map +1 -1
  29. package/dist/mcp-apps-outbound.js +940 -261
  30. package/dist/origin-validation.d.ts +120 -0
  31. package/dist/origin-validation.d.ts.map +1 -0
  32. package/dist/origin-validation.js +199 -0
  33. package/dist/render-read-gate.d.ts +50 -0
  34. package/dist/render-read-gate.d.ts.map +1 -0
  35. package/dist/render-read-gate.js +36 -0
  36. package/dist/runtime-bundle-route.d.ts +15 -0
  37. package/dist/runtime-bundle-route.d.ts.map +1 -1
  38. package/dist/runtime-bundle-route.js +20 -4
  39. package/dist/server.d.ts +227 -9
  40. package/dist/server.d.ts.map +1 -1
  41. package/dist/server.js +292 -28
  42. package/package.json +12 -12
@@ -32,8 +32,11 @@
32
32
  * same `@ggui-ai/mcp-server` instance that mints the bootstrap.
33
33
  */
34
34
  import type { BlueprintIndex, GguiSessionStore, VectorStore } from "@ggui-ai/mcp-server-core";
35
+ import { type BlueprintDurabilityDeps } from "@ggui-ai/mcp-server-handlers/renders";
35
36
  import { type McpAppAiGguiRenderMeta } from "@ggui-ai/protocol/integrations/mcp-apps";
36
37
  import { type McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
38
+ import type { HandlerContext } from "@ggui-ai/mcp-server-handlers";
39
+ import type { Logger } from "./logger.js";
37
40
  export declare const GGUI_RENDER_SHELL_HTML = "<!doctype html>\n<html lang=\"en\" style=\"height:100%;background-color:var(--ggui-color-surface, #1e293b)\"><head><meta charset=\"utf-8\"><title>ggui render</title></head>\n<body style=\"margin:0;height:100%;min-height:480px;background-color:var(--ggui-color-surface, #1e293b)\"><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), set window.__GGUI_META__ to\n// the 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; sessionId 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\": { sessionId,\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('MALFORMED_BOOTSTRAP','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 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. Spec-compliant hosts (Claude Desktop, claude.ai\n // Connector, Claude Code) deliver slice-envelope material here.\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 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.\n var specMeta=readMetaFromCallToolResult(m.params);\n if(specMeta){mountFromMeta(specMeta);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 branch\n // 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-render',version:'1.0.0'},\n protocolVersion:'2026-01-26'\n}).then(function(){\n clearTimeout(initTimer);\n postNotification('ui/notifications/initialized',{});\n // Wait for the host to send ui/notifications/tool-result carrying\n // the slice envelope in _meta \u2014 the spec-canonical delivery channel.\n // The ui/initialize result itself carries no slice meta (the\n // McpUiInitializeResult schema defines no such field).\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
41
  /**
39
42
  * CSP `script-src` source expression that authorises the inline
@@ -75,7 +78,41 @@ export declare const GGUI_RENDER_SHELL_HTML = "<!doctype html>\n<html lang=\"en\
75
78
  * the CSP it inherits.
76
79
  */
77
80
  export declare const GGUI_RENDER_SHELL_SCRIPT_HASH: string;
78
- export declare function registerGguiRenderResource(server: McpServer, shellHtml?: string, publicBaseUrl?: string): void;
81
+ /**
82
+ * Build the INLINE-RUNTIME static shell: a standalone document carrying
83
+ * the iframe-runtime bundle in its own bytes instead of an external
84
+ * `<script src>` tag. For MCP Apps hosts whose iframe CSP forbids
85
+ * external `script-src` while permitting inline scripts — the thin
86
+ * postMessage shell can never load its runtime there, so the shell IS
87
+ * the runtime.
88
+ *
89
+ * Per-render state does NOT live here (same posture as the thin
90
+ * shell): the host delivers it via `ui/notifications/tool-result`,
91
+ * caught either by the buffer script (pre-parse arrivals) or by the
92
+ * runtime's own autostart listener. Live-channel / codeUrl fetches are
93
+ * unavailable under the CSP this shell targets; delivered meta is
94
+ * expected to carry the fetch-free channels (inline `codeB64`, inline
95
+ * `propsJson`).
96
+ *
97
+ * Served per-mount via `installMcpAppsOutbound({ shellHtml })` — the
98
+ * module-level thin-shell constants (and their pinned CSP hash) are
99
+ * deliberately untouched.
100
+ */
101
+ export declare function buildInlineRenderShellHtml(runtimeSource: string): string;
102
+ export declare function registerGguiRenderResource(server: McpServer, shellHtml?: string, publicBaseUrl?: string,
103
+ /**
104
+ * Absolute runtime-bundle URL used as the CSP-declaration fallback
105
+ * when `publicBaseUrl` is absent — same posture as the per-render
106
+ * template registration ({@link buildCspMeta}'s second parameter).
107
+ * Deployments that publish an absolute `runtime.url` but
108
+ * deliberately do NOT set `publicBaseUrl` (it also feeds
109
+ * Origin/Host enforcement and OAuth) still get
110
+ * `_meta.ui.csp.{connectDomains,resourceDomains}` on the static
111
+ * resource read; before this fallback those reads carried no
112
+ * declaration at all and spec-compliant hosts applied the
113
+ * restrictive default (`connect-src 'none'`).
114
+ */
115
+ runtimeUrl?: string): void;
79
116
  /**
80
117
  * Advertise the `io.modelcontextprotocol/ui` extension capability on
81
118
  * an `McpServer`'s underlying `Server`. Idempotent - calling twice on
@@ -113,6 +150,16 @@ export interface SelfContainedShellInputs {
113
150
  * renders without re-parsing.
114
151
  */
115
152
  readonly codeHash?: string;
153
+ /**
154
+ * Base64-encoded compiled component source inlined on the bootstrap
155
+ * — the fetch-free twin of {@link codeUrl}, decoded by the
156
+ * iframe-runtime instead of fetched. May coexist with codeUrl
157
+ * (consumers prefer the inline bytes); mutually exclusive with
158
+ * {@link systemKind}. For hosts whose iframe CSP blocks the codeUrl
159
+ * fetch, and for deployments whose code store is unwired or down —
160
+ * the inline bytes make the shell mountable independently of both.
161
+ */
162
+ readonly codeB64?: string;
116
163
  /**
117
164
  * System-card kind identifier, mapped at runtime against the
118
165
  * built-in `SYSTEM_CARD_REGISTRY`. Mutually exclusive with
@@ -281,20 +328,6 @@ export interface SelfContainedShellInputs {
281
328
  * @public
282
329
  */
283
330
  export declare function buildSelfContainedShell(opts: SelfContainedShellInputs): string;
284
- /**
285
- * Minimal "loading" HTML served when a per-render resource is fetched
286
- * for a render whose visible-bits surface has no componentCode yet
287
- * (placeholder, generation in flight). Renders a tiny status surface
288
- * so hosts that pin lifecycle selectors don't see a blank document.
289
- *
290
- * Hosts SHOULD re-fetch when they observe additional `ggui_render`
291
- * results on the same render — the per-call `_meta.ui.resourceUri`
292
- * value stays stable across commits for a render, so re-fetching the
293
- * same URI returns fresher HTML on the second try.
294
- *
295
- * @public
296
- */
297
- export declare function buildSelfContainedLoadingShell(sessionId: string): string;
298
331
  /**
299
332
  * Options for {@link registerGguiRenderResourceTemplate}.
300
333
  *
@@ -310,10 +343,13 @@ export interface GguiRenderResourceTemplateOptions {
310
343
  * Content-addressable code-blob store. When wired alongside
311
344
  * {@link codeBaseUrl}, the resource template hashes the render's
312
345
  * componentCode, writes it to the store, and inlines the resulting
313
- * `codeUrl` into the shell bootstrap. Without these deps the
314
- * resource path emits the loading shell when the render is a
315
- * compiled component (the static-component channel cannot deliver
316
- * without a URL).
346
+ * `codeUrl` into the shell bootstrap.
347
+ *
348
+ * This is one of the two delivery channels; {@link mintWsToken} is
349
+ * the other, and a compiled component needs at least one of them. A
350
+ * read that resolves a render this server can deliver by neither
351
+ * fails with `NOT_MOUNTABLE` rather than returning a shell that would
352
+ * never paint.
317
353
  */
318
354
  readonly codeStore?: import("@ggui-ai/mcp-server-core").CodeStore;
319
355
  /**
@@ -339,6 +375,68 @@ export interface GguiRenderResourceTemplateOptions {
339
375
  * at hook-mount with a clear "not provided" message).
340
376
  */
341
377
  readonly appMetadataStore?: import("@ggui-ai/mcp-server-core").AppMetadataStore;
378
+ /**
379
+ * Durable per-session identity records. When wired alongside
380
+ * {@link durableBlueprints}, a read of a locator whose render row is
381
+ * GONE resolves the record instead of giving up: the render is
382
+ * re-created from it and mounts live, with the props it last
383
+ * carried.
384
+ *
385
+ * Absent ⇒ neither store is consulted, and `NOT_SUPPORTED` takes
386
+ * `NOT_FOUND`'s place wholesale — the honest answer for a server on
387
+ * which an evicted locator can never come back. Wholesale is the
388
+ * load-bearing word: it answers for a locator that never existed AND
389
+ * for one whose row exists but the caller may not read, so the choice
390
+ * of code says nothing about which locators exist. Reads that get far
391
+ * enough to fail `NOT_MOUNTABLE` still do — that verdict is about the
392
+ * caller's own render, not about this server's memory.
393
+ *
394
+ * Binding a store whose records do not outlive the render rows
395
+ * themselves buys nothing — the record is read precisely when the
396
+ * row is gone.
397
+ */
398
+ readonly renderIdentityStore?: import("@ggui-ai/mcp-server-core").RenderIdentityStore;
399
+ /**
400
+ * Durable blueprint pair — row metadata plus the compiled body
401
+ * behind its content hash. The re-mint path needs BOTH halves: the
402
+ * record names a blueprint, the blueprint names a body, and the body
403
+ * is what a re-created render mounts. A pair carrying only
404
+ * `blueprintStore` persists metadata for other consumers but cannot
405
+ * complete a re-mint, so the path skips as if unwired.
406
+ *
407
+ * Same shape the registration path writes through, so a deployment
408
+ * threads one pair to both ends.
409
+ */
410
+ readonly durableBlueprints?: BlueprintDurabilityDeps;
411
+ /**
412
+ * Render-row retention window in ms — the SAME operator knob
413
+ * `ggui_render` stamps new renders with. The read path spends it in
414
+ * two places, and both are lifecycle decisions about a row this
415
+ * handler is putting back into service:
416
+ *
417
+ * - a re-mint commits its reconstructed row with this lifetime;
418
+ * - a read of a row that is present but PAST its expiry gives the
419
+ * row this lifetime again, so the live-channel token minted in
420
+ * the same read cannot outlive the row it addresses.
421
+ *
422
+ * Absent ⇒ one hour, matching `ggui_render`'s own fallback. Setting
423
+ * it on a deployment whose renders live far longer than an hour is
424
+ * what stops a rehydrated render from being quietly demoted to a
425
+ * fraction of its neighbours' lifetime.
426
+ */
427
+ readonly renderTtlMs?: number;
428
+ /**
429
+ * #457 — total-lifetime bound on RESURRECTION: an expired row's read
430
+ * (and a re-mint's fresh commit) may never advance `expiresAt` past
431
+ * `createdAt + maxRenderLifetimeMs`. Unset = unbounded, today's
432
+ * behavior — the knob exists because operator-set short TTLs are
433
+ * exactly where uncapped in-grace self-extension keeps a PII row
434
+ * alive forever on one poll per (TTL + grace). Live-row heartbeats
435
+ * are deliberately NOT capped (active-use lease renewal is intended);
436
+ * rows whose `createdAt` projects the legacy 0 sentinel are exempt
437
+ * (a cap keyed on 0 would mass-kill pre-column rows).
438
+ */
439
+ readonly maxRenderLifetimeMs?: number;
342
440
  /**
343
441
  * Vector store backing the blueprint registry. When wired alongside
344
442
  * `defaultAppIdFallback`, the resource handler runs a registry-only
@@ -346,9 +444,26 @@ export interface GguiRenderResourceTemplateOptions {
346
444
  * (`ui://ggui/render/{sessionId}/{blueprintKey}`): if the render is
347
445
  * gone but the blueprint registry still holds the entry, return the
348
446
  * static initial render (default props + default context) instead of
349
- * the dead "Generating UI…" loading shell. Single-tenant OSS sees
350
- * meaningful improvement; multi-tenant deployments leave both options
351
- * undefined to keep the loading-shell behavior on render miss.
447
+ * failing the read.
448
+ *
449
+ * The lookup is keyed by the caller-supplied `blueprintKey` under
450
+ * `defaultAppIdFallback` alone — it never reads the render row — so
451
+ * it answers the same for a caller who may not read the row as for
452
+ * one probing a locator that never existed. That is what keeps it
453
+ * safe to run on a refused read, which it must, or refusal would
454
+ * become distinguishable from a miss.
455
+ *
456
+ * Leaving both options undefined does NOT return callers to a
457
+ * placeholder shell — there is no longer one to return. It removes
458
+ * the third resolution, so a read that neither the row nor a re-mint
459
+ * can serve fails typed: `NOT_FOUND`, or `NOT_SUPPORTED` on a
460
+ * deployment that also keeps no durable record, or `NOT_MOUNTABLE`
461
+ * where the row is the caller's own and simply has no component yet.
462
+ * Deployments serving
463
+ * more than one tenant from one registry scope are the reason the
464
+ * option exists at all: the fallback discloses whether a blueprint
465
+ * key exists under that scope, so leave it unset if that is not
466
+ * acceptable.
352
467
  */
353
468
  readonly vectorStore?: VectorStore;
354
469
  /**
@@ -363,10 +478,17 @@ export interface GguiRenderResourceTemplateOptions {
363
478
  /**
364
479
  * App-id used for blueprint-registry scoping when the render has
365
480
  * been evicted. The registry is per-`appId`, but a missing render
366
- * has no way to derive its tenant — multi-tenant deployments leave
367
- * this undefined to fail-safe back to the loading shell. Single-
368
- * tenant OSS sets `'builder'` (the universal-MCP default identity)
369
- * and rehydrate works across render expiry / process restart.
481
+ * has no way to derive whose it was, so the fallback needs one scope
482
+ * named up front. Set to `'builder'` (the universal-MCP default
483
+ * identity) and rehydrate works across render expiry / process
484
+ * restart.
485
+ *
486
+ * Leaving it undefined disables the fallback entirely — the reads it
487
+ * would have served fail typed instead (`NOT_FOUND`, or
488
+ * `NOT_SUPPORTED` where no durable record is kept). That is the
489
+ * setting for a deployment that cannot accept the fallback answering
490
+ * "a blueprint with this key exists under this scope" to whoever
491
+ * asks; it is not a way to get a gentler response.
370
492
  */
371
493
  readonly defaultAppIdFallback?: string;
372
494
  /**
@@ -403,6 +525,21 @@ export interface GguiRenderResourceTemplateOptions {
403
525
  readonly token: string;
404
526
  readonly expiresAt: string;
405
527
  };
528
+ /**
529
+ * Per-request handler-context accessor — the SAME AsyncLocalStorage
530
+ * read the tool path uses. The per-session resource handler gates
531
+ * reads on it (render-read-gate.ts). Absent ⇒ the handler fails
532
+ * closed for rows scoped to any app other than the single-tenant
533
+ * default (compose paths that cannot thread a context keep working
534
+ * for OSS single-tenant flows only).
535
+ */
536
+ readonly getContext?: () => HandlerContext | undefined;
537
+ /**
538
+ * Structured logger for warn-level denial audit lines
539
+ * (`render_resource_read_denied`). Absent ⇒ denials are silent
540
+ * server-side (still enforced — only the log line is skipped).
541
+ */
542
+ readonly logger?: Logger;
406
543
  }
407
544
  /**
408
545
  * Register a `ui://ggui/render/{sessionId}` resource template. Each
@@ -416,11 +553,79 @@ export interface GguiRenderResourceTemplateOptions {
416
553
  * legacy postMessage shell at the static URI, self-contained shell at
417
554
  * the templated URI.
418
555
  *
419
- * Failure modes:
420
- * - GguiSession not found → loading shell (host re-fetches; absent
421
- * render is a transient state immediately after `ggui_render`).
422
- * - GguiSession found, no componentCode yet → loading shell.
423
- * - GguiSession found, componentCode present → self-contained shell.
556
+ * # The obligation
557
+ *
558
+ * A read returns EITHER a shell carrying mount material — a static
559
+ * component URL, a live channel, or a system card — OR exactly one
560
+ * typed JSON-RPC error. There is no third outcome, and in particular no
561
+ * successful result wrapping a shell that can never paint anything. A
562
+ * host can check this without trusting the server: if it got
563
+ * `contents`, it got something mountable.
564
+ *
565
+ * The obligation covers outcomes this server can DECIDE. A malfunction
566
+ * still reaches the caller as an internal error (`-32603`) carrying
567
+ * none of the four codes: a template wired with an empty `runtimeUrl`,
568
+ * or a delivery channel that faults **and leaves the read with no other
569
+ * channel to mount through**. A fault the read survives — the usual
570
+ * case on a deployment wiring both channels — is not an outcome at all;
571
+ * the render mounts through whatever is left. That split is deliberate:
572
+ * `-32603` says "something is broken here", which the four codes must
573
+ * never be diluted into claiming, and equally must not be raised over a
574
+ * blip the server routed around.
575
+ *
576
+ * Three resolutions are tried, in this order, and any of them can
577
+ * produce the mount:
578
+ *
579
+ * 1. The render row itself.
580
+ * 2. A re-mint from the durable identity record, when the row is gone
581
+ * and this server keeps one ({@link GguiRenderResourceTemplateOptions.renderIdentityStore}
582
+ * + {@link GguiRenderResourceTemplateOptions.durableBlueprints}).
583
+ * 3. The blueprint registry, keyed by the locator's own `blueprintKey`
584
+ * — the original component with its authoring-time defaults rather
585
+ * than the state the render last held.
586
+ *
587
+ * # Failure modes
588
+ *
589
+ * Which code a given read produces is fully predictable from two facts:
590
+ * whether this server binds a durable substrate (both
591
+ * {@link GguiRenderResourceTemplateOptions.renderIdentityStore} and
592
+ * {@link GguiRenderResourceTemplateOptions.durableBlueprints} — either
593
+ * one alone is as good as neither), and how far the read got.
594
+ *
595
+ * - `NOT_FOUND` (`-32002`) — nothing resolved the locator, **and** the
596
+ * response for a caller who may not read a locator that DOES
597
+ * resolve. Those two are byte-identical by construction: both route
598
+ * through the protocol's projection, which substitutes a constant
599
+ * message and drops `detail`. A distinguishable refusal would turn
600
+ * this read into an oracle for the existence of other callers'
601
+ * renders, so the equality is a security property, not a courtesy.
602
+ * - `NOT_SUPPORTED` (`-32006`) — the same two cases, on a server with
603
+ * no durable substrate: an evicted locator can never be restored
604
+ * here, so saying "not found" would understate it. It takes
605
+ * `NOT_FOUND`'s place WHOLESALE on such a server rather than
606
+ * answering for some locators and not others — a server that said
607
+ * `NOT_FOUND` for a row it refused and `NOT_SUPPORTED` for one that
608
+ * never existed would have rebuilt the same oracle out of the two
609
+ * codes. Correspondingly, a server that DOES bind the substrate
610
+ * never emits it.
611
+ * - `BLUEPRINT_UNRESOLVABLE` (`-32006`) — a record named the render
612
+ * but its component is gone; `detail` names which link broke.
613
+ * Reachable only after the access check.
614
+ * - `NOT_MOUNTABLE` (`-32006`) — something resolved, but nothing
615
+ * mountable can be produced from it: no delivery channel is wired
616
+ * (neither {@link GguiRenderResourceTemplateOptions.codeStore} nor
617
+ * {@link GguiRenderResourceTemplateOptions.mintWsToken}), the row's
618
+ * generation has not committed a component yet, or a registry
619
+ * blueprint matched but cannot be delivered. Unlike the pair above,
620
+ * this one does NOT vary with the substrate: it is what the
621
+ * caller's OWN row gets on every server, because "this server
622
+ * cannot rehydrate" is not the useful truth for a row that is
623
+ * sitting right there. Reachable only after the access check, or —
624
+ * for the registry case — off the caller's own supplied key, so it
625
+ * discloses nothing either way.
626
+ *
627
+ * A URI matching neither template never reaches any of this: the
628
+ * transport rejects it as invalid params, outside these four codes.
424
629
  *
425
630
  * Returns nothing; mutates the server in place.
426
631
  *
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"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"}