@ggui-ai/mcp-server 0.14.0 → 0.16.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.
- package/README.md +20 -0
- package/dist/api-renders-routes.d.ts +0 -7
- package/dist/api-renders-routes.d.ts.map +1 -1
- package/dist/api-renders-routes.js +5 -5
- package/dist/api-renders-stream-route.js +4 -4
- package/dist/auth.d.ts +3 -3
- package/dist/auth.js +6 -6
- package/dist/build-mcp.d.ts +1 -1
- package/dist/console-blueprint-routes.js +2 -2
- package/dist/console-chat-routes.d.ts +1 -1
- package/dist/console-chat-routes.js +1 -1
- package/dist/console-keys-routes.js +2 -2
- package/dist/console-llm-keys-routes.d.ts +1 -1
- package/dist/console-llm-keys-routes.js +5 -5
- package/dist/console-registry-routes.d.ts +1 -1
- package/dist/console-registry-routes.js +1 -1
- package/dist/console-theme-routes.d.ts +1 -1
- package/dist/console-theme-routes.js +1 -1
- package/dist/control-service.d.ts +3 -3
- package/dist/control-service.js +3 -3
- package/dist/ggui-session-channel/socket-router.js +2 -2
- package/dist/ggui-session-channel/subscribe.d.ts +1 -1
- package/dist/ggui-session-channel/subscribe.d.ts.map +1 -1
- package/dist/ggui-session-channel/subscribe.js +2 -2
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts +1 -1
- package/dist/ggui-session-channel/subscriber-lifecycle.js +1 -1
- package/dist/ggui-session-channel.d.ts +4 -4
- package/dist/ggui-session-channel.js +4 -4
- package/dist/health-routes.d.ts +7 -6
- package/dist/health-routes.d.ts.map +1 -1
- package/dist/health-routes.js +8 -7
- package/dist/index.js +1 -1
- package/dist/instructions-presets.js +5 -5
- package/dist/mcp-apps-outbound.d.ts +17 -11
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +53 -25
- package/dist/mcp-endpoint-routes.d.ts +12 -4
- package/dist/mcp-endpoint-routes.d.ts.map +1 -1
- package/dist/mcp-endpoint-routes.js +89 -30
- package/dist/oauth-as-routes.d.ts +1 -1
- package/dist/oauth.d.ts +3 -3
- package/dist/oauth.js +3 -3
- package/dist/render-read-gate.d.ts +3 -3
- package/dist/render-read-gate.js +2 -2
- package/dist/runtime-bundle-hash.d.ts +1 -1
- package/dist/runtime-bundle-hash.js +1 -1
- package/dist/schema-compat.d.ts +2 -1
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +5 -5
- package/dist/server.d.ts +48 -76
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +29 -41
- package/dist/thread-transport.js +1 -1
- package/package.json +14 -14
|
@@ -51,7 +51,7 @@ const ACTION_ROUTING_PARAGRAPH = "Action routing: every actionSpec entry is a GE
|
|
|
51
51
|
* `always` only — shows the literal handshake → render pattern an
|
|
52
52
|
* operator can copy.
|
|
53
53
|
*/
|
|
54
|
-
const WORKED_EXAMPLE_PARAGRAPH = "Example: ggui_handshake({ intent: 'a settings panel for notification preferences', blueprintDraft: { contract: {...}, variance: { persona: 'minimalist' } } }) returns { handshakeId: 'h_…', suggestion: { origin: 'cache' | 'agent' | 'synth', blueprintMeta: { blueprintId?, contractHash, codeHash?, source?, variance }, amendments?, validationFindings? }, contractHash: '<hex>' }. props is REQUIRED on every render (pass props: {} when the contract declares no propsSpec). ACCEPT PATH: ggui_render({ handshakeId: 'h_…', props: {} }) — OMIT override to reuse the proposed blueprintId verbatim. Read the response's `outcome` first: it is one of `rendered` | `failed` | `refused`. A `rendered` response carries the minted sessionId + the mount URI; a `failed` one carries the sessionId plus an `error` and CONSUMES the handshake; a `refused` one carries only `refusal` ({code, message, fix, retry}) — nothing was committed, the handshake is NOT consumed, and the same handshakeId works again once the fix lands.
|
|
54
|
+
const WORKED_EXAMPLE_PARAGRAPH = "Example: ggui_handshake({ intent: 'a settings panel for notification preferences', blueprintDraft: { contract: {...}, variance: { persona: 'minimalist' } } }) returns { handshakeId: 'h_…', suggestion: { origin: 'cache' | 'agent' | 'synth', blueprintMeta: { blueprintId?, contractHash, codeHash?, source?, variance }, amendments?, validationFindings? }, contractHash: '<hex>' }. props is REQUIRED on every render (pass props: {} when the contract declares no propsSpec). ACCEPT PATH: ggui_render({ handshakeId: 'h_…', props: {} }) — OMIT override to reuse the proposed blueprintId verbatim. Read the response's `outcome` first: it is one of `rendered` | `failed` | `refused`. A `rendered` response carries the minted sessionId + the mount URI; a `failed` one carries the sessionId plus an `error` and CONSUMES the handshake; a `refused` one carries only `refusal` ({code, message, fix, retry, handshake: 'intact'}) — nothing was committed, the handshake is NOT consumed, and the same handshakeId works again once the fix lands. Retry by its `retry` class: an `after-fix` refusal is yours to retry only when its `fixBy` is `caller` (otherwise surface `refusal.fix` — retrying does not perform someone else's fix); a `later` refusal retries after the delay it names; `next-period` and `never` are not retried. OVERRIDE PATH: ggui_render({ handshakeId: 'h_…', props: {}, override: { contract: { ...refined } } }) — STRICT fresh draft against override.contract (server does NOT repair). override: { variance: {...} } instead re-aims the variant (persona/aesthetic/context/seedPrompt) while keeping the agreed contract.";
|
|
55
55
|
/**
|
|
56
56
|
* The default-preset body, captured as a const so `aggressive` and
|
|
57
57
|
* `always` can layer additional paragraphs on top without diverging
|
|
@@ -83,13 +83,13 @@ const DEFAULT_PRESET_BODY = [
|
|
|
83
83
|
' • `ggui_consume`, `ggui_amend`, and `ggui_update` operate on EXISTING sessions identified by `sessionId` and require NO handshake. The `sessionId` was minted by an earlier `ggui_render` call; it identifies the live iframe the user is interacting with. Calling `ggui_handshake` before `ggui_consume`/`ggui_amend`/`ggui_update` is a category error — it would mint a SEPARATE new session that the user can\'t see, orphaning whichever live iframe the gesture targeted.',
|
|
84
84
|
' • Each `sessionId` is the agent\'s persistent handle to one specific live iframe. Reuse it across consume/amend/update for as long as the user is interacting with that UI; only call `ggui_handshake` again when you need to spawn a genuinely new UI surface.',
|
|
85
85
|
'',
|
|
86
|
-
' 1. ggui_handshake — FIRST CALL for any UI. Negotiate a contract for the next render. Post {intent, blueprintDraft: {contract, variance?, generator?}}. `variance` is optional and steers cache lookup + gen by axis: `persona` ("minimalist", "playful"), `aesthetic` ("dense", "spacious"), `seedPrompt` (deterministic seed). HANDSHAKE IS FORGIVING — it
|
|
86
|
+
' 1. ggui_handshake — FIRST CALL for any UI. Negotiate a contract for the next render. Post {intent, blueprintDraft: {contract, variance?, generator?}}. `variance` is optional and steers cache lookup + gen by axis: `persona` ("minimalist", "playful"), `aesthetic` ("dense", "spacious"), `seedPrompt` (deterministic seed). HANDSHAKE IS FORGIVING — the server cache-matches an existing blueprint OR repairs your draft until it passes validation and returns a CONFORMING proposal, with two honest exceptions: `action: \'declined\'` (nothing in your draft passed the contract gate — NO proposal and NO ggui_render for that handshakeId; read `suggestion.validationFindings`, fix the draft, handshake once more) and a `proposedContractSummary` starting with `PARTIAL` (the proposal is your draft MINUS the entries the protocol refused — render it, then re-declare the dropped entries via `override: {contract}` or a corrected re-handshake). The suggestion carries origin: cache (an existing blueprint matched — delivered instantly) | agent (your draft was already clean) | synth (your draft had problems and the server repaired it; `suggestion.validationFindings` lists exactly what was wrong so you learn for next time). CRITICAL: a synth result is NOT an error and NOT a request to retry — the returned contract is READY. Accept it and call ggui_render. Do NOT re-call ggui_handshake hoping for a different origin — that is a loop that wastes turns and changes nothing.',
|
|
87
87
|
'',
|
|
88
|
-
' 2. ggui_render — deliver the UI. handshakeId is REQUIRED (from step 1)
|
|
88
|
+
' 2. ggui_render — deliver the UI. handshakeId is REQUIRED (from step 1): a call without `handshakeId` is rejected at input validation (-32602); an id that does not resolve fails as not found (`handshake_not_found` — call ggui_handshake again). props is ALSO REQUIRED on every call — send {handshakeId, props} (pass props: {} when the contract declares no propsSpec). OMIT `override` to use the handshake suggestion verbatim — THE NORMAL PATH, since the contract is already conforming. To commit to a FRESH draft of your own, add {handshakeId, props, override:{contract:{...}}}. OVERRIDE IS STRICT: a forced override.contract MUST already conform — the server does NOT repair it (override means "use mine exactly"). Alternatively {handshakeId, props, override:{variance:{...}}} re-aims the variant (persona/aesthetic/context/seedPrompt) while keeping the agreed contract. Prefer omitting override; override only when the suggestion genuinely is not what you want AND your replacement is correct. READ `outcome` FIRST — every render response carries it, and it is one of `rendered` | `failed` | `refused`. On `rendered` you get the minted `sessionId` — your handle for every follow-up call — plus the mount URI. On `failed` the sessionId is still there (the error session IS committed) alongside an `error` classification, and the handshake is consumed, so recovery starts at a fresh ggui_handshake. On `refused` the deployment declined BEFORE doing any work: the response carries only `refusal` ({code, message, fix, retry, handshake: \'intact\'}) — no sessionId, no cache, no error — nothing was committed and the handshake is NOT consumed: a `rendered` or `failed` render consumes the handshake; a `refused` render or a recoverable validation error leaves it intact, so the SAME handshakeId still works once the fix lands. Retry by its `retry` class: an `after-fix` refusal is yours to retry only when its `fixBy` is `caller` (otherwise surface `refusal.fix` — retrying does not perform someone else\'s fix); a `later` refusal retries after the delay it names; `next-period` and `never` are not retried. Handshake records are SINGLE-USE and expire. On error: `handshake_not_found` → call ggui_handshake again; `contract_violation` (props don\'t match propsSpec — a missing required prop is `contract_violation` too) → fix props and retry with the SAME handshakeId; `override_contract_invalid` (your override.contract failed validation) → drop override entirely, re-run ggui_handshake with that draft (which WILL repair it), and render the suggestion — do NOT retry override with the same draft.',
|
|
89
89
|
'',
|
|
90
90
|
' 3. NEXT STEP — read the render response. If it carries a `nextStep` field, call that tool with the given args. Render only emits `nextStep` when the contract declared a non-empty actionSpec (i.e., the UI has interactive buttons/forms); in that case nextStep names ggui_consume and your job is to long-poll for the user\'s gesture. If the render response has NO nextStep, the UI is pure-display (props only, no actionSpec) — you can end your turn; the user reads the UI and types their next prompt when they\'re ready.',
|
|
91
91
|
'',
|
|
92
|
-
' 4. ggui_consume (when render said to) — long-poll for user interaction. Keyed by sessionId.
|
|
92
|
+
' 4. ggui_consume (when render said to) — long-poll for user interaction. Keyed by sessionId. `timeout` is an integer in [0, 25] seconds (default 0 — an instant non-blocking drain); pass `timeout: 25` to long-poll up to the server-enforced per-call cap, returning when an actionSpec event arrives or the window lapses. On an empty `events` array you may re-call once, then end your turn — when nobody is polling, a user gesture rings the chat as a new user message carrying its own consume directive. Each return carries `{events, status}`: `events[]` is the discrete action(s) the user just took. Each event is an envelope `{type: "action", sessionId, intent, actionData, uiContext, actionId, firedAt}` — `actionData` is WHAT the user did (action name + validated payload, plus `nextStep` hint if the author declared one); `uiContext` is the iframe-local snapshot of every declared contextSpec slot AT THE MOMENT the action fired (form fields, selected tab, slider value, scroll position — whatever the contract declared). Both inform your reaction without a second round trip. Honor each event\'s `actionData.nextStep` hint if the tool is available, then loop back to step 1 to render the response. Continue until consume returns status:"expired" (the render is gone) or no further follow-up is needed.',
|
|
93
93
|
'',
|
|
94
94
|
' 5. ggui_amend — reflect the new state in the UI, in place. After ANY domain-tool call whose result changed data the rendered UI displays (e.g. `todo_toggle` flips a todo\'s `done`, `cart_add_item` extends a cart, `note_save` persists text), you MUST immediately call `ggui_amend` with the refreshed props so the user sees what just happened. The rendered UI does NOT auto-refresh — it only shows the props it was last given. Skipping this after a state-mutating tool call leaves the user staring at stale state and is the #1 wire bug. Pattern: `consume → domain-tool → ggui_amend → loop to consume`. Two modes: `{sessionId, kind:"replace", props}` sends the FULL new props map (use when most fields changed or you want deterministic restoration); `{sessionId, kind:"merge", patch}` sends ONLY the delta as RFC 7396 JSON Merge Patch (shallow merge, recurse on nested objects, `null` deletes a key, arrays fully replace — use when most props stay the same and only one or two fields changed). Prefer `merge` after a single domain-tool mutation. The only times you skip it: (a) the domain tool was pure-read (todo_list, search, etc.) AND its result wasn\'t for the UI, or (b) the contract has no propsSpec (pure-display with no mutable state). AMEND vs UPDATE vs NEW RENDER: `ggui_amend` repaints the SAME card quietly — no new card, the history number does not advance; it is the default move in gesture loops. `ggui_update` renders the updated state as a NEW card in the conversation and advances the history number — use it for state MILESTONES worth a card in the transcript, or when the original card is no longer visible/usable. When the user\'s gesture asks for a structurally DIFFERENT surface (picking from a menu, opening a detail view, navigating), run ggui_handshake + ggui_render for the next UI instead. If amend/update returns `updated: false` with a warning, your patch changed nothing: either send genuinely different values or render the next surface.',
|
|
95
95
|
'',
|
|
@@ -120,7 +120,7 @@ const DEFAULT_PRESET_BODY = [
|
|
|
120
120
|
'',
|
|
121
121
|
' • ggui_emit — push frames to a streamSpec channel on a delivered UI. Use when the contract declared streamSpec (chat tokens, progress bars, live data). Frames must match the channel\'s declared `schema`. The live channel of the wire carries these.',
|
|
122
122
|
'',
|
|
123
|
-
' • ggui_get_session / ggui_list_sessions — inspect current session state if you\'ve lost track of what\'s on screen. ggui_get_session returns the wire projection — variant, id, appId, eventSequence, createdAt, lastActivityAt, expiresAt — plus `contextSnapshot` (the last-known contextSpec values) when the render has one: the read path for contextSpec values from a raw MCP client. ggui_list_sessions
|
|
123
|
+
' • ggui_get_session / ggui_list_sessions — inspect current session state if you\'ve lost track of what\'s on screen. ggui_get_session returns the wire projection — variant, id, appId, eventSequence, createdAt, lastActivityAt, expiresAt — plus `contextSnapshot` (the last-known contextSpec values) when the render has one: the read path for contextSpec values from a raw MCP client. ggui_list_sessions lists the caller\'s sessions — pass `hostName` + `hostSessionId` (the pair the host set on `_meta["ai.ggui/host-session"]` at render creation) to narrow to one chat conversation.',
|
|
124
124
|
'',
|
|
125
125
|
'═══ HOST RENDERING ═══',
|
|
126
126
|
'',
|
|
@@ -32,7 +32,8 @@
|
|
|
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
|
|
35
|
+
import { type BlueprintDurabilityDeps, type SliceThemeDeps } from "@ggui-ai/mcp-server-handlers/renders";
|
|
36
|
+
import { type FontFaceDeclaration } from "@ggui-ai/design/themes";
|
|
36
37
|
import { type McpAppAiGguiRenderMeta } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
37
38
|
import { type McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
38
39
|
import type { HandlerContext } from "@ggui-ai/mcp-server-handlers";
|
|
@@ -98,7 +99,9 @@ export declare const GGUI_RENDER_SHELL_SCRIPT_HASH: string;
|
|
|
98
99
|
* module-level thin-shell constants (and their pinned CSP hash) are
|
|
99
100
|
* deliberately untouched.
|
|
100
101
|
*/
|
|
101
|
-
export declare function buildInlineRenderShellHtml(runtimeSource: string
|
|
102
|
+
export declare function buildInlineRenderShellHtml(runtimeSource: string, opts?: {
|
|
103
|
+
readonly fontFacesCss?: string;
|
|
104
|
+
}): string;
|
|
102
105
|
/**
|
|
103
106
|
* Swap the served shell's working-state mark for a court's own block
|
|
104
107
|
* (#667's per-deployment knob; guuey#559's blob is the first
|
|
@@ -157,7 +160,7 @@ runtimeUrl?: string,
|
|
|
157
160
|
* the resource cross-origin hosts (claude.ai) mount and derive the
|
|
158
161
|
* frame CSP from, so origins declared only on per-render resources
|
|
159
162
|
* never reach the frame. Without these, deployments that set no
|
|
160
|
-
* `publicBaseUrl` (
|
|
163
|
+
* `publicBaseUrl` (a hosted deployment's posture — the value feeds Origin/Host enforcement)
|
|
161
164
|
* declare only the runtime-CDN origin and every SSE / HTTP-polling /
|
|
162
165
|
* WS rung of the failover ladder is CSP-blocked in the mounted
|
|
163
166
|
* iframe — observed live on claude.ai (#471 round 11: frame booted
|
|
@@ -195,7 +198,7 @@ export declare function advertiseMcpAppsUiCapability(server: McpServer): void;
|
|
|
195
198
|
export interface SelfContainedShellInputs {
|
|
196
199
|
/** GguiSession id whose visible-bits surface is being inlined. */
|
|
197
200
|
readonly sessionId: string;
|
|
198
|
-
/** App
|
|
201
|
+
/** App id the render is scoped to. */
|
|
199
202
|
readonly appId: string;
|
|
200
203
|
/**
|
|
201
204
|
* Content-addressable URL the iframe fetches the compiled ES module
|
|
@@ -428,11 +431,14 @@ export interface GguiRenderResourceTemplateOptions {
|
|
|
428
431
|
* componentCode. */
|
|
429
432
|
readonly renderStore: GguiSessionStore;
|
|
430
433
|
/**
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
* the
|
|
434
|
+
* Font faces the deployment declares (ggui#987 §5 — the theme
|
|
435
|
+
* document's `typography.faces`, handed here by the composer that
|
|
436
|
+
* holds the document). Each face's origin rides
|
|
437
|
+
* `_meta.ui.csp.resourceDomains` on every served shell and the
|
|
438
|
+
* `@font-face` rules are inlined in it, so the faces load under the
|
|
439
|
+
* card's `font-src` without any host cooperation. Absent = no faces.
|
|
434
440
|
*/
|
|
435
|
-
readonly
|
|
441
|
+
readonly fontFaces?: readonly FontFaceDeclaration[];
|
|
436
442
|
/** Absolute URL of the iframe-runtime bundle inlined in the shell. */
|
|
437
443
|
readonly runtimeUrl: string;
|
|
438
444
|
/**
|
|
@@ -580,7 +586,7 @@ export interface GguiRenderResourceTemplateOptions {
|
|
|
580
586
|
* deployment that also keeps no durable record, or `NOT_MOUNTABLE`
|
|
581
587
|
* where the row is the caller's own and simply has no component yet.
|
|
582
588
|
* Deployments serving
|
|
583
|
-
* more than one
|
|
589
|
+
* more than one app from one registry scope are the reason the
|
|
584
590
|
* option exists at all: the fallback discloses whether a blueprint
|
|
585
591
|
* key exists under that scope, so leave it unset if that is not
|
|
586
592
|
* acceptable.
|
|
@@ -649,9 +655,9 @@ export interface GguiRenderResourceTemplateOptions {
|
|
|
649
655
|
* Per-request handler-context accessor — the SAME AsyncLocalStorage
|
|
650
656
|
* read the tool path uses. The per-session resource handler gates
|
|
651
657
|
* reads on it (render-read-gate.ts). Absent ⇒ the handler fails
|
|
652
|
-
* closed for rows scoped to any app other than the single-
|
|
658
|
+
* closed for rows scoped to any app other than the single-app
|
|
653
659
|
* default (compose paths that cannot thread a context keep working
|
|
654
|
-
* for OSS single-
|
|
660
|
+
* for OSS single-app flows only).
|
|
655
661
|
*/
|
|
656
662
|
readonly getContext?: () => HandlerContext | undefined;
|
|
657
663
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAEhB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,
|
|
1
|
+
{"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,7 +31,8 @@
|
|
|
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 { deriveBundleOrigins, deriveContractBundle, derivePublicEnvProjection, deriveRenderMeta,
|
|
34
|
+
import { deriveBundleOrigins, deriveContractBundle, derivePublicEnvProjection, deriveRenderMeta, filterDescriptorsToContract, findBlueprintExact, resolveSliceTheme, spreadRenderMetaViewOntoSlice, wsOriginToHttpOrigin, } from "@ggui-ai/mcp-server-handlers/renders";
|
|
35
|
+
import { fontFaceRules } from "@ggui-ai/design/themes";
|
|
35
36
|
import { GGUI_WAVE_VERSION, RESOURCE_NOT_FOUND_MESSAGE, deriveContextDefault, isRecord, resolveAppGadgets, resourceReadErrorToJsonRpc, } from "@ggui-ai/protocol";
|
|
36
37
|
import { GGUI_RENDER_RESOURCE_MIME, GGUI_RENDER_RESOURCE_URI, GGUI_RENDER_SHELL_SCHEME_STYLE, GGUI_RENDER_SHELL_SURFACE, GGUI_SHELL_LOADING_INDICATOR_HTML, MCP_APPS_UI_CAPABILITY, MCP_APP_BOOTSTRAP_FAILED_TYPE, asGguiRenderBootstrap, composeSessionApiUrls, deriveContextName, escapeInlineScript, gguiShellHtml, parseEpochUri, toMcpAppEnvelope, } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
37
38
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -156,7 +157,7 @@ var lastEnvelope=null;
|
|
|
156
157
|
// own scheme <style> defines (#662) — legible on whichever neutral
|
|
157
158
|
// ground prefers-color-scheme picked. (#481 killed the illegible
|
|
158
159
|
// hardcoded #666; #662 killed the dark-only fallback.)
|
|
159
|
-
var SHELL_FG='var(--ggui-color-
|
|
160
|
+
var SHELL_FG='var(--ggui-color-onGround,var(--ggui-shell-scheme-on-surface,#374151))';
|
|
160
161
|
function setOverlay(text){
|
|
161
162
|
if(mounted)return;
|
|
162
163
|
rootEl.innerHTML='<div style="font:13px system-ui,sans-serif;padding:24px;color:'+SHELL_FG+';opacity:.55">'+text+'</div>';
|
|
@@ -376,7 +377,7 @@ function startInit(){
|
|
|
376
377
|
startInit();
|
|
377
378
|
})();
|
|
378
379
|
`;
|
|
379
|
-
// `--ggui-color-
|
|
380
|
+
// `--ggui-color-ground` is injected at `:root` on this document's
|
|
380
381
|
// `<head>` by the iframe-runtime at boot (react-renderer.ts ->
|
|
381
382
|
// `<style id="ggui-theme-vars">`), so the `var()` resolves to the
|
|
382
383
|
// active theme's exact per-mode surface color at runtime. The static
|
|
@@ -527,7 +528,10 @@ try{
|
|
|
527
528
|
* module-level thin-shell constants (and their pinned CSP hash) are
|
|
528
529
|
* deliberately untouched.
|
|
529
530
|
*/
|
|
530
|
-
export function buildInlineRenderShellHtml(runtimeSource) {
|
|
531
|
+
export function buildInlineRenderShellHtml(runtimeSource, opts) {
|
|
532
|
+
const fontsStyle = opts?.fontFacesCss !== undefined && opts.fontFacesCss !== ""
|
|
533
|
+
? `<style data-ggui-fonts>${opts.fontFacesCss}</style>`
|
|
534
|
+
: "";
|
|
531
535
|
// No anchor div: the runtime appends its own mount target to
|
|
532
536
|
// `document.body` at boot, so a thin-shell-style `#ggui-root`
|
|
533
537
|
// placeholder here would just stack empty space ABOVE the rendered
|
|
@@ -536,7 +540,7 @@ export function buildInlineRenderShellHtml(runtimeSource) {
|
|
|
536
540
|
// no-container posture as `gguiShellHtml`; the shell marker rides
|
|
537
541
|
// on `<body>`.
|
|
538
542
|
return `<!doctype html>
|
|
539
|
-
<html lang="en" style="background-color:${GGUI_RENDER_SHELL_SURFACE}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="color-scheme" content="light dark">${GGUI_RENDER_SHELL_SCHEME_STYLE}<title>ggui render</title
|
|
543
|
+
<html lang="en" style="background-color:${GGUI_RENDER_SHELL_SURFACE}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="color-scheme" content="light dark">${GGUI_RENDER_SHELL_SCHEME_STYLE}<title>ggui render</title>${fontsStyle}</head>
|
|
540
544
|
<body style="margin:0;background-color:${GGUI_RENDER_SHELL_SURFACE}" data-ggui-shell="inline">${GGUI_SHELL_LOADING_INDICATOR_HTML}
|
|
541
545
|
<script>${GGUI_INLINE_SHELL_BUFFER_SCRIPT_BODY}</script>
|
|
542
546
|
<script type="module" data-ggui-runtime="inline">${escapeInlineScript(runtimeSource)}</script></body></html>`;
|
|
@@ -599,7 +603,14 @@ runtimeUrl,
|
|
|
599
603
|
* WebSocket, EventSource, and fetch are all connect-src-governed,
|
|
600
604
|
* and the base's ws-twin flip only covers the base's own host.
|
|
601
605
|
*/
|
|
602
|
-
extraConnectUrls
|
|
606
|
+
extraConnectUrls,
|
|
607
|
+
/**
|
|
608
|
+
* Resource URLs whose ORIGINS must be fetchable from the served
|
|
609
|
+
* shell (ggui#987 §5: every declared font face's `src`). Unioned
|
|
610
|
+
* into `resourceDomains`, deduped — a declared face is admitted by
|
|
611
|
+
* construction, as a gadget's bundle origin is.
|
|
612
|
+
*/
|
|
613
|
+
extraResourceUrls) {
|
|
603
614
|
const source = publicBaseUrl ?? runtimeUrl;
|
|
604
615
|
if (!source)
|
|
605
616
|
return undefined;
|
|
@@ -624,11 +635,23 @@ extraConnectUrls) {
|
|
|
624
635
|
if (!connectDomains.includes(extraOrigin))
|
|
625
636
|
connectDomains.push(extraOrigin);
|
|
626
637
|
}
|
|
638
|
+
const resourceDomains = [origin];
|
|
639
|
+
for (const url of extraResourceUrls ?? []) {
|
|
640
|
+
let extraOrigin;
|
|
641
|
+
try {
|
|
642
|
+
extraOrigin = new URL(url).origin;
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
if (!resourceDomains.includes(extraOrigin))
|
|
648
|
+
resourceDomains.push(extraOrigin);
|
|
649
|
+
}
|
|
627
650
|
return {
|
|
628
651
|
ui: {
|
|
629
652
|
csp: {
|
|
630
653
|
connectDomains,
|
|
631
|
-
resourceDomains
|
|
654
|
+
resourceDomains,
|
|
632
655
|
},
|
|
633
656
|
},
|
|
634
657
|
};
|
|
@@ -637,6 +660,17 @@ extraConnectUrls) {
|
|
|
637
660
|
return undefined;
|
|
638
661
|
}
|
|
639
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* Inline the deployment's `@font-face` rules into a served shell
|
|
665
|
+
* (ggui#987 §5) under ONE `<style data-ggui-fonts>` in `<head>`, so a
|
|
666
|
+
* host that announces no fonts of its own still paints the app's
|
|
667
|
+
* faces. Empty rules leave the shell byte-identical.
|
|
668
|
+
*/
|
|
669
|
+
function withFontFaces(html, fontFacesCss) {
|
|
670
|
+
if (fontFacesCss === "")
|
|
671
|
+
return html;
|
|
672
|
+
return html.replace("</head>", `<style data-ggui-fonts>${fontFacesCss}</style></head>`);
|
|
673
|
+
}
|
|
640
674
|
/**
|
|
641
675
|
* Swap the served shell's working-state mark for a court's own block
|
|
642
676
|
* (#667's per-deployment knob; guuey#559's blob is the first
|
|
@@ -697,7 +731,7 @@ runtimeUrl,
|
|
|
697
731
|
* the resource cross-origin hosts (claude.ai) mount and derive the
|
|
698
732
|
* frame CSP from, so origins declared only on per-render resources
|
|
699
733
|
* never reach the frame. Without these, deployments that set no
|
|
700
|
-
* `publicBaseUrl` (
|
|
734
|
+
* `publicBaseUrl` (a hosted deployment's posture — the value feeds Origin/Host enforcement)
|
|
701
735
|
* declare only the runtime-CDN origin and every SSE / HTTP-polling /
|
|
702
736
|
* WS rung of the failover ladder is CSP-blocked in the mounted
|
|
703
737
|
* iframe — observed live on claude.ai (#471 round 11: frame booted
|
|
@@ -1200,7 +1234,7 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1200
1234
|
const legacyTemplate = new ResourceTemplate(`${GGUI_RENDER_RESOURCE_URI}/{sessionId}`, {
|
|
1201
1235
|
// No list-callback — the resource set is unbounded per render
|
|
1202
1236
|
// count, and `resources/list` would leak render ids across
|
|
1203
|
-
//
|
|
1237
|
+
// apps. Hosts discover specific URIs via per-call `_meta.ui.
|
|
1204
1238
|
// resourceUri` instead.
|
|
1205
1239
|
list: undefined,
|
|
1206
1240
|
});
|
|
@@ -1213,7 +1247,11 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1213
1247
|
// fails with a generic "script error" since cross-origin script
|
|
1214
1248
|
// loading is blocked. Same shape declared on the static
|
|
1215
1249
|
// `ui://ggui/render` resource; this is the per-call mirror.
|
|
1216
|
-
|
|
1250
|
+
// ggui#987 §5: the declared faces' origins ride every CSP meta this
|
|
1251
|
+
// registration emits, and their rules ride every served shell.
|
|
1252
|
+
const faceSrcs = (opts.fontFaces ?? []).map((face) => face.src);
|
|
1253
|
+
const fontFacesCss = opts.fontFaces !== undefined && opts.fontFaces.length > 0 ? fontFaceRules(opts.fontFaces) : "";
|
|
1254
|
+
const templateCspMeta = buildCspMeta(opts.publicBaseUrl, opts.runtimeUrl, undefined, faceSrcs);
|
|
1217
1255
|
/**
|
|
1218
1256
|
* Merge gadget-declared origins from
|
|
1219
1257
|
* {@link deriveBundleOrigins} into the base `templateCspMeta`. The
|
|
@@ -1628,13 +1666,7 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1628
1666
|
// (the browser-enforced gate ultimately comes from the host's
|
|
1629
1667
|
// `allow=""` attribute when the host translates
|
|
1630
1668
|
// `_meta.ui.permissions` — set by McpAppIframe consumers).
|
|
1631
|
-
|
|
1632
|
-
// no appId AND project an empty view (no theme), so the enrichment
|
|
1633
|
-
// is definitionally a no-op there — skip rather than fake an id.
|
|
1634
|
-
const baseView = deriveRenderMeta(picked.source);
|
|
1635
|
-
const view = picked.source.type !== "mcpApps"
|
|
1636
|
-
? await withResolvedThemeBase(baseView, opts, picked.source.appId)
|
|
1637
|
-
: baseView;
|
|
1669
|
+
const view = deriveRenderMeta(picked.source);
|
|
1638
1670
|
const isSystem = picked.kind !== undefined;
|
|
1639
1671
|
// A fault on EITHER delivery channel, held rather than acted on.
|
|
1640
1672
|
//
|
|
@@ -1710,7 +1742,7 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1710
1742
|
// (wsUrl + wsToken) instead of failing as undeliverable —
|
|
1711
1743
|
// parity with the `/r/<shortCode>` path. (See the gate after the
|
|
1712
1744
|
// mint.) This matters for deployments that wire `mintWsToken` but no
|
|
1713
|
-
// `codeStore`/`codeBaseUrl` (e.g.
|
|
1745
|
+
// `codeStore`/`codeBaseUrl` (e.g. a hosted deployment): the agent-server
|
|
1714
1746
|
// inlines THIS resource, so without live-mode every render on such a
|
|
1715
1747
|
// deployment would resolve fine and then be reported as having no
|
|
1716
1748
|
// way to be delivered.
|
|
@@ -1766,7 +1798,7 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1766
1798
|
// when the mint above produced a token (both URLs embed it); base
|
|
1767
1799
|
// = publicBaseUrl when configured, else the ws→http origin flip
|
|
1768
1800
|
// of the minted wsUrl (session API served on the WS origin — OSS
|
|
1769
|
-
// defaults +
|
|
1801
|
+
// defaults + a hosted deployment's single ingress).
|
|
1770
1802
|
let sessionApiUrls;
|
|
1771
1803
|
if (wsToken !== undefined) {
|
|
1772
1804
|
const base = opts.publicBaseUrl ?? (wsUrl !== undefined ? wsOriginToHttpOrigin(wsUrl) : undefined);
|
|
@@ -1887,13 +1919,9 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1887
1919
|
// capstone's CSP block). Same-origin stamps dedupe to the
|
|
1888
1920
|
// registration-time declaration.
|
|
1889
1921
|
const renderCspBase = sessionApiUrls !== undefined || wsUrl !== undefined
|
|
1890
|
-
? buildCspMeta(opts.publicBaseUrl, opts.runtimeUrl, [
|
|
1891
|
-
wsUrl,
|
|
1892
|
-
sessionApiUrls?.sseUrl,
|
|
1893
|
-
sessionApiUrls?.pollingUrl,
|
|
1894
|
-
])
|
|
1922
|
+
? buildCspMeta(opts.publicBaseUrl, opts.runtimeUrl, [wsUrl, sessionApiUrls?.sseUrl, sessionApiUrls?.pollingUrl], faceSrcs)
|
|
1895
1923
|
: templateCspMeta;
|
|
1896
|
-
return shellContents(uri, html, augmentCspMeta(gadgetOrigins, renderCspBase));
|
|
1924
|
+
return shellContents(uri, withFontFaces(html, fontFacesCss), augmentCspMeta(gadgetOrigins, renderCspBase));
|
|
1897
1925
|
}
|
|
1898
1926
|
/**
|
|
1899
1927
|
* Reconstruct the props of history record `#epoch` from the event
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* POST <universalMcpPath> — DATA PLANE: agent+runtime tools
|
|
5
5
|
* (default `/mcp`)
|
|
6
|
-
* POST <pathPrefix>/:appId — data plane, per-
|
|
6
|
+
* POST <pathPrefix>/:appId — data plane, per-app variant
|
|
7
7
|
* (opt-in via `perAppRouting`)
|
|
8
8
|
* POST /control — CONTROL PLANE: design-time
|
|
9
9
|
* spec/discovery (anonymous) +
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* (`agent` / `runtime` / `protocol` / `ops`) and the wire-name prefix
|
|
32
32
|
* rules.
|
|
33
33
|
*/
|
|
34
|
+
import { type JsonValue } from "@ggui-ai/protocol";
|
|
34
35
|
import type { AuthAdapter, AuthResult } from "@ggui-ai/mcp-server-core";
|
|
35
36
|
import type { HandlerContext, SharedHandler } from "@ggui-ai/mcp-server-handlers";
|
|
36
37
|
import type { Express } from "express";
|
|
@@ -39,7 +40,7 @@ import type { ZodRawShape } from "zod";
|
|
|
39
40
|
import { type BuildMcpServerOptions, type ServerInfo } from "./build-mcp.js";
|
|
40
41
|
import type { Logger } from "./logger.js";
|
|
41
42
|
import type { McpService } from "./mcp-mounts.js";
|
|
42
|
-
/** Per-
|
|
43
|
+
/** Per-app URL routing shape — mirrors `CreateGguiServerOptions.perAppRouting`. */
|
|
43
44
|
interface PerAppRouting {
|
|
44
45
|
readonly paramName: string;
|
|
45
46
|
readonly paramPattern: string;
|
|
@@ -61,6 +62,13 @@ export interface ErrorMapperResult {
|
|
|
61
62
|
readonly message: string;
|
|
62
63
|
/** Response headers to set before the JSON body is written. */
|
|
63
64
|
readonly headers?: Readonly<Record<string, string>>;
|
|
65
|
+
/**
|
|
66
|
+
* JSON-RPC 2.0 error `data` — any JSON value the deployment wants the
|
|
67
|
+
* client to read alongside `code` / `message` (a structured reason, a
|
|
68
|
+
* retry hint). Serialized verbatim on `error.data`; omitted from the
|
|
69
|
+
* body when absent, so a mapper that never sets it changes nothing.
|
|
70
|
+
*/
|
|
71
|
+
readonly data?: JsonValue;
|
|
64
72
|
}
|
|
65
73
|
interface MountOptions {
|
|
66
74
|
/** Express app to mount onto. */
|
|
@@ -92,9 +100,9 @@ interface MountOptions {
|
|
|
92
100
|
readonly als: AsyncLocalStorage<HandlerContext>;
|
|
93
101
|
/** Identity → appId resolution rule (SPEC §12.2). */
|
|
94
102
|
readonly appIdFromIdentity: (result: AuthResult) => string;
|
|
95
|
-
/** Universal endpoint path (default `/mcp`;
|
|
103
|
+
/** Universal endpoint path (default `/mcp`; a deployment may serve it at `/`). */
|
|
96
104
|
readonly universalMcpPath: string;
|
|
97
|
-
/** Per-
|
|
105
|
+
/** Per-app endpoint shape — absent = universal-only deployment. */
|
|
98
106
|
readonly perAppRouting?: PerAppRouting;
|
|
99
107
|
/** Whether OAuth is enabled (adds `WWW-Authenticate` on 401). */
|
|
100
108
|
readonly oauthEnabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-endpoint-routes.d.ts","sourceRoot":"","sources":["../src/mcp-endpoint-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;
|
|
1
|
+
{"version":3,"file":"mcp-endpoint-routes.d.ts","sourceRoot":"","sources":["../src/mcp-endpoint-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElF,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEvC,OAAO,EAAkB,KAAK,qBAAqB,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAM7F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,mFAAmF;AACnF,UAAU,aAAa;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;CAC3B;AA2CD,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IAC1E;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IACjF;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClD,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAChD,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAChD,qDAAqD;IACrD,QAAQ,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,CAAC;IAC3D,kFAAkF;IAClF,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,mEAAmE;IACnE,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,uDAAuD;IACvD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,iBAAiB,GAAG,SAAS,CAAC;IACvE;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC;CACjD;AAyBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAob1D"}
|