@guuey/mcp-apps-host 0.10.0 → 0.12.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/dist/card-mount.d.ts +11 -0
- package/dist/card-mount.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -0
- package/dist/reader.d.ts +52 -4
- package/dist/reader.d.ts.map +1 -1
- package/dist/reader.js +62 -3
- package/dist/view-host-protocol.d.ts +24 -0
- package/dist/view-host-protocol.d.ts.map +1 -1
- package/dist/view-host-protocol.js +24 -0
- package/dist/view-host.d.ts +14 -0
- package/dist/view-host.d.ts.map +1 -1
- package/dist/view-host.js +14 -0
- package/package.json +2 -2
- package/src/card-mount.ts +11 -0
- package/src/index.ts +5 -0
- package/src/react.tsx +8 -0
- package/src/reader.ts +48 -5
- package/src/view-host-protocol.ts +40 -0
- package/src/view-host.ts +25 -0
package/dist/card-mount.d.ts
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
* from the response and never from mount material a producer inlined.
|
|
42
42
|
*/
|
|
43
43
|
import { type McpUiResourcePayload } from "./block-ui.js";
|
|
44
|
+
import type { McpUiResourceCsp } from "@modelcontextprotocol/ext-apps";
|
|
44
45
|
import type { AgBlock, JsonValue } from "@silverprotocol/core";
|
|
45
46
|
/**
|
|
46
47
|
* Which sandbox-trust channel a mount rides. See this module's header.
|
|
@@ -77,6 +78,16 @@ export interface ResolvedViewMount {
|
|
|
77
78
|
channel: "inline" | "ggui";
|
|
78
79
|
/** The payload an mcp-ui host mounts, identical in shape for both channels. */
|
|
79
80
|
resource: McpUiResourcePayload;
|
|
81
|
+
/**
|
|
82
|
+
* The server's per-resource CSP declaration (`_meta.ui.csp` on the
|
|
83
|
+
* `resources/read` result — MCP Apps SEP-1865; guuey#312), when the read
|
|
84
|
+
* path saw one. Spec-schema-validated at the reader
|
|
85
|
+
* (`declaredResourceCsp`) — never fabricated, absent = undeclared. This
|
|
86
|
+
* is the generic successor to the uri-prefix channel heuristic: hosts
|
|
87
|
+
* derive per-card sandbox CSP / tripwire filters from it
|
|
88
|
+
* (`<GuueyView>` defaults its `cspOrigins` from here).
|
|
89
|
+
*/
|
|
90
|
+
csp?: McpUiResourceCsp;
|
|
80
91
|
}
|
|
81
92
|
/** The durable-identity arm: no mount material, only the uri to re-fetch. */
|
|
82
93
|
export interface LocatorViewMount {
|
package/dist/card-mount.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-mount.d.ts","sourceRoot":"","sources":["../src/card-mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAA+D,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACvH,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,QAAQ,EAAE,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"card-mount.d.ts","sourceRoot":"","sources":["../src/card-mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAA+D,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACvH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,gBAAgB,CAAC;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,SAAS,CAAC;IACnB,4GAA4G;IAC5G,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;AAEvF;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,GAC/C,SAAS,GAAG,SAAS,CAKvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAUhF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAKxC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { asResourcePayload, asUiResource, blockUiResource, isJsonObject, resourceHtml, scanProviderRawForUiResource, snapshotUiResource, toolResultLocator, toolResultUiResource, uiLocator, type McpUiResourcePayload, } from "./block-ui.js";
|
|
7
7
|
export { resolveViewMount, snapshotViewMount, toolResultViewMount, type LocatorViewMount, type ResolvedViewMount, type UiResourceReader, type ViewMount, type ViewMountChannel, } from "./card-mount.js";
|
|
8
|
-
export { createMcpUiResourceReader, uiResourceChannel, type CreateMcpUiResourceReaderDeps, type McpResourceReadResult, } from "./reader.js";
|
|
8
|
+
export { createMcpUiResourceReader, declaredResourceCsp, uiResourceChannel, type CreateMcpUiResourceReaderDeps, type McpResourceReadResult, } from "./reader.js";
|
|
9
|
+
export type { McpUiResourceCsp } from "@modelcontextprotocol/ext-apps";
|
|
9
10
|
export { asToolCallResult, createMcpUiActionRelay, unavailableToolCallResult, UI_ACTION_TOOLS, UI_ACTION_UNAVAILABLE_TEXT, UI_SEMANTIC_ACTION_TOOLS, type CreateMcpUiActionRelayDeps, type McpToolCallContent, type McpToolCallResult, type McpToolStructuredContent, type UiActionRequest, } from "./action.js";
|
|
10
11
|
export { initializeResult, initialViewHostState, resourceReadResponse, RESOURCES_READ_METHOD, teardownMessage, toolCallResponse, TOOLS_CALL_METHOD, viewHostElapsed, viewHostReceive, diagnoseCspViolation, type CspViolationLike, type ViewCspDiagnosis, type ViewCspOrigins, type ViewHostBehavior, type ViewHostEffect, type ViewHostOutbound, type ViewHostPhase, type ViewHostInfo, type ViewHostState, type ViewHostTransition, type ViewRequestId, } from "./view-host-protocol.js";
|
|
11
12
|
export { attachViewHost, viewDocumentHtml, type AttachViewHostConfig, type ViewCspEvents, type ViewFrameLike, type ViewHostEvents, } from "./view-host.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,SAAS,EACT,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,SAAS,EACT,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AAIrB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,0BAA0B,EAC1B,wBAAwB,EACxB,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { asResourcePayload, asUiResource, blockUiResource, isJsonObject, resourceHtml, scanProviderRawForUiResource, snapshotUiResource, toolResultLocator, toolResultUiResource, uiLocator, } from "./block-ui.js";
|
|
7
7
|
export { resolveViewMount, snapshotViewMount, toolResultViewMount, } from "./card-mount.js";
|
|
8
|
-
export { createMcpUiResourceReader, uiResourceChannel, } from "./reader.js";
|
|
8
|
+
export { createMcpUiResourceReader, declaredResourceCsp, uiResourceChannel, } from "./reader.js";
|
|
9
9
|
export { asToolCallResult, createMcpUiActionRelay, unavailableToolCallResult, UI_ACTION_TOOLS, UI_ACTION_UNAVAILABLE_TEXT, UI_SEMANTIC_ACTION_TOOLS, } from "./action.js";
|
|
10
10
|
export { initializeResult, initialViewHostState, resourceReadResponse, RESOURCES_READ_METHOD, teardownMessage, toolCallResponse, TOOLS_CALL_METHOD, viewHostElapsed, viewHostReceive, diagnoseCspViolation, } from "./view-host-protocol.js";
|
|
11
11
|
export { attachViewHost, viewDocumentHtml, } from "./view-host.js";
|
package/dist/react.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export type { AttachViewHostConfig, ViewCspEvents, ViewFrameLike, ViewHostEvents
|
|
|
41
41
|
export { attachSandboxPageDelivery, isSandboxProxyReady, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, type SandboxPageDeliveryConfig, } from "./sandbox-page.js";
|
|
42
42
|
export type { ViewCspDiagnosis, ViewCspOrigins, ViewHostPhase } from "./view-host-protocol.js";
|
|
43
43
|
export type { ResolvedViewMount, ViewMount, ViewMountChannel } from "./card-mount.js";
|
|
44
|
-
export interface GuueyViewProps extends Pick<AttachViewHostConfig, "hostCapabilities" | "hostInfo" | "hostContext" | "onCallTool" | "onReadResource" | "onSizeChanged" | "negotiationTimeoutMs" | "cspOrigins" | "onCspDiagnosis"> {
|
|
44
|
+
export interface GuueyViewProps extends Pick<AttachViewHostConfig, "hostCapabilities" | "hostInfo" | "hostContext" | "onCallTool" | "onUpdateModelContext" | "onReadResource" | "onSizeChanged" | "negotiationTimeoutMs" | "cspOrigins" | "onCspDiagnosis"> {
|
|
45
45
|
/** The resolved card to mount (see `toolResultViewMount`/`resolveViewMount`). */
|
|
46
46
|
mount: ResolvedViewMount;
|
|
47
47
|
/**
|
package/dist/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAwC,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjG,OAAO,EAAoC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACzG,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/F,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKtF,MAAM,WAAW,cACf,SAAQ,IAAI,CACV,oBAAoB,EAClB,kBAAkB,GAClB,UAAU,GACV,aAAa,GACb,YAAY,GACZ,gBAAgB,GAChB,eAAe,GACf,sBAAsB,GACtB,YAAY,GACZ,gBAAgB,CACnB;IACD,iFAAiF;IACjF,KAAK,EAAE,iBAAiB,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,gBAAgB,KAAK,SAAS,CAAC;CAClF;AA8CD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAwC,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjG,OAAO,EAAoC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACzG,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/F,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKtF,MAAM,WAAW,cACf,SAAQ,IAAI,CACV,oBAAoB,EAClB,kBAAkB,GAClB,UAAU,GACV,aAAa,GACb,YAAY,GACZ,sBAAsB,GACtB,gBAAgB,GAChB,eAAe,GACf,sBAAsB,GACtB,YAAY,GACZ,gBAAgB,CACnB;IACD,iFAAiF;IACjF,KAAK,EAAE,iBAAiB,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,gBAAgB,KAAK,SAAS,CAAC;CAClF;AA8CD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CA0K1D"}
|
package/dist/react.js
CHANGED
|
@@ -124,8 +124,15 @@ export function GuueyView(props) {
|
|
|
124
124
|
if (sandboxPageUrl !== undefined && page === undefined)
|
|
125
125
|
return; // refused config — nothing mounts
|
|
126
126
|
const resourceUri = mount.resource.uri;
|
|
127
|
+
// guuey#312: the resolved mount's DECLARED per-resource CSP
|
|
128
|
+
// (`_meta.ui.csp`, spec-schema-validated at the reader) is the CSP
|
|
129
|
+
// tripwire's default filter — the declaration finally doing host-side
|
|
130
|
+
// work. An explicit `cspOrigins` prop always wins; both absent keeps
|
|
131
|
+
// the tripwire inert exactly as before.
|
|
132
|
+
const cspOrigins = latest.current.hostConfig.cspOrigins ?? mount.csp;
|
|
127
133
|
const detachHost = attachViewHost(frame, {
|
|
128
134
|
...latest.current.hostConfig,
|
|
135
|
+
...(cspOrigins !== undefined ? { cspOrigins } : {}),
|
|
129
136
|
resourceUri,
|
|
130
137
|
onPhaseChange: (next) => {
|
|
131
138
|
setPhase(next);
|
package/dist/reader.d.ts
CHANGED
|
@@ -1,10 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generic SEP-1865 locator reader (guuey#127): resolve a persisted
|
|
3
|
+
* `ui://` locator ({@link LocatorViewMount}) into a mountable
|
|
4
|
+
* {@link ResolvedViewMount} by ONE fresh `resources/read` over a transport
|
|
5
|
+
* the HOST supplies.
|
|
6
|
+
*
|
|
7
|
+
* The transport is a structural callable — deliberately no
|
|
8
|
+
* `@modelcontextprotocol/sdk` dependency — because hosts hold wildly
|
|
9
|
+
* different connections: a raw MCP client (ggui's `with-guuey-web` sample
|
|
10
|
+
* keeps one for guest `tools/call` relay), an authenticated platform proxy
|
|
11
|
+
* (`@guuey/agent-client`'s `createUiResourceReader` is exactly this assembly
|
|
12
|
+
* over guuey's `GET /v1/threads/:threadId/ui-resource` route), or anything
|
|
13
|
+
* else that can perform one read. The host adapts whatever client it holds.
|
|
14
|
+
*
|
|
15
|
+
* Trust rules, identical to the platform reader and now in ONE place:
|
|
16
|
+
*
|
|
17
|
+
* - a transport deny, a miss, and a thrown transport error are all
|
|
18
|
+
* `undefined` → the host renders its placeholder, never an error surface
|
|
19
|
+
* (deny == miss — no oracle for which locators resolve);
|
|
20
|
+
* - enforcement (user-ownership, tenancy, credential handling) belongs
|
|
21
|
+
* INSIDE the transport, before any bytes come back — this assembly never
|
|
22
|
+
* sees a credential;
|
|
23
|
+
* - the sandbox-trust channel derives from the REQUESTED locator uri, never
|
|
24
|
+
* from the response — a server answering with a foreign `ui://ggui/…` uri
|
|
25
|
+
* must not steer its HTML into the ggui-CSP host page.
|
|
26
|
+
*/
|
|
27
|
+
import { type McpUiResourceCsp } from "@modelcontextprotocol/ext-apps";
|
|
1
28
|
import type { ResolvedViewMount, ViewMountChannel } from "./card-mount.js";
|
|
2
29
|
/**
|
|
3
30
|
* One `resources/read` `contents[]` entry, structurally (SEP-1865). Kept a
|
|
4
31
|
* separate type from {@link McpUiResourcePayload} even though the shapes
|
|
5
32
|
* coincide today: this is the UNVALIDATED wire-side entry a transport hands
|
|
6
|
-
* over
|
|
7
|
-
*
|
|
33
|
+
* over, while the payload is the narrowed thing a host mounts. `_meta` is
|
|
34
|
+
* phase 2's growth (guuey#312, conformance map step 3): the per-resource
|
|
35
|
+
* declarations (`_meta.ui.csp` — MCP Apps SEP-1865) ride here when the
|
|
36
|
+
* transport forwards them; {@link declaredResourceCsp} is the ONLY door
|
|
37
|
+
* from this open extension map into typed material.
|
|
8
38
|
*/
|
|
9
39
|
export interface McpResourceReadResult {
|
|
10
40
|
uri: string;
|
|
@@ -12,14 +42,32 @@ export interface McpResourceReadResult {
|
|
|
12
42
|
text?: string;
|
|
13
43
|
/** Base64 payload arm — hosts decode via {@link resourceHtml}. */
|
|
14
44
|
blob?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The entry's `_meta` as the transport saw it — an open extension map,
|
|
47
|
+
* so genuinely unknown-shaped at this boundary; never read it directly,
|
|
48
|
+
* narrow through {@link declaredResourceCsp}.
|
|
49
|
+
*/
|
|
50
|
+
_meta?: unknown;
|
|
15
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The server's per-resource CSP declaration from a read result's
|
|
54
|
+
* `_meta.ui.csp` (guuey#312 — the phase-2 channel `uiResourceChannel`'s
|
|
55
|
+
* docs promised). Validated against the SPEC's own schema
|
|
56
|
+
* (`McpUiResourceCspSchema` from `@modelcontextprotocol/ext-apps`), never
|
|
57
|
+
* a hand-rolled mirror. Malformed or absent → `undefined` — an undeclared
|
|
58
|
+
* resource is the secure default (consumers only ever use a declaration to
|
|
59
|
+
* build allowances; absence widens nothing).
|
|
60
|
+
*/
|
|
61
|
+
export declare function declaredResourceCsp(entry: McpResourceReadResult): McpUiResourceCsp | undefined;
|
|
16
62
|
/**
|
|
17
63
|
* Phase-1 sandbox-trust channel resolution (conformance map, retirement
|
|
18
64
|
* step 3): a `ui://ggui/…` locator mounts through the ggui-CSP host page
|
|
19
65
|
* (`"ggui"` — the shell boots ggui's runtime bundle and opens its WSS),
|
|
20
66
|
* anything else through the self-only page (`"inline"` — arbitrary tenant
|
|
21
|
-
* HTML).
|
|
22
|
-
*
|
|
67
|
+
* HTML). The declaration itself ships (guuey#312 — `ResolvedViewMount.csp`
|
|
68
|
+
* via {@link declaredResourceCsp}); this heuristic retires when hosts route
|
|
69
|
+
* SANDBOX TRUST from that declaration too (per-card CSP construction, the
|
|
70
|
+
* conformance map's step-3 second half). Until then the uri prefix is the only
|
|
23
71
|
* signal a host has BEFORE fetching mount material.
|
|
24
72
|
*/
|
|
25
73
|
export declare function uiResourceChannel(resourceUri: string): Exclude<ViewMountChannel, "locator">;
|
package/dist/reader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../src/reader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../src/reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAE/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE3E;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,qBAAqB,GAAG,gBAAgB,GAAG,SAAS,CAW9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAE3F;AAED,oFAAoF;AACpF,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAAC;CAC3E;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,6BAA6B,GAClC,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CA+BjE"}
|
package/dist/reader.js
CHANGED
|
@@ -1,10 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generic SEP-1865 locator reader (guuey#127): resolve a persisted
|
|
3
|
+
* `ui://` locator ({@link LocatorViewMount}) into a mountable
|
|
4
|
+
* {@link ResolvedViewMount} by ONE fresh `resources/read` over a transport
|
|
5
|
+
* the HOST supplies.
|
|
6
|
+
*
|
|
7
|
+
* The transport is a structural callable — deliberately no
|
|
8
|
+
* `@modelcontextprotocol/sdk` dependency — because hosts hold wildly
|
|
9
|
+
* different connections: a raw MCP client (ggui's `with-guuey-web` sample
|
|
10
|
+
* keeps one for guest `tools/call` relay), an authenticated platform proxy
|
|
11
|
+
* (`@guuey/agent-client`'s `createUiResourceReader` is exactly this assembly
|
|
12
|
+
* over guuey's `GET /v1/threads/:threadId/ui-resource` route), or anything
|
|
13
|
+
* else that can perform one read. The host adapts whatever client it holds.
|
|
14
|
+
*
|
|
15
|
+
* Trust rules, identical to the platform reader and now in ONE place:
|
|
16
|
+
*
|
|
17
|
+
* - a transport deny, a miss, and a thrown transport error are all
|
|
18
|
+
* `undefined` → the host renders its placeholder, never an error surface
|
|
19
|
+
* (deny == miss — no oracle for which locators resolve);
|
|
20
|
+
* - enforcement (user-ownership, tenancy, credential handling) belongs
|
|
21
|
+
* INSIDE the transport, before any bytes come back — this assembly never
|
|
22
|
+
* sees a credential;
|
|
23
|
+
* - the sandbox-trust channel derives from the REQUESTED locator uri, never
|
|
24
|
+
* from the response — a server answering with a foreign `ui://ggui/…` uri
|
|
25
|
+
* must not steer its HTML into the ggui-CSP host page.
|
|
26
|
+
*/
|
|
27
|
+
import { McpUiResourceCspSchema } from "@modelcontextprotocol/ext-apps";
|
|
28
|
+
/**
|
|
29
|
+
* The server's per-resource CSP declaration from a read result's
|
|
30
|
+
* `_meta.ui.csp` (guuey#312 — the phase-2 channel `uiResourceChannel`'s
|
|
31
|
+
* docs promised). Validated against the SPEC's own schema
|
|
32
|
+
* (`McpUiResourceCspSchema` from `@modelcontextprotocol/ext-apps`), never
|
|
33
|
+
* a hand-rolled mirror. Malformed or absent → `undefined` — an undeclared
|
|
34
|
+
* resource is the secure default (consumers only ever use a declaration to
|
|
35
|
+
* build allowances; absence widens nothing).
|
|
36
|
+
*/
|
|
37
|
+
export function declaredResourceCsp(entry) {
|
|
38
|
+
const meta = entry._meta;
|
|
39
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta) || !("ui" in meta)) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const ui = meta.ui;
|
|
43
|
+
if (typeof ui !== "object" || ui === null || Array.isArray(ui) || !("csp" in ui)) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const parsed = McpUiResourceCspSchema.safeParse(ui.csp);
|
|
47
|
+
return parsed.success ? parsed.data : undefined;
|
|
48
|
+
}
|
|
1
49
|
/**
|
|
2
50
|
* Phase-1 sandbox-trust channel resolution (conformance map, retirement
|
|
3
51
|
* step 3): a `ui://ggui/…` locator mounts through the ggui-CSP host page
|
|
4
52
|
* (`"ggui"` — the shell boots ggui's runtime bundle and opens its WSS),
|
|
5
53
|
* anything else through the self-only page (`"inline"` — arbitrary tenant
|
|
6
|
-
* HTML).
|
|
7
|
-
*
|
|
54
|
+
* HTML). The declaration itself ships (guuey#312 — `ResolvedViewMount.csp`
|
|
55
|
+
* via {@link declaredResourceCsp}); this heuristic retires when hosts route
|
|
56
|
+
* SANDBOX TRUST from that declaration too (per-card CSP construction, the
|
|
57
|
+
* conformance map's step-3 second half). Until then the uri prefix is the only
|
|
8
58
|
* signal a host has BEFORE fetching mount material.
|
|
9
59
|
*/
|
|
10
60
|
export function uiResourceChannel(resourceUri) {
|
|
@@ -40,6 +90,15 @@ export function createMcpUiResourceReader(deps) {
|
|
|
40
90
|
...(typeof entry.text === "string" ? { text: entry.text } : {}),
|
|
41
91
|
...(typeof entry.blob === "string" ? { blob: entry.blob } : {}),
|
|
42
92
|
};
|
|
43
|
-
|
|
93
|
+
// guuey#312: the per-resource CSP declaration rides the resolved mount
|
|
94
|
+
// when the wire carried one — hosts build per-card allowances from the
|
|
95
|
+
// DECLARATION, never from the uri prefix. Absent = undeclared (secure
|
|
96
|
+
// default); never fabricated.
|
|
97
|
+
const csp = declaredResourceCsp(entry);
|
|
98
|
+
return {
|
|
99
|
+
channel: uiResourceChannel(resourceUri),
|
|
100
|
+
resource,
|
|
101
|
+
...(csp !== undefined ? { csp } : {}),
|
|
102
|
+
};
|
|
44
103
|
};
|
|
45
104
|
}
|
|
@@ -45,6 +45,15 @@ export interface ViewHostInfo {
|
|
|
45
45
|
}
|
|
46
46
|
/** The standard MCP method a view uses to reach host-proxied tools. */
|
|
47
47
|
export declare const TOOLS_CALL_METHOD = "tools/call";
|
|
48
|
+
/**
|
|
49
|
+
* The spec method a view uses to push a model-context snapshot at its host
|
|
50
|
+
* (`McpUiUpdateModelContextRequest`). A local constant — the ext-apps root
|
|
51
|
+
* exports the TYPE but no method constant for it (guuey#335). Producers
|
|
52
|
+
* (ggui context-observer) fire it on slot changes as a COMPLEMENT to the
|
|
53
|
+
* action path; the choice itself always rides tools/call or the live
|
|
54
|
+
* channel ("Actions drive turns; context observes state").
|
|
55
|
+
*/
|
|
56
|
+
export declare const UPDATE_MODEL_CONTEXT_METHOD = "ui/update-model-context";
|
|
48
57
|
/**
|
|
49
58
|
* The standard MCP method a view uses to read host-proxied resources —
|
|
50
59
|
* `ReadResourceRequest` in the spec's App→Host request union
|
|
@@ -128,6 +137,19 @@ export type ViewHostEffect = {
|
|
|
128
137
|
kind: "relay-resource-read";
|
|
129
138
|
id: ViewRequestId;
|
|
130
139
|
uri: string;
|
|
140
|
+
} | {
|
|
141
|
+
/**
|
|
142
|
+
* A `ui/update-model-context` snapshot to hand the embedder
|
|
143
|
+
* (guuey#335). The machine already ANSWERED the view (success — the
|
|
144
|
+
* spec result is empty), so this effect is pure delivery: a throwing
|
|
145
|
+
* sink can never leave the view hanging. Only emitted when
|
|
146
|
+
* {@link ViewHostBehavior.modelContextSink} is true — unwired, the
|
|
147
|
+
* machine refuses in-band like every other unsupported request.
|
|
148
|
+
*/
|
|
149
|
+
kind: "model-context-update";
|
|
150
|
+
params: {
|
|
151
|
+
[key: string]: unknown;
|
|
152
|
+
};
|
|
131
153
|
} | {
|
|
132
154
|
/**
|
|
133
155
|
* The view reported its content size (`ui/notifications/size-changed`
|
|
@@ -161,6 +183,8 @@ export interface ViewHostBehavior {
|
|
|
161
183
|
hostContext: McpUiHostContext;
|
|
162
184
|
/** Whether a `tools/call` relay hook is wired (see `view-host.ts`). */
|
|
163
185
|
toolRelay: boolean;
|
|
186
|
+
/** Whether a model-context sink is wired (see `view-host.ts`, guuey#335). */
|
|
187
|
+
modelContextSink: boolean;
|
|
164
188
|
/** Whether a `resources/read` relay hook is wired (see `view-host.ts`). */
|
|
165
189
|
resourceRelay: boolean;
|
|
166
190
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-host-protocol.d.ts","sourceRoot":"","sources":["../src/view-host-protocol.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"view-host-protocol.d.ts","sourceRoot":"","sources":["../src/view-host-protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,6 +36,15 @@ import { INITIALIZE_METHOD, LATEST_PROTOCOL_VERSION, RESOURCE_TEARDOWN_METHOD, S
|
|
|
36
36
|
const METHOD_NOT_SUPPORTED = -32601;
|
|
37
37
|
/** The standard MCP method a view uses to reach host-proxied tools. */
|
|
38
38
|
export const TOOLS_CALL_METHOD = "tools/call";
|
|
39
|
+
/**
|
|
40
|
+
* The spec method a view uses to push a model-context snapshot at its host
|
|
41
|
+
* (`McpUiUpdateModelContextRequest`). A local constant — the ext-apps root
|
|
42
|
+
* exports the TYPE but no method constant for it (guuey#335). Producers
|
|
43
|
+
* (ggui context-observer) fire it on slot changes as a COMPLEMENT to the
|
|
44
|
+
* action path; the choice itself always rides tools/call or the live
|
|
45
|
+
* channel ("Actions drive turns; context observes state").
|
|
46
|
+
*/
|
|
47
|
+
export const UPDATE_MODEL_CONTEXT_METHOD = "ui/update-model-context";
|
|
39
48
|
/**
|
|
40
49
|
* The standard MCP method a view uses to read host-proxied resources —
|
|
41
50
|
* `ReadResourceRequest` in the spec's App→Host request union
|
|
@@ -192,6 +201,20 @@ export function viewHostReceive(state, behavior, data) {
|
|
|
192
201
|
}
|
|
193
202
|
// fall through: a nameless tools/call is not a call we can relay.
|
|
194
203
|
}
|
|
204
|
+
if (req.method === UPDATE_MODEL_CONTEXT_METHOD && behavior.modelContextSink) {
|
|
205
|
+
// Answer success FIRST-class (the spec result is empty) and hand the
|
|
206
|
+
// params to the sink as an effect. Producers fire-and-forget this
|
|
207
|
+
// method (ggui swallows failures), but answering honestly keeps any
|
|
208
|
+
// strict producer green — and the sink gets the snapshot either way.
|
|
209
|
+
const params = isPlainObject(req.params) ? req.params : {};
|
|
210
|
+
return {
|
|
211
|
+
state,
|
|
212
|
+
effects: [
|
|
213
|
+
{ kind: "respond", message: { jsonrpc: "2.0", id: req.id, result: {} } },
|
|
214
|
+
{ kind: "model-context-update", params },
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
195
218
|
if (req.method === RESOURCES_READ_METHOD && behavior.resourceRelay) {
|
|
196
219
|
const uri = req.params?.["uri"];
|
|
197
220
|
if (typeof uri === "string") {
|
|
@@ -203,6 +226,7 @@ export function viewHostReceive(state, behavior, data) {
|
|
|
203
226
|
INITIALIZE_METHOD,
|
|
204
227
|
...(behavior.toolRelay ? [TOOLS_CALL_METHOD] : []),
|
|
205
228
|
...(behavior.resourceRelay ? [RESOURCES_READ_METHOD] : []),
|
|
229
|
+
...(behavior.modelContextSink ? [UPDATE_MODEL_CONTEXT_METHOD] : []),
|
|
206
230
|
];
|
|
207
231
|
return {
|
|
208
232
|
state,
|
package/dist/view-host.d.ts
CHANGED
|
@@ -95,6 +95,20 @@ export interface AttachViewHostConfig {
|
|
|
95
95
|
* owns allowlisting and validation (`createMcpUiActionRelay` does both).
|
|
96
96
|
*/
|
|
97
97
|
onCallTool?: (request: UiActionRequest) => Promise<McpToolCallResult>;
|
|
98
|
+
/**
|
|
99
|
+
* Sink for `ui/update-model-context` snapshots (guuey#335): the view
|
|
100
|
+
* pushes its context state (e.g. ggui slot values) for the model's
|
|
101
|
+
* FUTURE turns. Wiring it makes the host ANSWER the method (empty
|
|
102
|
+
* result, per spec) instead of `method_not_supported`, and advertises
|
|
103
|
+
* `updateModelContext` (an explicit `hostCapabilities` entry still
|
|
104
|
+
* wins). The machine answers BEFORE delivery — a throwing sink never
|
|
105
|
+
* leaves the view hanging. Producers treat this channel as a COMPLEMENT
|
|
106
|
+
* to the action path (the choice itself rides tools/call / the live
|
|
107
|
+
* channel), so a sink that only records is honest.
|
|
108
|
+
*/
|
|
109
|
+
onUpdateModelContext?: (params: {
|
|
110
|
+
[key: string]: unknown;
|
|
111
|
+
}) => void;
|
|
98
112
|
/**
|
|
99
113
|
* The mounted resource's `ui://` locator — the scope stamped on every
|
|
100
114
|
* relayed {@link UiActionRequest}. Required for the relay to fire;
|
package/dist/view-host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-host.d.ts","sourceRoot":"","sources":["../src/view-host.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"view-host.d.ts","sourceRoot":"","sources":["../src/view-host.ts"],"names":[],"mappings":"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"}
|
package/dist/view-host.js
CHANGED
|
@@ -44,6 +44,7 @@ function hostContextFor(frame, config) {
|
|
|
44
44
|
function behaviorFor(frame, config) {
|
|
45
45
|
const relayWired = config.onCallTool !== undefined && config.resourceUri !== undefined;
|
|
46
46
|
const readWired = config.onReadResource !== undefined;
|
|
47
|
+
const contextSinkWired = config.onUpdateModelContext !== undefined;
|
|
47
48
|
return {
|
|
48
49
|
hostInfo: config.hostInfo ?? DEFAULT_HOST_INFO,
|
|
49
50
|
hostCapabilities: {
|
|
@@ -51,11 +52,13 @@ function behaviorFor(frame, config) {
|
|
|
51
52
|
// hostCapabilities entry still wins (the serverTools precedent).
|
|
52
53
|
...(relayWired ? { serverTools: {} } : {}),
|
|
53
54
|
...(readWired ? { serverResources: {} } : {}),
|
|
55
|
+
...(contextSinkWired ? { updateModelContext: { text: {} } } : {}),
|
|
54
56
|
...config.hostCapabilities,
|
|
55
57
|
},
|
|
56
58
|
hostContext: hostContextFor(frame, config),
|
|
57
59
|
toolRelay: relayWired,
|
|
58
60
|
resourceRelay: readWired,
|
|
61
|
+
modelContextSink: contextSinkWired,
|
|
59
62
|
};
|
|
60
63
|
}
|
|
61
64
|
/**
|
|
@@ -129,6 +132,17 @@ export function attachViewHost(frame, config = {}) {
|
|
|
129
132
|
relay(effect.id, effect.name, effect.arguments);
|
|
130
133
|
else if (effect.kind === "relay-resource-read")
|
|
131
134
|
relayRead(effect.id, effect.uri);
|
|
135
|
+
else if (effect.kind === "model-context-update") {
|
|
136
|
+
// Delivery only — the machine already answered the view. Contained:
|
|
137
|
+
// a throwing embedder sink must not unwind the message pump.
|
|
138
|
+
try {
|
|
139
|
+
config.onUpdateModelContext?.(effect.params);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Observer failure is the embedder's bug to notice in its own
|
|
143
|
+
// logs; the view's contract is already satisfied.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
132
146
|
else {
|
|
133
147
|
config.onSizeChanged?.({
|
|
134
148
|
...(effect.width !== undefined ? { width: effect.width } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guuey/mcp-apps-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "The MCP Apps (SEP-1865) Host role for guuey's chat surfaces — view-mount narrowing across UI channels, ui:// locator rehydration by resources/read, and the sandbox-trust channel contract. Vendor-neutral: any spec-following MCP App mounts through it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ggui-ai/protocol": "0.
|
|
36
|
+
"@ggui-ai/protocol": "0.10.0",
|
|
37
37
|
"@modelcontextprotocol/ext-apps": "^1.7.5",
|
|
38
38
|
"@silverprotocol/core": "0.5.0"
|
|
39
39
|
},
|
package/src/card-mount.ts
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
* from the response and never from mount material a producer inlined.
|
|
42
42
|
*/
|
|
43
43
|
import { snapshotUiResource, toolResultLocator, toolResultUiResource, type McpUiResourcePayload } from "./block-ui.js";
|
|
44
|
+
import type { McpUiResourceCsp } from "@modelcontextprotocol/ext-apps";
|
|
44
45
|
import type { AgBlock, JsonValue } from "@silverprotocol/core";
|
|
45
46
|
|
|
46
47
|
/**
|
|
@@ -80,6 +81,16 @@ export interface ResolvedViewMount {
|
|
|
80
81
|
channel: "inline" | "ggui";
|
|
81
82
|
/** The payload an mcp-ui host mounts, identical in shape for both channels. */
|
|
82
83
|
resource: McpUiResourcePayload;
|
|
84
|
+
/**
|
|
85
|
+
* The server's per-resource CSP declaration (`_meta.ui.csp` on the
|
|
86
|
+
* `resources/read` result — MCP Apps SEP-1865; guuey#312), when the read
|
|
87
|
+
* path saw one. Spec-schema-validated at the reader
|
|
88
|
+
* (`declaredResourceCsp`) — never fabricated, absent = undeclared. This
|
|
89
|
+
* is the generic successor to the uri-prefix channel heuristic: hosts
|
|
90
|
+
* derive per-card sandbox CSP / tripwire filters from it
|
|
91
|
+
* (`<GuueyView>` defaults its `cspOrigins` from here).
|
|
92
|
+
*/
|
|
93
|
+
csp?: McpUiResourceCsp;
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
/** The durable-identity arm: no mount material, only the uri to re-fetch. */
|
package/src/index.ts
CHANGED
|
@@ -28,10 +28,15 @@ export {
|
|
|
28
28
|
} from "./card-mount.js";
|
|
29
29
|
export {
|
|
30
30
|
createMcpUiResourceReader,
|
|
31
|
+
declaredResourceCsp,
|
|
31
32
|
uiResourceChannel,
|
|
32
33
|
type CreateMcpUiResourceReaderDeps,
|
|
33
34
|
type McpResourceReadResult,
|
|
34
35
|
} from "./reader.js";
|
|
36
|
+
// The spec's own per-resource CSP type (SEP-1865 `_meta.ui.csp`) — the
|
|
37
|
+
// shape of `ResolvedViewMount.csp` (guuey#312); identical to
|
|
38
|
+
// `ViewCspOrigins`, re-exported under its spec name for mount consumers.
|
|
39
|
+
export type { McpUiResourceCsp } from "@modelcontextprotocol/ext-apps";
|
|
35
40
|
export {
|
|
36
41
|
asToolCallResult,
|
|
37
42
|
createMcpUiActionRelay,
|
package/src/react.tsx
CHANGED
|
@@ -60,6 +60,7 @@ export interface GuueyViewProps
|
|
|
60
60
|
| "hostInfo"
|
|
61
61
|
| "hostContext"
|
|
62
62
|
| "onCallTool"
|
|
63
|
+
| "onUpdateModelContext"
|
|
63
64
|
| "onReadResource"
|
|
64
65
|
| "onSizeChanged"
|
|
65
66
|
| "negotiationTimeoutMs"
|
|
@@ -236,8 +237,15 @@ export function GuueyView(props: GuueyViewProps): ReactNode {
|
|
|
236
237
|
if (frame === null || html === undefined) return;
|
|
237
238
|
if (sandboxPageUrl !== undefined && page === undefined) return; // refused config — nothing mounts
|
|
238
239
|
const resourceUri = mount.resource.uri;
|
|
240
|
+
// guuey#312: the resolved mount's DECLARED per-resource CSP
|
|
241
|
+
// (`_meta.ui.csp`, spec-schema-validated at the reader) is the CSP
|
|
242
|
+
// tripwire's default filter — the declaration finally doing host-side
|
|
243
|
+
// work. An explicit `cspOrigins` prop always wins; both absent keeps
|
|
244
|
+
// the tripwire inert exactly as before.
|
|
245
|
+
const cspOrigins = latest.current.hostConfig.cspOrigins ?? mount.csp;
|
|
239
246
|
const detachHost = attachViewHost(frame, {
|
|
240
247
|
...latest.current.hostConfig,
|
|
248
|
+
...(cspOrigins !== undefined ? { cspOrigins } : {}),
|
|
241
249
|
resourceUri,
|
|
242
250
|
onPhaseChange: (next) => {
|
|
243
251
|
setPhase(next);
|
package/src/reader.ts
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* from the response — a server answering with a foreign `ui://ggui/…` uri
|
|
25
25
|
* must not steer its HTML into the ggui-CSP host page.
|
|
26
26
|
*/
|
|
27
|
+
import { McpUiResourceCspSchema, type McpUiResourceCsp } from "@modelcontextprotocol/ext-apps";
|
|
27
28
|
import type { McpUiResourcePayload } from "./block-ui.js";
|
|
28
29
|
import type { ResolvedViewMount, ViewMountChannel } from "./card-mount.js";
|
|
29
30
|
|
|
@@ -31,8 +32,11 @@ import type { ResolvedViewMount, ViewMountChannel } from "./card-mount.js";
|
|
|
31
32
|
* One `resources/read` `contents[]` entry, structurally (SEP-1865). Kept a
|
|
32
33
|
* separate type from {@link McpUiResourcePayload} even though the shapes
|
|
33
34
|
* coincide today: this is the UNVALIDATED wire-side entry a transport hands
|
|
34
|
-
* over
|
|
35
|
-
*
|
|
35
|
+
* over, while the payload is the narrowed thing a host mounts. `_meta` is
|
|
36
|
+
* phase 2's growth (guuey#312, conformance map step 3): the per-resource
|
|
37
|
+
* declarations (`_meta.ui.csp` — MCP Apps SEP-1865) ride here when the
|
|
38
|
+
* transport forwards them; {@link declaredResourceCsp} is the ONLY door
|
|
39
|
+
* from this open extension map into typed material.
|
|
36
40
|
*/
|
|
37
41
|
export interface McpResourceReadResult {
|
|
38
42
|
uri: string;
|
|
@@ -40,6 +44,34 @@ export interface McpResourceReadResult {
|
|
|
40
44
|
text?: string;
|
|
41
45
|
/** Base64 payload arm — hosts decode via {@link resourceHtml}. */
|
|
42
46
|
blob?: string;
|
|
47
|
+
/**
|
|
48
|
+
* The entry's `_meta` as the transport saw it — an open extension map,
|
|
49
|
+
* so genuinely unknown-shaped at this boundary; never read it directly,
|
|
50
|
+
* narrow through {@link declaredResourceCsp}.
|
|
51
|
+
*/
|
|
52
|
+
_meta?: unknown;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The server's per-resource CSP declaration from a read result's
|
|
57
|
+
* `_meta.ui.csp` (guuey#312 — the phase-2 channel `uiResourceChannel`'s
|
|
58
|
+
* docs promised). Validated against the SPEC's own schema
|
|
59
|
+
* (`McpUiResourceCspSchema` from `@modelcontextprotocol/ext-apps`), never
|
|
60
|
+
* a hand-rolled mirror. Malformed or absent → `undefined` — an undeclared
|
|
61
|
+
* resource is the secure default (consumers only ever use a declaration to
|
|
62
|
+
* build allowances; absence widens nothing).
|
|
63
|
+
*/
|
|
64
|
+
export function declaredResourceCsp(entry: McpResourceReadResult): McpUiResourceCsp | undefined {
|
|
65
|
+
const meta = entry._meta;
|
|
66
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta) || !("ui" in meta)) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
const ui: unknown = meta.ui;
|
|
70
|
+
if (typeof ui !== "object" || ui === null || Array.isArray(ui) || !("csp" in ui)) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const parsed = McpUiResourceCspSchema.safeParse(ui.csp);
|
|
74
|
+
return parsed.success ? parsed.data : undefined;
|
|
43
75
|
}
|
|
44
76
|
|
|
45
77
|
/**
|
|
@@ -47,8 +79,10 @@ export interface McpResourceReadResult {
|
|
|
47
79
|
* step 3): a `ui://ggui/…` locator mounts through the ggui-CSP host page
|
|
48
80
|
* (`"ggui"` — the shell boots ggui's runtime bundle and opens its WSS),
|
|
49
81
|
* anything else through the self-only page (`"inline"` — arbitrary tenant
|
|
50
|
-
* HTML).
|
|
51
|
-
*
|
|
82
|
+
* HTML). The declaration itself ships (guuey#312 — `ResolvedViewMount.csp`
|
|
83
|
+
* via {@link declaredResourceCsp}); this heuristic retires when hosts route
|
|
84
|
+
* SANDBOX TRUST from that declaration too (per-card CSP construction, the
|
|
85
|
+
* conformance map's step-3 second half). Until then the uri prefix is the only
|
|
52
86
|
* signal a host has BEFORE fetching mount material.
|
|
53
87
|
*/
|
|
54
88
|
export function uiResourceChannel(resourceUri: string): Exclude<ViewMountChannel, "locator"> {
|
|
@@ -93,6 +127,15 @@ export function createMcpUiResourceReader(
|
|
|
93
127
|
...(typeof entry.text === "string" ? { text: entry.text } : {}),
|
|
94
128
|
...(typeof entry.blob === "string" ? { blob: entry.blob } : {}),
|
|
95
129
|
};
|
|
96
|
-
|
|
130
|
+
// guuey#312: the per-resource CSP declaration rides the resolved mount
|
|
131
|
+
// when the wire carried one — hosts build per-card allowances from the
|
|
132
|
+
// DECLARATION, never from the uri prefix. Absent = undeclared (secure
|
|
133
|
+
// default); never fabricated.
|
|
134
|
+
const csp = declaredResourceCsp(entry);
|
|
135
|
+
return {
|
|
136
|
+
channel: uiResourceChannel(resourceUri),
|
|
137
|
+
resource,
|
|
138
|
+
...(csp !== undefined ? { csp } : {}),
|
|
139
|
+
};
|
|
97
140
|
};
|
|
98
141
|
}
|
|
@@ -60,6 +60,16 @@ const METHOD_NOT_SUPPORTED = -32601;
|
|
|
60
60
|
/** The standard MCP method a view uses to reach host-proxied tools. */
|
|
61
61
|
export const TOOLS_CALL_METHOD = "tools/call";
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* The spec method a view uses to push a model-context snapshot at its host
|
|
65
|
+
* (`McpUiUpdateModelContextRequest`). A local constant — the ext-apps root
|
|
66
|
+
* exports the TYPE but no method constant for it (guuey#335). Producers
|
|
67
|
+
* (ggui context-observer) fire it on slot changes as a COMPLEMENT to the
|
|
68
|
+
* action path; the choice itself always rides tools/call or the live
|
|
69
|
+
* channel ("Actions drive turns; context observes state").
|
|
70
|
+
*/
|
|
71
|
+
export const UPDATE_MODEL_CONTEXT_METHOD = "ui/update-model-context";
|
|
72
|
+
|
|
63
73
|
/**
|
|
64
74
|
* The standard MCP method a view uses to read host-proxied resources —
|
|
65
75
|
* `ReadResourceRequest` in the spec's App→Host request union
|
|
@@ -152,6 +162,18 @@ export type ViewHostEffect =
|
|
|
152
162
|
id: ViewRequestId;
|
|
153
163
|
uri: string;
|
|
154
164
|
}
|
|
165
|
+
| {
|
|
166
|
+
/**
|
|
167
|
+
* A `ui/update-model-context` snapshot to hand the embedder
|
|
168
|
+
* (guuey#335). The machine already ANSWERED the view (success — the
|
|
169
|
+
* spec result is empty), so this effect is pure delivery: a throwing
|
|
170
|
+
* sink can never leave the view hanging. Only emitted when
|
|
171
|
+
* {@link ViewHostBehavior.modelContextSink} is true — unwired, the
|
|
172
|
+
* machine refuses in-band like every other unsupported request.
|
|
173
|
+
*/
|
|
174
|
+
kind: "model-context-update";
|
|
175
|
+
params: { [key: string]: unknown };
|
|
176
|
+
}
|
|
155
177
|
| {
|
|
156
178
|
/**
|
|
157
179
|
* The view reported its content size (`ui/notifications/size-changed`
|
|
@@ -186,6 +208,8 @@ export interface ViewHostBehavior {
|
|
|
186
208
|
hostContext: McpUiHostContext;
|
|
187
209
|
/** Whether a `tools/call` relay hook is wired (see `view-host.ts`). */
|
|
188
210
|
toolRelay: boolean;
|
|
211
|
+
/** Whether a model-context sink is wired (see `view-host.ts`, guuey#335). */
|
|
212
|
+
modelContextSink: boolean;
|
|
189
213
|
/** Whether a `resources/read` relay hook is wired (see `view-host.ts`). */
|
|
190
214
|
resourceRelay: boolean;
|
|
191
215
|
}
|
|
@@ -360,6 +384,21 @@ export function viewHostReceive(
|
|
|
360
384
|
// fall through: a nameless tools/call is not a call we can relay.
|
|
361
385
|
}
|
|
362
386
|
|
|
387
|
+
if (req.method === UPDATE_MODEL_CONTEXT_METHOD && behavior.modelContextSink) {
|
|
388
|
+
// Answer success FIRST-class (the spec result is empty) and hand the
|
|
389
|
+
// params to the sink as an effect. Producers fire-and-forget this
|
|
390
|
+
// method (ggui swallows failures), but answering honestly keeps any
|
|
391
|
+
// strict producer green — and the sink gets the snapshot either way.
|
|
392
|
+
const params = isPlainObject(req.params) ? req.params : {};
|
|
393
|
+
return {
|
|
394
|
+
state,
|
|
395
|
+
effects: [
|
|
396
|
+
{ kind: "respond", message: { jsonrpc: "2.0", id: req.id, result: {} } },
|
|
397
|
+
{ kind: "model-context-update", params },
|
|
398
|
+
],
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
363
402
|
if (req.method === RESOURCES_READ_METHOD && behavior.resourceRelay) {
|
|
364
403
|
const uri = req.params?.["uri"];
|
|
365
404
|
if (typeof uri === "string") {
|
|
@@ -372,6 +411,7 @@ export function viewHostReceive(
|
|
|
372
411
|
INITIALIZE_METHOD,
|
|
373
412
|
...(behavior.toolRelay ? [TOOLS_CALL_METHOD] : []),
|
|
374
413
|
...(behavior.resourceRelay ? [RESOURCES_READ_METHOD] : []),
|
|
414
|
+
...(behavior.modelContextSink ? [UPDATE_MODEL_CONTEXT_METHOD] : []),
|
|
375
415
|
];
|
|
376
416
|
return {
|
|
377
417
|
state,
|
package/src/view-host.ts
CHANGED
|
@@ -117,6 +117,18 @@ export interface AttachViewHostConfig {
|
|
|
117
117
|
* owns allowlisting and validation (`createMcpUiActionRelay` does both).
|
|
118
118
|
*/
|
|
119
119
|
onCallTool?: (request: UiActionRequest) => Promise<McpToolCallResult>;
|
|
120
|
+
/**
|
|
121
|
+
* Sink for `ui/update-model-context` snapshots (guuey#335): the view
|
|
122
|
+
* pushes its context state (e.g. ggui slot values) for the model's
|
|
123
|
+
* FUTURE turns. Wiring it makes the host ANSWER the method (empty
|
|
124
|
+
* result, per spec) instead of `method_not_supported`, and advertises
|
|
125
|
+
* `updateModelContext` (an explicit `hostCapabilities` entry still
|
|
126
|
+
* wins). The machine answers BEFORE delivery — a throwing sink never
|
|
127
|
+
* leaves the view hanging. Producers treat this channel as a COMPLEMENT
|
|
128
|
+
* to the action path (the choice itself rides tools/call / the live
|
|
129
|
+
* channel), so a sink that only records is honest.
|
|
130
|
+
*/
|
|
131
|
+
onUpdateModelContext?: (params: { [key: string]: unknown }) => void;
|
|
120
132
|
/**
|
|
121
133
|
* The mounted resource's `ui://` locator — the scope stamped on every
|
|
122
134
|
* relayed {@link UiActionRequest}. Required for the relay to fire;
|
|
@@ -207,6 +219,7 @@ function hostContextFor(frame: ViewFrameLike, config: AttachViewHostConfig): Mcp
|
|
|
207
219
|
function behaviorFor(frame: ViewFrameLike, config: AttachViewHostConfig): ViewHostBehavior {
|
|
208
220
|
const relayWired = config.onCallTool !== undefined && config.resourceUri !== undefined;
|
|
209
221
|
const readWired = config.onReadResource !== undefined;
|
|
222
|
+
const contextSinkWired = config.onUpdateModelContext !== undefined;
|
|
210
223
|
return {
|
|
211
224
|
hostInfo: config.hostInfo ?? DEFAULT_HOST_INFO,
|
|
212
225
|
hostCapabilities: {
|
|
@@ -214,11 +227,13 @@ function behaviorFor(frame: ViewFrameLike, config: AttachViewHostConfig): ViewHo
|
|
|
214
227
|
// hostCapabilities entry still wins (the serverTools precedent).
|
|
215
228
|
...(relayWired ? { serverTools: {} } : {}),
|
|
216
229
|
...(readWired ? { serverResources: {} } : {}),
|
|
230
|
+
...(contextSinkWired ? { updateModelContext: { text: {} } } : {}),
|
|
217
231
|
...config.hostCapabilities,
|
|
218
232
|
},
|
|
219
233
|
hostContext: hostContextFor(frame, config),
|
|
220
234
|
toolRelay: relayWired,
|
|
221
235
|
resourceRelay: readWired,
|
|
236
|
+
modelContextSink: contextSinkWired,
|
|
222
237
|
};
|
|
223
238
|
}
|
|
224
239
|
|
|
@@ -295,6 +310,16 @@ export function attachViewHost(frame: ViewFrameLike, config: AttachViewHostConfi
|
|
|
295
310
|
if (effect.kind === "respond") post(effect.message);
|
|
296
311
|
else if (effect.kind === "relay-tool-call") relay(effect.id, effect.name, effect.arguments);
|
|
297
312
|
else if (effect.kind === "relay-resource-read") relayRead(effect.id, effect.uri);
|
|
313
|
+
else if (effect.kind === "model-context-update") {
|
|
314
|
+
// Delivery only — the machine already answered the view. Contained:
|
|
315
|
+
// a throwing embedder sink must not unwind the message pump.
|
|
316
|
+
try {
|
|
317
|
+
config.onUpdateModelContext?.(effect.params);
|
|
318
|
+
} catch {
|
|
319
|
+
// Observer failure is the embedder's bug to notice in its own
|
|
320
|
+
// logs; the view's contract is already satisfied.
|
|
321
|
+
}
|
|
322
|
+
}
|
|
298
323
|
else {
|
|
299
324
|
config.onSizeChanged?.({
|
|
300
325
|
...(effect.width !== undefined ? { width: effect.width } : {}),
|