From 699549ee3c14ccbf95949ecbe637c10374e6eaf7 Mon Sep 17 00:00:00 2001 From: Ido Salomon Date: Tue, 16 Dec 2025 00:36:04 +0200 Subject: [PATCH 1/7] feat: enhance sandbox capability negotiation --- examples/basic-host/src/implementation.ts | 20 +++- examples/basic-host/src/sandbox.ts | 39 ++++++- examples/simple-host/sandbox.html | 112 ++++++++++++++++++ specification/draft/apps.mdx | 87 +++++++++++++- src/generated/schema.json | 129 +++++++++++++++++++++ src/generated/schema.test.ts | 22 +++- src/generated/schema.ts | 135 +++++++++++++++++----- src/spec.types.ts | 33 ++++++ src/types.ts | 2 + 9 files changed, 525 insertions(+), 54 deletions(-) create mode 100644 examples/simple-host/sandbox.html diff --git a/examples/basic-host/src/implementation.ts b/examples/basic-host/src/implementation.ts index 9ab57591..331c1995 100644 --- a/examples/basic-host/src/implementation.ts +++ b/examples/basic-host/src/implementation.ts @@ -45,6 +45,13 @@ interface UiResourceData { csp?: { connectDomains?: string[]; resourceDomains?: string[]; + frameDomains?: string[]; + baseUriDomains?: string[]; + }; + permissions?: { + camera?: boolean; + microphone?: boolean; + geolocation?: boolean; }; } @@ -118,15 +125,16 @@ async function getUiResource(serverInfo: ServerInfo, uri: string): Promise`; } +// Build iframe allow attribute from permissions +function buildAllowAttribute(permissions?: { + camera?: boolean; + microphone?: boolean; + geolocation?: boolean; +}): string { + if (!permissions) return ""; + + const allowList: string[] = []; + if (permissions.camera) allowList.push("camera"); + if (permissions.microphone) allowList.push("microphone"); + if (permissions.geolocation) allowList.push("geolocation"); + + return allowList.join("; "); +} + window.addEventListener("message", async (event) => { if (event.source === window.parent) { // NOTE: In production you'll also want to validate `event.origin` against // your Host domain. if (event.data && event.data.method === RESOURCE_READY_NOTIFICATION) { - const { html, sandbox, csp } = event.data.params; + const { html, sandbox, csp, permissions } = event.data.params; if (typeof sandbox === "string") { inner.setAttribute("sandbox", sandbox); } + // Set Permission Policy allow attribute if permissions are requested + const allowAttribute = buildAllowAttribute(permissions); + if (allowAttribute) { + console.log("[Sandbox] Setting allow attribute:", allowAttribute); + inner.setAttribute("allow", allowAttribute); + } if (typeof html === "string") { // Inject CSP meta tag at the start of if CSP is provided console.log("[Sandbox] Received CSP:", csp); diff --git a/examples/simple-host/sandbox.html b/examples/simple-host/sandbox.html new file mode 100644 index 00000000..2937aff5 --- /dev/null +++ b/examples/simple-host/sandbox.html @@ -0,0 +1,112 @@ + + + + + + + + MCP-UI Proxy + + + + + + diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index d9485675..89076c15 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -138,6 +138,53 @@ interface UIResourceMeta { * ["https://cdn.jsdelivr.net", "https://*.cloudflare.com"] */ resourceDomains?: string[], + /** + * Origins for nested iframes + * + * - Empty or omitted = no nested iframes allowed (`frame-src 'none'`) + * - Maps to CSP `frame-src` directive + * + * @example + * ["https://www.youtube.com", "https://player.vimeo.com"] + */ + frameDomains?: string[], + /** + * Allowed base URIs for the document + * + * - Empty or omitted = only same origin allowed (`base-uri 'self'`) + * - Maps to CSP `base-uri` directive + * + * @example + * ["https://cdn.example.com"] + */ + baseUriDomains?: string[], + }, + /** + * Sandbox permissions requested by the UI + * + * Servers declare which browser capabilities their UI needs. + * Hosts MAY honor these by setting appropriate iframe `allow` attributes. + * Apps SHOULD NOT assume permissions are granted; use JS feature detection as fallback. + */ + permissions?: { + /** + * Request camera access + * + * Maps to Permission Policy `camera` feature + */ + camera?: boolean, + /** + * Request microphone access + * + * Maps to Permission Policy `microphone` feature + */ + microphone?: boolean, + /** + * Request geolocation access + * + * Maps to Permission Policy `geolocation` feature + */ + geolocation?: boolean, }, /** * Dedicated origin for widget @@ -180,6 +227,13 @@ The resource content is returned via `resources/read`: csp?: { connectDomains?: string[]; // Origins for fetch/XHR/WebSocket resourceDomains?: string[]; // Origins for images, scripts, etc + frameDomains?: string[]; // Origins for nested iframes + baseUriDomains?: string[]; // Allowed base URIs + }; + permissions?: { + camera?: boolean; // Request camera access + microphone?: boolean; // Request microphone access + geolocation?: boolean; // Request geolocation access }; domain?: string; prefersBorder?: boolean; @@ -362,9 +416,11 @@ If the Host is a web page, it MUST wrap the Guest UI and communicate with it thr 4. Once the Sandbox is ready, the Host MUST send the raw HTML resource to load in a `ui/notifications/sandbox-resource-ready` notification. 5. The Sandbox MUST load the raw HTML of the Guest UI with CSP settings that: - Enforce the domains declared in `ui.csp` metadata - - Prevent nested iframes (`frame-src 'none'`) - - Block dangerous features (`object-src 'none'`, `base-uri 'self'`) + - If `frameDomains` is provided, allow nested iframes from declared origins; otherwise use `frame-src 'none'` + - If `baseUriDomains` is provided, allow base URIs from declared origins; otherwise use `base-uri 'self'` + - Block dangerous features (`object-src 'none'`) - Apply restrictive defaults if no CSP metadata is provided + - If `permissions` is declared, the Sandbox MAY set the inner iframe's `allow` attribute accordingly 6. The Sandbox MUST forward messages sent by the Host to the Guest UI, and vice versa, for any method that doesn’t start with `ui/notifications/sandbox-`. This includes lifecycle messages, e.g., `ui/initialize` request & `ui/notifications/initialized` notification both sent by the Guest UI. The Host MUST NOT send any request or notification to the Guest UI before it receives an `initialized` notification. 7. The Sandbox SHOULD NOT create/send any requests to the Host or to the Guest UI (this would require synthesizing new request ids). 8. The Host MAY forward any message from the Guest UI (coming via the Sandbox) to the MCP Apps server, for any method that doesn’t start with `ui/`. While the Host SHOULD ensure the Guest UI’s MCP connection is spec-compliant, it MAY decide to block some messages or subject them to further user approval. @@ -826,12 +882,23 @@ These messages are reserved for web-based hosts that implement the recommended d method: "ui/notifications/sandbox-resource-ready", params: { html: string, // HTML content to load - sandbox: string // Optional override for inner iframe `sandbox` attribute + sandbox?: string, // Optional override for inner iframe `sandbox` attribute + csp?: { // CSP configuration from resource metadata + connectDomains?: string[], + resourceDomains?: string[], + frameDomains?: string[], + baseUriDomains?: string[], + }, + permissions?: { // Sandbox permissions from resource metadata + camera?: boolean, + microphone?: boolean, + geolocation?: boolean, + } } } ``` -These messages facilitate the communication between the outer sandbox proxy iframe and the host, enabling secure loading of untrusted HTML content. +These messages facilitate the communication between the outer sandbox proxy iframe and the host, enabling secure loading of untrusted HTML content. The `permissions` field maps to the inner iframe's `allow` attribute for Permission Policy features. ### Lifecycle @@ -1269,6 +1336,7 @@ Hosts MUST enforce Content Security Policies based on resource metadata. ```typescript const csp = resource._meta?.ui?.csp; +const permissions = resource._meta?.ui?.permissions; const cspValue = ` default-src 'none'; @@ -1278,10 +1346,17 @@ const cspValue = ` img-src 'self' data: ${csp?.resourceDomains?.join(' ') || ''}; font-src 'self' ${csp?.resourceDomains?.join(' ') || ''}; media-src 'self' data: ${csp?.resourceDomains?.join(' ') || ''}; - frame-src 'none'; + frame-src ${csp?.frameDomains?.join(' ') || "'none'"}; object-src 'none'; - base-uri 'self'; + base-uri ${csp?.baseUriDomains?.join(' ') || "'self'"}; `; + +// Permission Policy for iframe allow attribute +const allowList: string[] = []; +if (permissions?.camera) allowList.push('camera'); +if (permissions?.microphone) allowList.push('microphone'); +if (permissions?.geolocation) allowList.push('geolocation'); +const allowAttribute = allowList.join('; '); ``` **Security Requirements:** diff --git a/src/generated/schema.json b/src/generated/schema.json index c85ab081..59473696 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -89,6 +89,21 @@ "type": "object", "properties": {}, "additionalProperties": false + }, + "csp": { + "description": "CSP overrides the host supports for sandbox proxies.", + "type": "object", + "properties": { + "frameDomains": { + "description": "Host supports frame-src domain allowlisting.", + "type": "boolean" + }, + "baseUriDomains": { + "description": "Host supports base-uri domain allowlisting.", + "type": "boolean" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1826,6 +1841,21 @@ "type": "object", "properties": {}, "additionalProperties": false + }, + "csp": { + "description": "CSP overrides the host supports for sandbox proxies.", + "type": "object", + "properties": { + "frameDomains": { + "description": "Host supports frame-src domain allowlisting.", + "type": "boolean" + }, + "baseUriDomains": { + "description": "Host supports base-uri domain allowlisting.", + "type": "boolean" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2862,6 +2892,20 @@ "items": { "type": "string" } + }, + "frameDomains": { + "description": "Origins for nested iframes (frame-src directive).", + "type": "array", + "items": { + "type": "string" + } + }, + "baseUriDomains": { + "description": "Allowed base URIs for the document (base-uri directive).", + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false @@ -2887,6 +2931,39 @@ "items": { "type": "string" } + }, + "frameDomains": { + "description": "Origins for nested iframes (frame-src directive).", + "type": "array", + "items": { + "type": "string" + } + }, + "baseUriDomains": { + "description": "Allowed base URIs for the document (base-uri directive).", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "permissions": { + "description": "Sandbox permissions requested by the UI.", + "type": "object", + "properties": { + "camera": { + "description": "Request camera access (Permission Policy `camera` feature).", + "type": "boolean" + }, + "microphone": { + "description": "Request microphone access (Permission Policy `microphone` feature).", + "type": "boolean" + }, + "geolocation": { + "description": "Request geolocation access (Permission Policy `geolocation` feature).", + "type": "boolean" } }, "additionalProperties": false @@ -2902,6 +2979,25 @@ }, "additionalProperties": false }, + "McpUiResourcePermissions": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "camera": { + "description": "Request camera access (Permission Policy `camera` feature).", + "type": "boolean" + }, + "microphone": { + "description": "Request microphone access (Permission Policy `microphone` feature).", + "type": "boolean" + }, + "geolocation": { + "description": "Request geolocation access (Permission Policy `geolocation` feature).", + "type": "boolean" + } + }, + "additionalProperties": false + }, "McpUiResourceTeardownRequest": { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", @@ -2980,6 +3076,39 @@ "items": { "type": "string" } + }, + "frameDomains": { + "description": "Origins for nested iframes (frame-src directive).", + "type": "array", + "items": { + "type": "string" + } + }, + "baseUriDomains": { + "description": "Allowed base URIs for the document (base-uri directive).", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "permissions": { + "description": "Sandbox permissions from resource metadata.", + "type": "object", + "properties": { + "camera": { + "description": "Request camera access (Permission Policy `camera` feature).", + "type": "boolean" + }, + "microphone": { + "description": "Request microphone access (Permission Policy `microphone` feature).", + "type": "boolean" + }, + "geolocation": { + "description": "Request geolocation access (Permission Policy `geolocation` feature).", + "type": "boolean" } }, "additionalProperties": false diff --git a/src/generated/schema.test.ts b/src/generated/schema.test.ts index 47f2a37c..92fd1ebd 100644 --- a/src/generated/schema.test.ts +++ b/src/generated/schema.test.ts @@ -43,8 +43,8 @@ export type McpUiSandboxProxyReadyNotificationSchemaInferredType = z.infer< typeof generated.McpUiSandboxProxyReadyNotificationSchema >; -export type McpUiSandboxResourceReadyNotificationSchemaInferredType = z.infer< - typeof generated.McpUiSandboxResourceReadyNotificationSchema +export type McpUiResourcePermissionsSchemaInferredType = z.infer< + typeof generated.McpUiResourcePermissionsSchema >; export type McpUiSizeChangedNotificationSchemaInferredType = z.infer< @@ -99,6 +99,10 @@ export type McpUiMessageRequestSchemaInferredType = z.infer< typeof generated.McpUiMessageRequestSchema >; +export type McpUiSandboxResourceReadyNotificationSchemaInferredType = z.infer< + typeof generated.McpUiSandboxResourceReadyNotificationSchema +>; + export type McpUiToolResultNotificationSchemaInferredType = z.infer< typeof generated.McpUiToolResultNotificationSchema >; @@ -151,11 +155,11 @@ expectType( expectType( {} as spec.McpUiSandboxProxyReadyNotification, ); -expectType( - {} as McpUiSandboxResourceReadyNotificationSchemaInferredType, +expectType( + {} as McpUiResourcePermissionsSchemaInferredType, ); -expectType( - {} as spec.McpUiSandboxResourceReadyNotification, +expectType( + {} as spec.McpUiResourcePermissions, ); expectType( {} as McpUiSizeChangedNotificationSchemaInferredType, @@ -223,6 +227,12 @@ expectType( expectType( {} as spec.McpUiMessageRequest, ); +expectType( + {} as McpUiSandboxResourceReadyNotificationSchemaInferredType, +); +expectType( + {} as spec.McpUiSandboxResourceReadyNotification, +); expectType( {} as McpUiToolResultNotificationSchemaInferredType, ); diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 84682286..0d3751ec 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -180,39 +180,30 @@ export const McpUiSandboxProxyReadyNotificationSchema = z.object({ }); /** - * @description Notification containing HTML resource for the sandbox proxy to load. - * @internal - * @see https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx#sandbox-proxy + * @description Sandbox permissions requested by the UI resource. + * Hosts MAY honor these by setting appropriate iframe `allow` attributes. + * Apps SHOULD NOT assume permissions are granted; use JS feature detection as fallback. */ -export const McpUiSandboxResourceReadyNotificationSchema = z.object({ - method: z.literal("ui/notifications/sandbox-resource-ready"), - params: z.object({ - /** @description HTML content to load into the inner iframe. */ - html: z.string().describe("HTML content to load into the inner iframe."), - /** @description Optional override for the inner iframe's sandbox attribute. */ - sandbox: z - .string() - .optional() - .describe("Optional override for the inner iframe's sandbox attribute."), - /** @description CSP configuration from resource metadata. */ - csp: z - .object({ - /** @description Origins for network requests (fetch/XHR/WebSocket). */ - connectDomains: z - .array(z.string()) - .optional() - .describe("Origins for network requests (fetch/XHR/WebSocket)."), - /** @description Origins for static resources (scripts, images, styles, fonts). */ - resourceDomains: z - .array(z.string()) - .optional() - .describe( - "Origins for static resources (scripts, images, styles, fonts).", - ), - }) - .optional() - .describe("CSP configuration from resource metadata."), - }), +export const McpUiResourcePermissionsSchema = z.object({ + /** @description Request camera access (Permission Policy `camera` feature). */ + camera: z + .boolean() + .optional() + .describe("Request camera access (Permission Policy `camera` feature)."), + /** @description Request microphone access (Permission Policy `microphone` feature). */ + microphone: z + .boolean() + .optional() + .describe( + "Request microphone access (Permission Policy `microphone` feature).", + ), + /** @description Request geolocation access (Permission Policy `geolocation` feature). */ + geolocation: z + .boolean() + .optional() + .describe( + "Request geolocation access (Permission Policy `geolocation` feature).", + ), }); /** @@ -352,6 +343,22 @@ export const McpUiHostCapabilitiesSchema = z.object({ .describe("Host can proxy resource reads to the MCP server."), /** @description Host accepts log messages. */ logging: z.object({}).optional().describe("Host accepts log messages."), + /** @description CSP overrides the host supports for sandbox proxies. */ + csp: z + .object({ + /** @description Host supports frame-src domain allowlisting. */ + frameDomains: z + .boolean() + .optional() + .describe("Host supports frame-src domain allowlisting."), + /** @description Host supports base-uri domain allowlisting. */ + baseUriDomains: z + .boolean() + .optional() + .describe("Host supports base-uri domain allowlisting."), + }) + .optional() + .describe("CSP overrides the host supports for sandbox proxies."), }); /** @@ -400,6 +407,16 @@ export const McpUiResourceCspSchema = z.object({ .array(z.string()) .optional() .describe("Origins for static resources (scripts, images, styles, fonts)."), + /** @description Origins for nested iframes (frame-src directive). */ + frameDomains: z + .array(z.string()) + .optional() + .describe("Origins for nested iframes (frame-src directive)."), + /** @description Allowed base URIs for the document (base-uri directive). */ + baseUriDomains: z + .array(z.string()) + .optional() + .describe("Allowed base URIs for the document (base-uri directive)."), }); /** @@ -410,6 +427,10 @@ export const McpUiResourceMetaSchema = z.object({ csp: McpUiResourceCspSchema.optional().describe( "Content Security Policy configuration.", ), + /** @description Sandbox permissions requested by the UI. */ + permissions: McpUiResourcePermissionsSchema.optional().describe( + "Sandbox permissions requested by the UI.", + ), /** @description Dedicated origin for widget sandbox. */ domain: z .string() @@ -442,6 +463,56 @@ export const McpUiMessageRequestSchema = z.object({ }), }); +/** + * @description Notification containing HTML resource for the sandbox proxy to load. + * @internal + * @see https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx#sandbox-proxy + */ +export const McpUiSandboxResourceReadyNotificationSchema = z.object({ + method: z.literal("ui/notifications/sandbox-resource-ready"), + params: z.object({ + /** @description HTML content to load into the inner iframe. */ + html: z.string().describe("HTML content to load into the inner iframe."), + /** @description Optional override for the inner iframe's sandbox attribute. */ + sandbox: z + .string() + .optional() + .describe("Optional override for the inner iframe's sandbox attribute."), + /** @description CSP configuration from resource metadata. */ + csp: z + .object({ + /** @description Origins for network requests (fetch/XHR/WebSocket). */ + connectDomains: z + .array(z.string()) + .optional() + .describe("Origins for network requests (fetch/XHR/WebSocket)."), + /** @description Origins for static resources (scripts, images, styles, fonts). */ + resourceDomains: z + .array(z.string()) + .optional() + .describe( + "Origins for static resources (scripts, images, styles, fonts).", + ), + /** @description Origins for nested iframes (frame-src directive). */ + frameDomains: z + .array(z.string()) + .optional() + .describe("Origins for nested iframes (frame-src directive)."), + /** @description Allowed base URIs for the document (base-uri directive). */ + baseUriDomains: z + .array(z.string()) + .optional() + .describe("Allowed base URIs for the document (base-uri directive)."), + }) + .optional() + .describe("CSP configuration from resource metadata."), + /** @description Sandbox permissions from resource metadata. */ + permissions: McpUiResourcePermissionsSchema.optional().describe( + "Sandbox permissions from resource metadata.", + ), + }), +}); + /** * @description Notification containing tool execution result (Host -> Guest UI). */ diff --git a/src/spec.types.ts b/src/spec.types.ts index 2f1d2533..2af92d3d 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -222,7 +222,13 @@ export interface McpUiSandboxResourceReadyNotification { connectDomains?: string[]; /** @description Origins for static resources (scripts, images, styles, fonts). */ resourceDomains?: string[]; + /** @description Origins for nested iframes (frame-src directive). */ + frameDomains?: string[]; + /** @description Allowed base URIs for the document (base-uri directive). */ + baseUriDomains?: string[]; }; + /** @description Sandbox permissions from resource metadata. */ + permissions?: McpUiResourcePermissions; }; } @@ -403,6 +409,13 @@ export interface McpUiHostCapabilities { }; /** @description Host accepts log messages. */ logging?: {}; + /** @description CSP overrides the host supports for sandbox proxies. */ + csp?: { + /** @description Host supports frame-src domain allowlisting. */ + frameDomains?: boolean; + /** @description Host supports base-uri domain allowlisting. */ + baseUriDomains?: boolean; + }; } /** @@ -472,6 +485,24 @@ export interface McpUiResourceCsp { connectDomains?: string[]; /** @description Origins for static resources (scripts, images, styles, fonts). */ resourceDomains?: string[]; + /** @description Origins for nested iframes (frame-src directive). */ + frameDomains?: string[]; + /** @description Allowed base URIs for the document (base-uri directive). */ + baseUriDomains?: string[]; +} + +/** + * @description Sandbox permissions requested by the UI resource. + * Hosts MAY honor these by setting appropriate iframe `allow` attributes. + * Apps SHOULD NOT assume permissions are granted; use JS feature detection as fallback. + */ +export interface McpUiResourcePermissions { + /** @description Request camera access (Permission Policy `camera` feature). */ + camera?: boolean; + /** @description Request microphone access (Permission Policy `microphone` feature). */ + microphone?: boolean; + /** @description Request geolocation access (Permission Policy `geolocation` feature). */ + geolocation?: boolean; } /** @@ -480,6 +511,8 @@ export interface McpUiResourceCsp { export interface McpUiResourceMeta { /** @description Content Security Policy configuration. */ csp?: McpUiResourceCsp; + /** @description Sandbox permissions requested by the UI. */ + permissions?: McpUiResourcePermissions; /** @description Dedicated origin for widget sandbox. */ domain?: string; /** @description Visual boundary preference - true if UI prefers a visible border. */ diff --git a/src/types.ts b/src/types.ts index ac6eb1b5..dd501203 100644 --- a/src/types.ts +++ b/src/types.ts @@ -38,6 +38,7 @@ export { type McpUiInitializeResult, type McpUiInitializedNotification, type McpUiResourceCsp, + type McpUiResourcePermissions, type McpUiResourceMeta, } from "./spec.types.js"; @@ -88,6 +89,7 @@ export { McpUiInitializeResultSchema, McpUiInitializedNotificationSchema, McpUiResourceCspSchema, + McpUiResourcePermissionsSchema, McpUiResourceMetaSchema, } from "./generated/schema.js"; From 7aef0d8ece0d881c83ccc0ff99d486c53644447e Mon Sep 17 00:00:00 2001 From: Ido Salomon Date: Tue, 16 Dec 2025 02:39:41 +0200 Subject: [PATCH 2/7] Update specification/draft/apps.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- specification/draft/apps.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 89076c15..38124157 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -1356,7 +1356,7 @@ const allowList: string[] = []; if (permissions?.camera) allowList.push('camera'); if (permissions?.microphone) allowList.push('microphone'); if (permissions?.geolocation) allowList.push('geolocation'); -const allowAttribute = allowList.join('; '); +const allowAttribute = allowList.join(' '); ``` **Security Requirements:** From e038020b50c85a3250b68a67b15a03a7553e48b7 Mon Sep 17 00:00:00 2001 From: Ido Salomon Date: Thu, 18 Dec 2025 22:31:00 +0200 Subject: [PATCH 3/7] add all csp and permissions to host capabilities + update SEP --- examples/basic-host/package.json | 1 + examples/basic-server-react/package.json | 1 + examples/basic-server-vanillajs/package.json | 1 + examples/budget-allocator-server/package.json | 1 + examples/cohort-heatmap-server/package.json | 1 + .../customer-segmentation-server/package.json | 1 + examples/scenario-modeler-server/package.json | 1 + examples/simple-host/sandbox.html | 18 ++- examples/system-monitor-server/package.json | 1 + examples/threejs-server/package.json | 1 + examples/wiki-explorer-server/package.json | 1 + package-lock.json | 24 ++-- specification/draft/apps.mdx | 46 +++++++ src/generated/schema.json | 124 +++++++++++++++--- src/generated/schema.test.ts | 28 ++-- src/generated/schema.ts | 122 +++++++++-------- src/spec.types.ts | 12 +- 17 files changed, 271 insertions(+), 113 deletions(-) diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index 78cf826f..cb775a64 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -25,6 +25,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "prettier": "^3.6.2", "vite": "^6.0.0", diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index 5927040c..e21d4484 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -26,6 +26,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 5bc5a71c..5554546e 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -21,6 +21,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 86a95100..6a1f325c 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -25,6 +25,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index aff80002..499f46a8 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -29,6 +29,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index b8b7f197..157efa01 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -25,6 +25,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index 95c13fc1..33b14c58 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -30,6 +30,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/simple-host/sandbox.html b/examples/simple-host/sandbox.html index 2937aff5..14849447 100644 --- a/examples/simple-host/sandbox.html +++ b/examples/simple-host/sandbox.html @@ -57,10 +57,21 @@ inner.setAttribute('sandbox', 'allow-scripts allow-same-origin'); document.body.appendChild(inner); + // Build iframe allow attribute from permissions + function buildAllowAttribute(permissions) { + if (!permissions) return ''; + const allowList = []; + if (permissions.camera) allowList.push('camera'); + if (permissions.microphone) allowList.push('microphone'); + if (permissions.geolocation) allowList.push('geolocation'); + return allowList.join('; '); + } + window.addEventListener('message', async (event) => { if (event.source === window.parent) { if (event.data && event.data.method === 'ui/notifications/sandbox-resource-ready') { - const { html, sandbox } = event.data.params || {}; + const { html, sandbox, permissions } = event.data.params || {}; + // Note: csp is not extracted here - CSP is set via HTTP response headers in serve.ts if (typeof sandbox === 'string') { // Ensure allow-same-origin is present for document.write to work let finalSandbox = sandbox; @@ -69,6 +80,11 @@ } inner.setAttribute('sandbox', finalSandbox); } + // Set Permission Policy allow attribute if permissions are provided + const allowAttribute = buildAllowAttribute(permissions); + if (allowAttribute) { + inner.setAttribute('allow', allowAttribute); + } if (typeof html === 'string') { // Use document.write instead of srcdoc to avoid CSP base-uri issues // document.write allows the browser to resolve relative URLs correctly diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index 6adfbb91..485df6f5 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -26,6 +26,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index bdc00f55..e88463a8 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -31,6 +31,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index 5c389c31..daef0e9f 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -25,6 +25,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "force-graph": "^1.49.0", "typescript": "^5.9.3", diff --git a/package-lock.json b/package-lock.json index 2a1bf159..2a5b60ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,6 +85,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "prettier": "^3.6.2", "typescript": "^5.9.3", @@ -128,6 +129,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -164,6 +166,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -201,6 +204,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -242,6 +246,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -279,6 +284,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -321,6 +327,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -359,6 +366,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -402,6 +410,7 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", @@ -439,6 +448,7 @@ "@types/node": "^22.0.0", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "express": "^5.1.0", "force-graph": "^1.49.0", "typescript": "^5.9.3", @@ -492,7 +502,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2098,7 +2107,6 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2123,7 +2131,6 @@ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2624,7 +2631,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3340,7 +3346,6 @@ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -3788,7 +3793,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5321,7 +5325,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5420,7 +5423,6 @@ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -6191,7 +6193,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -6941,7 +6942,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7035,7 +7035,6 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -7169,7 +7168,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7422,7 +7420,6 @@ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -7467,7 +7464,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 01ff639c..c43881f2 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -578,6 +578,52 @@ Example: } ``` +### Host Capabilities + +`HostCapabilities` are sent to the Guest UI as part of the response to `ui/initialize` (inside `McpUiInitializeResult`). +They describe the features and capabilities that the Host supports. + +```typescript +interface HostCapabilities { + /** Experimental features (structure TBD). */ + experimental?: {}; + /** Host supports opening external URLs. */ + openLinks?: {}; + /** Host can proxy tool calls to the MCP server. */ + serverTools?: { + /** Host supports tools/list_changed notifications. */ + listChanged?: boolean; + }; + /** Host can proxy resource reads to the MCP server. */ + serverResources?: { + /** Host supports resources/list_changed notifications. */ + listChanged?: boolean; + }; + /** Host accepts log messages. */ + logging?: {}; + /** Sandbox configuration applied by the host. */ + sandbox?: { + /** Permissions granted by the host (camera, microphone, geolocation). */ + permissions?: { + camera?: boolean; + microphone?: boolean; + geolocation?: boolean; + }; + /** CSP domains approved by the host. */ + csp?: { + /** Approved origins for network requests (fetch/XHR/WebSocket). */ + connectDomains?: string[]; + /** Approved origins for static resources (scripts, images, styles, fonts). */ + resourceDomains?: string[]; + /** Approved origins for nested iframes (frame-src directive). */ + frameDomains?: string[]; + /** Approved base URIs for the document (base-uri directive). */ + baseUriDomains?: string[]; + }; + }; +} +``` + ### Theming Hosts can optionally pass CSS custom properties via `HostContext.styles.variables` for visual cohesion with the host environment. diff --git a/src/generated/schema.json b/src/generated/schema.json index 80835036..ee56ecfc 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -90,17 +90,63 @@ "properties": {}, "additionalProperties": false }, - "csp": { - "description": "CSP overrides the host supports for sandbox proxies.", + "sandbox": { + "description": "Sandbox configuration applied by the host.", "type": "object", "properties": { - "frameDomains": { - "description": "Host supports frame-src domain allowlisting.", - "type": "boolean" + "permissions": { + "description": "Permissions granted by the host (camera, microphone, geolocation).", + "type": "object", + "properties": { + "camera": { + "description": "Request camera access (Permission Policy `camera` feature).", + "type": "boolean" + }, + "microphone": { + "description": "Request microphone access (Permission Policy `microphone` feature).", + "type": "boolean" + }, + "geolocation": { + "description": "Request geolocation access (Permission Policy `geolocation` feature).", + "type": "boolean" + } + }, + "additionalProperties": false }, - "baseUriDomains": { - "description": "Host supports base-uri domain allowlisting.", - "type": "boolean" + "csp": { + "description": "CSP domains approved by the host.", + "type": "object", + "properties": { + "connectDomains": { + "description": "Origins for network requests (fetch/XHR/WebSocket).", + "type": "array", + "items": { + "type": "string" + } + }, + "resourceDomains": { + "description": "Origins for static resources (scripts, images, styles, fonts).", + "type": "array", + "items": { + "type": "string" + } + }, + "frameDomains": { + "description": "Origins for nested iframes (frame-src directive).", + "type": "array", + "items": { + "type": "string" + } + }, + "baseUriDomains": { + "description": "Allowed base URIs for the document (base-uri directive).", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1898,17 +1944,63 @@ "properties": {}, "additionalProperties": false }, - "csp": { - "description": "CSP overrides the host supports for sandbox proxies.", + "sandbox": { + "description": "Sandbox configuration applied by the host.", "type": "object", "properties": { - "frameDomains": { - "description": "Host supports frame-src domain allowlisting.", - "type": "boolean" + "permissions": { + "description": "Permissions granted by the host (camera, microphone, geolocation).", + "type": "object", + "properties": { + "camera": { + "description": "Request camera access (Permission Policy `camera` feature).", + "type": "boolean" + }, + "microphone": { + "description": "Request microphone access (Permission Policy `microphone` feature).", + "type": "boolean" + }, + "geolocation": { + "description": "Request geolocation access (Permission Policy `geolocation` feature).", + "type": "boolean" + } + }, + "additionalProperties": false }, - "baseUriDomains": { - "description": "Host supports base-uri domain allowlisting.", - "type": "boolean" + "csp": { + "description": "CSP domains approved by the host.", + "type": "object", + "properties": { + "connectDomains": { + "description": "Origins for network requests (fetch/XHR/WebSocket).", + "type": "array", + "items": { + "type": "string" + } + }, + "resourceDomains": { + "description": "Origins for static resources (scripts, images, styles, fonts).", + "type": "array", + "items": { + "type": "string" + } + }, + "frameDomains": { + "description": "Origins for nested iframes (frame-src directive).", + "type": "array", + "items": { + "type": "string" + } + }, + "baseUriDomains": { + "description": "Allowed base URIs for the document (base-uri directive).", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/src/generated/schema.test.ts b/src/generated/schema.test.ts index becbd115..b2f77fbd 100644 --- a/src/generated/schema.test.ts +++ b/src/generated/schema.test.ts @@ -79,20 +79,20 @@ export type McpUiResourceTeardownResultSchemaInferredType = z.infer< typeof generated.McpUiResourceTeardownResultSchema >; -export type McpUiHostCapabilitiesSchemaInferredType = z.infer< - typeof generated.McpUiHostCapabilitiesSchema +export type McpUiResourceCspSchemaInferredType = z.infer< + typeof generated.McpUiResourceCspSchema >; export type McpUiAppCapabilitiesSchemaInferredType = z.infer< typeof generated.McpUiAppCapabilitiesSchema >; -export type McpUiInitializedNotificationSchemaInferredType = z.infer< - typeof generated.McpUiInitializedNotificationSchema +export type McpUiHostCapabilitiesSchemaInferredType = z.infer< + typeof generated.McpUiHostCapabilitiesSchema >; -export type McpUiResourceCspSchemaInferredType = z.infer< - typeof generated.McpUiResourceCspSchema +export type McpUiInitializedNotificationSchemaInferredType = z.infer< + typeof generated.McpUiInitializedNotificationSchema >; export type McpUiResourceMetaSchemaInferredType = z.infer< @@ -221,26 +221,26 @@ expectType( expectType( {} as spec.McpUiResourceTeardownResult, ); -expectType( - {} as McpUiHostCapabilitiesSchemaInferredType, -); -expectType( - {} as spec.McpUiHostCapabilities, -); +expectType({} as McpUiResourceCspSchemaInferredType); +expectType({} as spec.McpUiResourceCsp); expectType( {} as McpUiAppCapabilitiesSchemaInferredType, ); expectType( {} as spec.McpUiAppCapabilities, ); +expectType( + {} as McpUiHostCapabilitiesSchemaInferredType, +); +expectType( + {} as spec.McpUiHostCapabilities, +); expectType( {} as McpUiInitializedNotificationSchemaInferredType, ); expectType( {} as spec.McpUiInitializedNotification, ); -expectType({} as McpUiResourceCspSchemaInferredType); -expectType({} as spec.McpUiResourceCsp); expectType({} as McpUiResourceMetaSchemaInferredType); expectType({} as spec.McpUiResourceMeta); expectType( diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 15b38f62..85ecd91e 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -324,6 +324,55 @@ export const McpUiResourceTeardownResultSchema = z.record( z.unknown(), ); +/** + * @description Content Security Policy configuration for UI resources. + */ +export const McpUiResourceCspSchema = z.object({ + /** @description Origins for network requests (fetch/XHR/WebSocket). */ + connectDomains: z + .array(z.string()) + .optional() + .describe("Origins for network requests (fetch/XHR/WebSocket)."), + /** @description Origins for static resources (scripts, images, styles, fonts). */ + resourceDomains: z + .array(z.string()) + .optional() + .describe("Origins for static resources (scripts, images, styles, fonts)."), + /** @description Origins for nested iframes (frame-src directive). */ + frameDomains: z + .array(z.string()) + .optional() + .describe("Origins for nested iframes (frame-src directive)."), + /** @description Allowed base URIs for the document (base-uri directive). */ + baseUriDomains: z + .array(z.string()) + .optional() + .describe("Allowed base URIs for the document (base-uri directive)."), +}); + +/** + * @description Capabilities provided by the Guest UI (App). + * @see {@link McpUiInitializeRequest} for the initialization request that includes these capabilities + */ +export const McpUiAppCapabilitiesSchema = z.object({ + /** @description Experimental features (structure TBD). */ + experimental: z + .object({}) + .optional() + .describe("Experimental features (structure TBD)."), + /** @description App exposes MCP-style tools that the host can call. */ + tools: z + .object({ + /** @description App supports tools/list_changed notifications. */ + listChanged: z + .boolean() + .optional() + .describe("App supports tools/list_changed notifications."), + }) + .optional() + .describe("App exposes MCP-style tools that the host can call."), +}); + /** * @description Capabilities supported by the host application. * @see {@link McpUiInitializeResult} for the initialization result that includes these capabilities @@ -363,45 +412,20 @@ export const McpUiHostCapabilitiesSchema = z.object({ .describe("Host can proxy resource reads to the MCP server."), /** @description Host accepts log messages. */ logging: z.object({}).optional().describe("Host accepts log messages."), - /** @description CSP overrides the host supports for sandbox proxies. */ - csp: z - .object({ - /** @description Host supports frame-src domain allowlisting. */ - frameDomains: z - .boolean() - .optional() - .describe("Host supports frame-src domain allowlisting."), - /** @description Host supports base-uri domain allowlisting. */ - baseUriDomains: z - .boolean() - .optional() - .describe("Host supports base-uri domain allowlisting."), - }) - .optional() - .describe("CSP overrides the host supports for sandbox proxies."), -}); - -/** - * @description Capabilities provided by the Guest UI (App). - * @see {@link McpUiInitializeRequest} for the initialization request that includes these capabilities - */ -export const McpUiAppCapabilitiesSchema = z.object({ - /** @description Experimental features (structure TBD). */ - experimental: z - .object({}) - .optional() - .describe("Experimental features (structure TBD)."), - /** @description App exposes MCP-style tools that the host can call. */ - tools: z + /** @description Sandbox configuration applied by the host. */ + sandbox: z .object({ - /** @description App supports tools/list_changed notifications. */ - listChanged: z - .boolean() - .optional() - .describe("App supports tools/list_changed notifications."), + /** @description Permissions granted by the host (camera, microphone, geolocation). */ + permissions: McpUiResourcePermissionsSchema.optional().describe( + "Permissions granted by the host (camera, microphone, geolocation).", + ), + /** @description CSP domains approved by the host. */ + csp: McpUiResourceCspSchema.optional().describe( + "CSP domains approved by the host.", + ), }) .optional() - .describe("App exposes MCP-style tools that the host can call."), + .describe("Sandbox configuration applied by the host."), }); /** @@ -413,32 +437,6 @@ export const McpUiInitializedNotificationSchema = z.object({ params: z.object({}).optional(), }); -/** - * @description Content Security Policy configuration for UI resources. - */ -export const McpUiResourceCspSchema = z.object({ - /** @description Origins for network requests (fetch/XHR/WebSocket). */ - connectDomains: z - .array(z.string()) - .optional() - .describe("Origins for network requests (fetch/XHR/WebSocket)."), - /** @description Origins for static resources (scripts, images, styles, fonts). */ - resourceDomains: z - .array(z.string()) - .optional() - .describe("Origins for static resources (scripts, images, styles, fonts)."), - /** @description Origins for nested iframes (frame-src directive). */ - frameDomains: z - .array(z.string()) - .optional() - .describe("Origins for nested iframes (frame-src directive)."), - /** @description Allowed base URIs for the document (base-uri directive). */ - baseUriDomains: z - .array(z.string()) - .optional() - .describe("Allowed base URIs for the document (base-uri directive)."), -}); - /** * @description UI Resource metadata for security and rendering configuration. */ diff --git a/src/spec.types.ts b/src/spec.types.ts index 122b757c..6f1662fe 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -420,12 +420,12 @@ export interface McpUiHostCapabilities { }; /** @description Host accepts log messages. */ logging?: {}; - /** @description CSP overrides the host supports for sandbox proxies. */ - csp?: { - /** @description Host supports frame-src domain allowlisting. */ - frameDomains?: boolean; - /** @description Host supports base-uri domain allowlisting. */ - baseUriDomains?: boolean; + /** @description Sandbox configuration applied by the host. */ + sandbox?: { + /** @description Permissions granted by the host (camera, microphone, geolocation). */ + permissions?: McpUiResourcePermissions; + /** @description CSP domains approved by the host. */ + csp?: McpUiResourceCsp; }; } From ad92da0e18a588ff6449e3a94dbb7a94aae6ee3f Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 12 Jan 2026 12:24:13 +0000 Subject: [PATCH 4/7] feat: add clipboard-write permission support Adds clipboardWrite to McpUiResourcePermissions for clipboard access. Maps to Permission Policy 'clipboard-write' feature. --- .claude/commands/README.md | 99 +++++++ .claude/commands/setup-chatgpt-aggregator.md | 226 ++++++++++++++++ .gitignore | 1 + .mcp.json | 13 + examples/basic-host/src/sandbox.ts | 2 + intermediate-outputs/model-post-analysis.md | 222 ++++++++++++++++ intermediate-outputs/pr-158-analysis.md | 174 ++++++++++++ intermediate-outputs/pr-204-analysis.md | 248 ++++++++++++++++++ intermediate-outputs/pr-224-analysis.md | 166 ++++++++++++ .../release-notes-analysis.md | 241 +++++++++++++++++ .../resize-investigation-findings.md | 96 +++++++ package-lock.json | 1 - simple.ts | 100 +++++++ specification/draft/apps.mdx | 11 +- src/generated/schema.json | 20 ++ src/generated/schema.ts | 7 + src/spec.types.ts | 2 + 17 files changed, 1627 insertions(+), 2 deletions(-) create mode 100644 .claude/commands/README.md create mode 100644 .claude/commands/setup-chatgpt-aggregator.md create mode 100644 .mcp.json create mode 100644 intermediate-outputs/model-post-analysis.md create mode 100644 intermediate-outputs/pr-158-analysis.md create mode 100644 intermediate-outputs/pr-204-analysis.md create mode 100644 intermediate-outputs/pr-224-analysis.md create mode 100644 intermediate-outputs/release-notes-analysis.md create mode 100644 intermediate-outputs/resize-investigation-findings.md create mode 100644 simple.ts diff --git a/.claude/commands/README.md b/.claude/commands/README.md new file mode 100644 index 00000000..7234de14 --- /dev/null +++ b/.claude/commands/README.md @@ -0,0 +1,99 @@ +# Claude Code Custom Commands (Skills) + +This directory contains custom slash commands for Claude Code. + +## What are Skills/Commands? + +Claude Code commands are markdown files that get expanded as prompts when you invoke them with `/command-name`. They let you create reusable workflows. + +## Available Commands + +| Command | Description | +| --------------------------- | --------------------------------------------------------- | +| `/setup-chatgpt-aggregator` | Automate ChatGPT Apps connector setup with MCP Aggregator | + +## How to Use + +1. In Claude Code, type `/` followed by the command name +2. The command's markdown content becomes part of the conversation +3. Claude executes the workflow described in the command + +Example: + +``` +/setup-chatgpt-aggregator +``` + +## How to Package/Share + +Commands are just markdown files. To share: + +1. **Copy the file** - Share `.claude/commands/*.md` files +2. **Include in repo** - Commit to `.claude/commands/` in your project +3. **Global commands** - Put in `~/.claude/commands/` for all projects + +### Directory Structure + +``` +.claude/ +├── commands/ +│ ├── README.md # This file +│ └── setup-chatgpt-aggregator.md # ChatGPT setup skill +└── settings.local.json # Local permissions +``` + +## Prerequisites for setup-chatgpt-aggregator + +1. **Enable the chrome MCP server**: + + ``` + /mcp + # Select "chrome" → "Enable" + ``` + +2. **Install cloudflared**: + + ```bash + brew install cloudflare/cloudflare/cloudflared + ``` + +3. **Have a ChatGPT account** with developer mode access + +## Creating New Commands + +Create a new `.md` file in this directory: + +```markdown +# My Command Name + +Description of what this command does. + +## Steps + +1. First step... +2. Second step... +``` + +The filename (without `.md`) becomes the command name: + +- `my-command.md` → `/my-command` + +## MCP Server Dependency + +The `setup-chatgpt-aggregator` skill requires the `chrome` MCP server defined in `.mcp.json`: + +```json +{ + "mcpServers": { + "chrome": { + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest", "--headless=false"] + } + } +} +``` + +## More Info + +- [Claude Code Docs](https://docs.anthropic.com/en/docs/claude-code) +- [MCP Protocol](https://modelcontextprotocol.io) diff --git a/.claude/commands/setup-chatgpt-aggregator.md b/.claude/commands/setup-chatgpt-aggregator.md new file mode 100644 index 00000000..ba0d2cba --- /dev/null +++ b/.claude/commands/setup-chatgpt-aggregator.md @@ -0,0 +1,226 @@ +# Setup ChatGPT with MCP Aggregator Server + +Automate the setup of ChatGPT Apps connector with the local MCP Aggregator server. + +## FIRST: Enable the Chrome MCP Server + +Before running this skill, enable the chrome MCP server: + +1. Run `/mcp` in Claude Code +2. Select "chrome" from the list +3. Choose "Enable" + +Or manually add to `~/.claude.json` under the project's `enabledMcpjsonServers` array. + +## Overview + +This skill automates: + +1. Starting the aggregator server (port 3100) with all example backend servers +2. Creating a cloudflared tunnel to expose the aggregator +3. Opening ChatGPT.com and guiding through authentication +4. Configuring the Apps connector with the tunnel URL +5. Testing each tool from the aggregator + +## Prerequisites + +- `cloudflared` installed (`brew install cloudflare/cloudflare/cloudflared`) +- Chrome browser available +- ChatGPT account with developer mode access +- `chrome` MCP server enabled (see above) + +## Execution Steps + +### Step 1: Start Backend Servers and Aggregator + +First, start the example servers that will feed into the aggregator: + +```bash +# Start all example servers in background +npm run examples:start & + +# Wait for servers to be ready +sleep 5 + +# Start the aggregator server pointing to all backends +BACKEND_SERVERS='["http://localhost:3101/mcp","http://localhost:3102/mcp","http://localhost:3103/mcp","http://localhost:3104/mcp","http://localhost:3105/mcp","http://localhost:3106/mcp","http://localhost:3107/mcp","http://localhost:3108/mcp","http://localhost:3109/mcp"]' \ +PORT=3100 bun examples/aggregator-server/server.ts & + +# Wait for aggregator +sleep 3 +``` + +### Step 2: Start Cloudflared Tunnel + +```bash +# Start tunnel and capture the URL +cloudflared tunnel --url http://localhost:3100 2>&1 | tee /tmp/cloudflared.log & + +# Wait for tunnel URL to appear +sleep 5 + +# Extract the tunnel URL +TUNNEL_URL=$(grep -oE 'https://[a-z0-9-]+\.trycloudflare\.com' /tmp/cloudflared.log | head -1) +echo "Tunnel URL: $TUNNEL_URL" +``` + +### Step 3: Get Tool List from Aggregator + +Query the aggregator to get all available tools: + +```bash +# Initialize session and get tools +curl -s -X POST http://localhost:3100/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' \ + | jq -r '.result.sessionId // empty' > /tmp/mcp-session-id + +SESSION_ID=$(cat /tmp/mcp-session-id) + +# List tools +curl -s -X POST http://localhost:3100/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "mcp-session-id: $SESSION_ID" \ + -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \ + | jq -r '.result.tools[].name' > /tmp/aggregator-tools.txt + +echo "Available tools:" +cat /tmp/aggregator-tools.txt +``` + +### Step 4: Browser Automation with Chrome DevTools MCP + +Use the `chrome` MCP server to automate the ChatGPT setup. The MCP tools available are: + +- `browser_navigate` - Navigate to a URL +- `browser_click` - Click an element +- `browser_type` - Type text into an element +- `browser_snapshot` - Take accessibility snapshot +- `browser_wait` - Wait for element or timeout + +#### 4.1 Open ChatGPT and Authenticate + +``` +# Navigate to ChatGPT +Use chrome MCP: browser_navigate to "https://chatgpt.com" + +# Run audio prompt for user +Run: say "Please authenticate to ChatGPT. Press any key when done." +Read a line from stdin to wait. + +# Take snapshot to verify logged in +Use chrome MCP: browser_snapshot +Verify the page shows logged-in state (look for profile menu or chat input) +``` + +#### 4.2 Check for Developer Mode + +``` +# Take snapshot of the main input area +Use chrome MCP: browser_snapshot + +# Look for "developer mode" indicator near the input box +# This may appear as a badge or overlay on the input +``` + +#### 4.3 Navigate to Settings > Apps & Connectors + +``` +# Click the profile/settings menu (usually top-right) +Use chrome MCP: browser_click on the profile menu button + +# Click "Settings" in the dropdown +Use chrome MCP: browser_click on "Settings" + +# Wait for settings panel +Use chrome MCP: browser_wait for settings panel + +# Click "Apps & Connectors" or similar tab +Use chrome MCP: browser_click on "Apps & Connectors" +``` + +#### 4.4 Create New App Connector + +``` +# Click "Create App" or "Add Connector" button +Use chrome MCP: browser_click on the create/add button + +# Fill in the form: +# - Name: "Aggregator" +Use chrome MCP: browser_type "Aggregator" in the name field + +# - URL: The tunnel URL + /mcp +Use chrome MCP: browser_type "$TUNNEL_URL/mcp" in the URL field + +# Click continue/next if there's a confirmation +Use chrome MCP: browser_click on continue button + +# Select "No authentication" option +Use chrome MCP: browser_click on "None" or "No auth" option + +# Click "Create" to finalize +Use chrome MCP: browser_click on "Create" button +``` + +#### 4.5 Test the Connector + +``` +# Go back to main chat +Use chrome MCP: browser_navigate to "https://chatgpt.com" + +# Click the input box +Use chrome MCP: browser_click on the chat input + +# Type @Aggregator to trigger autocomplete +Use chrome MCP: browser_type "@Aggregator" + +# Wait for autocomplete dropdown +Use chrome MCP: browser_wait for autocomplete suggestions + +# Verify "Aggregator" appears in suggestions +Use chrome MCP: browser_snapshot to verify + +# Press Enter to select +Use chrome MCP: browser_type with Enter key + +# Verify it shows as selected (badge below input) +Use chrome MCP: browser_snapshot to verify selection badge +``` + +#### 4.6 Test Each Tool + +For each tool in `/tmp/aggregator-tools.txt`: + +``` +# Type a test prompt for this tool +Use chrome MCP: browser_type "use the {TOOL_NAME} mcp" + +# Submit the message +Use chrome MCP: browser_type with Enter key + +# Wait for response +Use chrome MCP: browser_wait for response to appear + +# Take snapshot to verify +Use chrome MCP: browser_snapshot +``` + +## Cleanup + +When done: + +```bash +# Kill background processes +pkill -f cloudflared +pkill -f "bun examples/aggregator-server" +pkill -f "npm run examples:start" +``` + +## Notes + +- The exact selectors for ChatGPT UI elements may change; use `browser_snapshot` to inspect the current DOM +- Developer mode access is required for Apps & Connectors +- The tunnel URL changes each time cloudflared starts +- All tools from the aggregator are prefixed with the backend server name (e.g., `basic-mcp-app-server-react/get-time`) diff --git a/.gitignore b/.gitignore index 1d78df59..1aae3dab 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ intermediate-findings/ # Playwright playwright-report/ test-results/ +.npm-cache.tmp diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 00000000..8c43524f --- /dev/null +++ b/.mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "chrome": { + "command": "npx", + "args": [ + "-y", + "chrome-devtools-mcp@latest", + "--headless=false", + "--userDataDir=/tmp/chatgpt-setup-chrome-profile" + ] + } + } +} diff --git a/examples/basic-host/src/sandbox.ts b/examples/basic-host/src/sandbox.ts index 562899ec..e38400f0 100644 --- a/examples/basic-host/src/sandbox.ts +++ b/examples/basic-host/src/sandbox.ts @@ -71,6 +71,7 @@ function buildAllowAttribute(permissions?: { camera?: boolean; microphone?: boolean; geolocation?: boolean; + clipboardWrite?: boolean; }): string { if (!permissions) return ""; @@ -78,6 +79,7 @@ function buildAllowAttribute(permissions?: { if (permissions.camera) allowList.push("camera"); if (permissions.microphone) allowList.push("microphone"); if (permissions.geolocation) allowList.push("geolocation"); + if (permissions.clipboardWrite) allowList.push("clipboard-write"); return allowList.join("; "); } diff --git a/intermediate-outputs/model-post-analysis.md b/intermediate-outputs/model-post-analysis.md new file mode 100644 index 00000000..82617572 --- /dev/null +++ b/intermediate-outputs/model-post-analysis.md @@ -0,0 +1,222 @@ +# Analysis of Model Discussion Post #181 + +**Source**: https://github.com/modelcontextprotocol/ext-apps/discussions/181 +**Title**: "Changes from 0.1.x to 0.2.2" +**Author**: @ochafik +**Category**: Announcements +**Date**: 2025-12-18 + +--- + +## 1. Overall Structure and Format + +The post follows a **changelog-style announcement** pattern with: + +1. **Opening paragraph** - Brief, friendly intro explaining context (multiple versions released quickly) +2. **Warning callout** - Critical migration note at the top +3. **Categorized sections** - Logically grouped changes under headers +4. **Footer link** - Full changelog reference + +### Section Order: + +1. Intro paragraph +2. Warning callout (GitHub alert syntax) +3. `### Highlights` - Major new features +4. `### API Changes` - Breaking/behavioral changes +5. `### Platform & DX` - Developer experience improvements +6. `### Bug Fixes` - Fixes +7. Full changelog link + +--- + +## 2. Tone and Style + +- **Friendly and casual**: Opens with "Hi all!" +- **Direct and informative**: Gets straight to the point +- **Scannable**: Heavy use of bold for key terms, bullet points for details +- **Professional but approachable**: Uses "we've" and conversational language + +--- + +## 3. Key Sections and Formatting Conventions + +### Opening Paragraph + +```markdown +Hi all! We've just pushed @modelcontextprotocol/ext-apps versions [0.2.0](...), [0.2.1](...) and [0.2.2](...) in quick succession, so here's what's changed since 0.1.x +``` + +- Links to each version's release page +- Explains the "why" briefly + +### Warning Callout (for Breaking Changes) + +```markdown +> [!WARNING] +> The `registerAppTool` helper makes sure defining `_meta.ui.resourceUri` is backward-compatible w/ clients expecting `"ui/resourceUri"` +``` + +- Uses GitHub's alert syntax `> [!WARNING]` +- Placed prominently at the top, before section headers +- Explains the migration path, not just the breaking change + +### Section Headers + +- Uses `###` (H3) for main sections +- Section names are short and descriptive: + - `Highlights` + - `API Changes` + - `Platform & DX` + - `Bug Fixes` + +### Bullet Point Format + +Each bullet follows this pattern: + +```markdown +- **Feature name** — Description with context. Links [#123](url) +``` + +Key conventions: + +- **Bold** for the feature/change name (2-4 words) +- **Em dash** (`—`) as separator (not hyphen or colon) +- Description explains the "what" and "why" +- PR link at the end with `[#123](url)` format +- Multiple related PRs can be listed + +### Examples of Well-Formatted Bullets: + +**New Feature:** + +```markdown +- **Server helpers** — New `registerAppTool()` and `registerAppResource()` simplify server setup with proper type safety. `connect()` now defaults to `PostMessageTransport(window.parent)`, enabling simpler initialization with just `await app.connect()` [#165](...) +``` + +**Breaking Change:** + +```markdown +- **MCP SDK as peer dependency** — Consumers control their SDK version, reducing duplication [#168](...) +``` + +**Bug Fix:** + +```markdown +- **Responsive UIs** — Fixed narrow viewport handling for mobile and sidebar experiences [#135](...) +``` + +--- + +## 4. How Breaking Changes vs New Features are Presented + +### Breaking Changes Get Special Treatment: + +1. **Warning callout at top** - The most critical breaking change uses `> [!WARNING]` syntax +2. **Placed in "API Changes" section** - Not hidden, but clearly separated from "Highlights" +3. **Migration path included** - Notes how backward compatibility is maintained +4. **Explains the benefit** - e.g., "Consumers control their SDK version, reducing duplication" + +### New Features (Highlights): + +- Listed first, emphasizing value +- Focus on capability gains +- More descriptive explanations + +### API Changes (Breaking/Behavioral): + +- Listed after highlights +- Focus on what changed and why +- Often includes deprecated alias notes: "Deprecated aliases maintained" + +### The Distinction: + +| Section | Focus | Tone | +| ------------- | -------------------- | --------------------------------- | +| Highlights | New capabilities | "Enables X", "New Y" | +| API Changes | What changed | "Now X", "Renamed Y", "Removed Z" | +| Platform & DX | Developer experience | "Support for X", "Widened Y" | +| Bug Fixes | What was broken | "Fixed X" | + +--- + +## 5. Conventions Used + +### Formatting Elements: + +- **Bold** (`**text**`) — Feature/change names +- **Em dash** (`—`) — Separator after bold title +- **Backticks** (`` `code` ``) — Code, function names, config keys +- **Links** (`[text](url)`) — Version releases, PR references +- **GitHub Alert** (`> [!WARNING]`) — Critical migration notes + +### No Emojis + +The post does **not** use emojis anywhere. It maintains a clean, professional look relying on: + +- Bold text for emphasis +- Section headers for organization +- Warning callouts for critical notes + +### PR Reference Format: + +```markdown +[#123](https://github.com/modelcontextprotocol/ext-apps/pull/123) +``` + +- Number only, no description +- Full URL (not shorthand) +- Placed at end of bullet + +### Footer: + +```markdown +**Full Changelog**: https://github.com/modelcontextprotocol/ext-apps/compare/v0.1.1...v0.2.2 +``` + +- Bold label +- Compare URL for full diff + +--- + +## 6. Template for Future Posts + +```markdown +Hi all! We've just released [package] version [X.Y.Z](release-url), here's what's changed since [previous version]. + +> [!WARNING] +> [Critical migration note if any breaking changes] + +### Highlights + +- **Feature Name** — Description of what it does and why it matters [#PR](url) +- **Another Feature** — Description [#PR](url) + +### API Changes + +- **Breaking Change** — What changed and how to migrate. Old behavior deprecated [#PR](url) +- **Renamed Method** — Old `sendFoo()` is now `foo()`. Deprecated aliases maintained [#PR](url) + +### Platform & DX + +- **Improvement** — Description of developer experience improvement [#PR](url) + +### Bug Fixes + +- **Fix Title** — Fixed [what was broken] [#PR](url) + +**Full Changelog**: [compare-url] +``` + +--- + +## 7. Key Takeaways for Writing Similar Posts + +1. **Lead with context** - Explain why the post exists (multiple versions, major release, etc.) +2. **Highlight breaking changes early** - Use `> [!WARNING]` callout before sections +3. **Organize by impact category** - Highlights > API Changes > DX > Fixes +4. **Be consistent with formatting** - Bold title, em dash, description, PR link +5. **Explain the "why"** - Don't just list changes, explain benefits +6. **Provide migration paths** - For breaking changes, show how to adapt +7. **Keep it scannable** - Bold keywords, bullets, headers +8. **No emojis** - Professional, clean style +9. **Link everything** - Releases, PRs, changelogs diff --git a/intermediate-outputs/pr-158-analysis.md b/intermediate-outputs/pr-158-analysis.md new file mode 100644 index 00000000..8ade61a9 --- /dev/null +++ b/intermediate-outputs/pr-158-analysis.md @@ -0,0 +1,174 @@ +# PR #158 Analysis: Enhance Sandbox Capability Negotiation + +## Summary + +**Title:** feat: enhance sandbox capability negotiation +**Author:** Ido Salomon (@idosal) +**State:** OPEN +**Branch:** `feat/sandbox-capabilities` -> `main` +**Related Issue:** https://github.com/modelcontextprotocol/ext-apps/issues/58 + +--- + +## 1. What the PR is Trying to Achieve + +This PR enhances the MCP Apps sandbox capability negotiation system by adding support for: + +### 1.1 New CSP Directives + +- **`frameDomains`**: Controls which origins can be used for nested iframes (maps to CSP `frame-src` directive) +- **`baseUriDomains`**: Controls allowed base URIs for the document (maps to CSP `base-uri` directive) + +### 1.2 Browser Permissions + +Introduces a `permissions` attribute (`ui/permissions`) in Resource Metadata supporting: + +- `camera`: Request camera access +- `microphone`: Request microphone access +- `geolocation`: Request geolocation access + +These map to the iframe's `allow` attribute for Permission Policy features. + +### 1.3 Host Capability Negotiation + +Adds `csp` to Host<>App capability negotiation so apps can detect at runtime which CSP features and permissions the host supports (since this is non-trivial to detect otherwise). + +--- + +## 2. Comment History and Discussions + +### Key Discussion Points: + +1. **@matteo8p (Dec 22)**: Asked where iFrame permissions are enforced and suggested using `document.featurePolicy.allowedFeatures()` as a runtime source of truth instead of negotiation. + +2. **@idosal (Jan 7)**: Responded that permissions were originally excluded from negotiation because they could be detected directly, but the negotiation approach assumes less about the app runtime and developer's awareness of browser APIs. Community input needed. + +3. **@matteo8p (Dec 22)**: Shared that OpenAI currently allows `local-network-access *; microphone *; midi *` in their iframe permissions. + +4. **@aharvard (Jan 8)**: Announced implementation of `frameDomains` and `baseUriDomains` in Goose: https://github.com/block/goose/pull/6399 + +5. **@ochafik (Jan 11)**: Created PR #234 for reference CSP handling in the sandbox server with `frameDomains` and `baseUriDomains`. Suggested this PR focus on sandbox permissions only. + +6. **@ochafik (Jan 12)**: Created a speech example (PR #240) relying on microphone and clipboard-write. Offered to help refresh/push to this branch. + +### Reviews: + +- **Copilot reviewer**: Generated automated review with 4 comments (overview only) +- **@ochafik**: Requested changes - "Thanks Ido!" and later commented "We should tell app what permissions were granted (and potentially which domains were allowed)" +- **@matteo8p**: Commented on permissions detection approach +- **@domfarolino**: Commented (no content shown in review body) + +--- + +## 3. What Changes Are Currently in the PR + +### 3.1 Specification Changes (`specification/draft/apps.mdx`) + +- Added `frameDomains` and `baseUriDomains` to `UIResourceMeta.csp` +- Added `permissions` object with `camera`, `microphone`, `geolocation` booleans +- Updated sandbox requirements documentation +- Added new `HostCapabilities` interface with `sandbox` section including permissions and CSP support + +### 3.2 Type System Changes + +**`src/spec.types.ts`**: + +- Extended `McpUiSandboxResourceReadyNotification` with `frameDomains`, `baseUriDomains`, and `permissions` +- Added `sandbox` to `McpUiHostCapabilities` +- Extended `McpUiResourceCsp` with `frameDomains` and `baseUriDomains` +- Added new `McpUiResourcePermissions` interface +- Extended `McpUiResourceMeta` with `permissions` + +**`src/generated/schema.ts`**: + +- Added `McpUiResourcePermissionsSchema` (camera, microphone, geolocation booleans) +- Extended `McpUiResourceCspSchema` with frameDomains, baseUriDomains +- Extended `McpUiHostCapabilitiesSchema` with sandbox section +- Updated `McpUiResourceMetaSchema` to include permissions +- Updated `McpUiSandboxResourceReadyNotificationSchema` with new fields + +**`src/types.ts`**: + +- Exported new `McpUiResourcePermissions` type and `McpUiResourcePermissionsSchema` + +### 3.3 Implementation Changes + +**`examples/basic-host/src/implementation.ts`**: + +- Extended `UiResourceData` interface with `frameDomains`, `baseUriDomains`, and `permissions` +- Updated resource extraction to include permissions metadata +- Updated `sendSandboxResourceReady` call to include permissions + +**`examples/basic-host/src/sandbox.ts`**: + +- Extended `buildCspMetaTag()` to handle `frameDomains` and `baseUriDomains` +- Added `buildAllowAttribute()` function for Permission Policy +- Updated message handler to set iframe `allow` attribute based on permissions + +**`examples/simple-host/sandbox.html`** (NEW): + +- Added new sandbox proxy HTML implementation +- Includes `buildAllowAttribute()` function +- Uses `document.write()` pattern instead of srcdoc for CSP compatibility +- Note: CSP handled via HTTP response headers in serve.ts, not meta tags + +### 3.4 Package Changes + +- Added `cross-env` dependency (^10.1.0) to multiple example packages +- Various package-lock.json updates (removing "peer" markers from several dependencies) + +### 3.5 Schema/Test Updates + +- Updated `src/generated/schema.json` with new types +- Updated `src/generated/schema.test.ts` with new type inference tests + +--- + +## 4. Issues, Conflicts, and Open Questions + +### 4.1 Potential Overlap with Other PRs + +- **PR #234**: @ochafik's PR for CSP handling in sandbox server already includes `frameDomains` and `baseUriDomains`. This creates potential overlap/conflict. +- **Suggestion**: Focus this PR on sandbox permissions only, let PR #234 handle CSP domain extensions + +### 4.2 Open Design Questions + +1. **Runtime detection vs negotiation**: Should apps use `document.featurePolicy.allowedFeatures()` at runtime instead of relying on host capability negotiation for permissions? +2. **Permission scope**: Currently limited to camera, microphone, geolocation. Other permissions (like `midi` in OpenAI's setup, or `clipboard-write` for the speech example) may be needed. + +### 4.3 Missing Implementation + +- E2E tests not yet added (author mentioned will add before merging) +- No verification that all example hosts properly implement the new features + +### 4.4 Collaboration Request + +- @ochafik offered to help refresh/push to this branch (Jan 12) + +--- + +## 5. Files Changed Summary + +| File | Changes | +| ------------------------------------------- | ----------------------------------------------------------- | +| `specification/draft/apps.mdx` | +137 lines - New CSP fields and permissions documentation | +| `src/spec.types.ts` | +38 lines - New interfaces for permissions and extended CSP | +| `src/generated/schema.ts` | +117 lines - Zod schemas for new types | +| `src/generated/schema.json` | +195 lines - JSON schema updates | +| `src/generated/schema.test.ts` | +26/-20 lines - Type test updates | +| `src/types.ts` | +2 lines - New exports | +| `examples/basic-host/src/implementation.ts` | +10/-3 lines - Permissions extraction | +| `examples/basic-host/src/sandbox.ts` | +40/-5 lines - Permission Policy implementation | +| `examples/simple-host/sandbox.html` | +128 lines (NEW) - New sandbox proxy | +| Multiple `package.json` files | +1 line each - cross-env dependency | +| `package-lock.json` | Various updates | + +--- + +## 6. Recommendations + +1. **Coordinate with PR #234**: Clarify scope division between this PR and the CSP handling PR +2. **Add E2E tests**: As author noted, tests needed before merge +3. **Consider additional permissions**: `clipboard-write`, `midi`, etc. based on real-world usage +4. **Document runtime detection option**: Even if negotiation is preferred, apps should know about `featurePolicy.allowedFeatures()` as fallback +5. **Review simple-host sandbox.html**: New file needs careful security review diff --git a/intermediate-outputs/pr-204-analysis.md b/intermediate-outputs/pr-204-analysis.md new file mode 100644 index 00000000..d623f6d5 --- /dev/null +++ b/intermediate-outputs/pr-204-analysis.md @@ -0,0 +1,248 @@ +# PR #204 Analysis: chore: release 0.3.0 + +**Repository:** modelcontextprotocol/ext-apps +**PR URL:** https://github.com/modelcontextprotocol/ext-apps/pull/204 +**Author:** ochafik (Olivier Chafik) +**State:** MERGED +**Created:** 2026-01-07T11:36:03Z +**Merged:** 2026-01-09T00:55:45Z +**Base Branch:** main +**Head Branch:** ochafik/release-0.3.0 + +--- + +## 1. Title and Description + +**Title:** `chore: release 0.3.0` + +**Summary:** This PR bumps the version from 0.2.2 to 0.3.0 and introduces release notes documenting all changes since the previous version. + +--- + +## 2. Files Changed + +**Total:** 19 files changed (+207 additions, -33 deletions) + +### New Files + +| File | Additions | Purpose | +| ---------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------- | +| `RELEASES.md` | +64 lines | New release notes document covering 0.3.0 and 0.2.2 changes | +| `examples/video-resource-server/src/server-utils.ts` | +110 lines | Shared utilities for running MCP servers with various transports (stdio and Streamable HTTP) | + +### Version Bumps (package.json files) + +All example servers had their `@modelcontextprotocol/ext-apps` dependency bumped from `^0.2.2` to `^0.3.0`: + +1. `examples/basic-server-preact/package.json` +2. `examples/basic-server-react/package.json` +3. `examples/basic-server-solid/package.json` +4. `examples/basic-server-svelte/package.json` +5. `examples/basic-server-vanillajs/package.json` +6. `examples/basic-server-vue/package.json` +7. `examples/budget-allocator-server/package.json` +8. `examples/cohort-heatmap-server/package.json` +9. `examples/customer-segmentation-server/package.json` +10. `examples/scenario-modeler-server/package.json` +11. `examples/sheet-music-server/package.json` +12. `examples/system-monitor-server/package.json` +13. `examples/threejs-server/package.json` +14. `examples/video-resource-server/package.json` +15. `examples/wiki-explorer-server/package.json` + +### Root Package Updates + +- `package.json` - version bump to 0.3.0 +- `package-lock.json` - updated lockfile reflecting version changes + +--- + +## 3. Purpose/Goal of the PR + +The primary purpose is to **release version 0.3.0** of the MCP Apps SDK. This involves: + +1. **Version bump** from 0.2.2 to 0.3.0 +2. **Creating comprehensive release notes** (RELEASES.md) documenting all changes +3. **Updating all example server dependencies** to use the new version +4. **Adding shared server utilities** for the video-resource-server example + +--- + +## 4. Breaking Changes + +Two breaking changes are documented in this release: + +### 4.1 `viewport` replaced with `containerDimensions` (PR #153) + +- **Before:** Host context had a `viewport` property +- **After:** Replaced with `containerDimensions` which provides clearer semantics +- **Impact:** The new type allows specifying either: + - `height` OR `maxHeight` + - `width` OR `maxWidth` +- **Reason:** Provides clearer distinction between fixed dimensions and maximum constraints +- **Migration:** Apps using `viewport` must update to use `containerDimensions` instead + +### 4.2 `eventSource` now required in `PostMessageTransport` (PR #208) + +- **Before:** `eventSource` parameter was optional in constructor +- **After:** `eventSource` parameter is now required +- **Impact:** Security improvement - enforces validation of message sources +- **Reason:** Prevents potential cross-app message spoofing attacks +- **Migration:** Callers must explicitly provide the `eventSource` parameter + +--- + +## 5. New Features and Improvements + +### 5.1 New Framework Examples + +- **Vue, Svelte, Preact, and Solid examples** (PR #141) + - `basic-server-vue` + - `basic-server-svelte` + - `basic-server-preact` + - `basic-server-solid` + +### 5.2 `safeAreaInsets` Support (PR #202) + +- Host context now includes safe area insets +- Allows apps to properly handle device notches and system UI elements +- All example apps updated to use this for proper padding + +### 5.3 `ui.resourceUri` Optional (PR #210) + +- Tools that need visibility but don't require a UI resource can now omit `resourceUri` in the schema + +### 5.4 Method Names Exported as Constants (PR #192) + +- MCP method names are now exported as typed constants +- Enables easier external library integration without brittle schema introspection + +### 5.5 Example Servers Publishable to npm (PR #184) + +- All 15 example servers are now published to npm under `@modelcontextprotocol` scope +- Users can run examples directly via `npx @modelcontextprotocol/server-basic-react` etc. + +### 5.6 `toolInfo.id` Optional (PR #216) + +- Aligned TypeScript types with spec by making `toolInfo.id` optional +- Allows hosts to pass just the tool definition + +### 5.7 New Example Servers + +- **Video resource server** (PR #175) - Demonstrates video resource handling with proper mimeType declarations +- **Sheet music server** (PR #196) - Interactive sheet music notation example + +### 5.8 Developer Experience Improvements + +- **`npm start` alias** (PR #183) - Added as alias for `npm run examples:start` +- **Examples cleanup** (PR #182) - Improved consistency across example servers +- **Documentation fixes** (PR #188) - Fixed tsc command in docs to use tsconfig.json + +--- + +## 6. Bug Fixes + +### 6.1 Dependency Fixes + +- **Move prettier to dev dependency** (PR #179) - Fixed incorrect dependency classification +- **Fix build errors in examples** (PR #180) - Resolved build issues across example servers + +### 6.2 Host Resilience Improvements (PR #206) + +- Server connections now use `Promise.allSettled` instead of `Promise.all` +- One server failure no longer crashes the entire UI + +### 6.3 E2E Test Reliability (PR #206) + +- Fixed flaky Three.js E2E tests with reliable canvas ID masking + +--- + +## 7. Security Fixes + +### 7.1 PostMessageTransport Origin Verification (PR #207) + +- Added proper source validation to default constructor +- Added validation to sandbox proxy +- Prevents cross-origin message spoofing + +### 7.2 Security E2E Tests (PR #208) + +- Added 14 comprehensive tests for: + - Origin validation + - Cross-app message injection protection + +--- + +## 8. New Server Utilities + +A new `server-utils.ts` file was added to the video-resource-server example, providing: + +### `startServer(createServer)` + +Main entry point that detects transport mode from CLI args: + +- If `--stdio` flag: Uses stdio transport +- Otherwise: Uses Streamable HTTP transport + +### `startStdioServer(createServer)` + +- Connects server using `StdioServerTransport` + +### `startStreamableHttpServer(createServer)` + +- Runs server with Streamable HTTP transport in stateless mode +- Each request creates fresh server and transport instances +- Listens on PORT env var (default: 3001) +- Includes proper cleanup on response end +- CORS enabled +- Graceful shutdown handling (SIGINT, SIGTERM) + +--- + +## 9. Related PRs Referenced + +The release notes document changes from multiple PRs: + +| PR | Description | +| ---- | -------------------------------------------------- | +| #153 | `viewport` → `containerDimensions` breaking change | +| #208 | `eventSource` required in `PostMessageTransport` | +| #141 | Framework examples (Vue, Svelte, Preact, Solid) | +| #202 | `safeAreaInsets` support | +| #210 | `ui.resourceUri` optional | +| #192 | Method names exported as constants | +| #184 | Example servers publishable to npm | +| #216 | `toolInfo.id` optional | +| #175 | Video resource server example | +| #196 | Sheet music server example | +| #183 | `npm start` alias | +| #182 | Examples cleanup | +| #188 | Documentation fixes | +| #179 | Prettier to dev dependency | +| #180 | Fix build errors in examples | +| #206 | Host resilience + E2E test reliability | +| #207 | PostMessageTransport origin verification | + +--- + +## 10. Backward Compatibility Note + +The PR description mentions: + +> A backward compatibility shim for the deprecated `viewport` property is available in PR #TBD (branch `ochafik/viewport-compat-shim`). Consider merging that first if backward compat is needed. + +This suggests that the `viewport` → `containerDimensions` breaking change may have a compatibility shim available if needed. + +--- + +## 11. Summary + +This is a **release preparation PR** that: + +1. Bumps version to 0.3.0 (semantic versioning minor bump due to breaking changes) +2. Documents comprehensive release notes in RELEASES.md +3. Updates all example dependencies to the new version +4. Adds shared server utilities for the video-resource example + +The release includes 2 breaking changes, 8+ new features, multiple bug fixes, and security improvements. All 15 example servers are updated and can be published to npm. diff --git a/intermediate-outputs/pr-224-analysis.md b/intermediate-outputs/pr-224-analysis.md new file mode 100644 index 00000000..c4f56462 --- /dev/null +++ b/intermediate-outputs/pr-224-analysis.md @@ -0,0 +1,166 @@ +# PR #224 Analysis: chore: release 0.3.1 + +**Repository:** [modelcontextprotocol/ext-apps](https://github.com/modelcontextprotocol/ext-apps) +**PR URL:** https://github.com/modelcontextprotocol/ext-apps/pull/224 +**Author:** Olivier Chafik (@ochafik) +**State:** MERGED +**Merged At:** 2026-01-09T18:22:27Z + +--- + +## 1. Title and Description + +### Title + +`chore: release 0.3.1` + +### Description + +This PR bumps the version from 0.3.0 to 0.3.1 and consolidates several prior changes into a release. + +--- + +## 2. Purpose/Goal of the PR + +The primary goal is to **release version 0.3.1** of the MCP Apps SDK. This release: + +1. Consolidates changes from PRs #219, #221, and #185 since version 0.3.0 +2. Bumps the version across the main package and all example packages +3. Fixes the npm-publish workflow to include previously missing examples + +--- + +## 3. All Changes Made + +### Files Changed (18 total) + +| File | Additions | Deletions | Change Type | +| ---------------------------------------------------- | --------- | --------- | ---------------- | +| `.github/workflows/npm-publish.yml` | 6 | 0 | CI/CD Fix | +| `package.json` | 1 | 1 | Version Bump | +| `package-lock.json` | 85 | 17 | Lockfile Update | +| `examples/basic-server-preact/package.json` | 1 | 1 | Dep Version Bump | +| `examples/basic-server-react/package.json` | 1 | 1 | Dep Version Bump | +| `examples/basic-server-solid/package.json` | 1 | 1 | Dep Version Bump | +| `examples/basic-server-svelte/package.json` | 1 | 1 | Dep Version Bump | +| `examples/basic-server-vanillajs/package.json` | 1 | 1 | Dep Version Bump | +| `examples/basic-server-vue/package.json` | 1 | 1 | Dep Version Bump | +| `examples/budget-allocator-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/cohort-heatmap-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/customer-segmentation-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/scenario-modeler-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/sheet-music-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/system-monitor-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/threejs-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/video-resource-server/package.json` | 1 | 1 | Dep Version Bump | +| `examples/wiki-explorer-server/package.json` | 1 | 1 | Dep Version Bump | + +**Total:** +107 additions, -33 deletions + +### Detailed Changes + +#### 1. Version Bump (package.json, package-lock.json) + +- Main package version: `0.3.0` -> `0.3.1` +- All example packages updated to depend on `@modelcontextprotocol/ext-apps: "^0.3.1"` + +#### 2. npm-publish.yml Workflow Fix + +Added 6 missing examples to the publish-examples matrix: + +- `basic-server-preact` +- `basic-server-solid` +- `basic-server-svelte` +- `basic-server-vue` +- `sheet-music-server` +- `video-resource-server` + +Note: `basic-host` was initially added but then removed in a follow-up commit (3d7c88c) since it's a host demo, not a server. + +--- + +## 4. Breaking Changes + +**No breaking changes** are introduced in this PR itself. + +However, this release **includes** PR #219 which aligns the `registerAppTool` signature with the MCP TypeScript SDK. This could be considered a **potential breaking change** for existing users if: + +- They were using `registerAppTool` with the old signature +- The signature alignment with the MCP TS SDK changes how parameters are passed + +--- + +## 5. New Features or Improvements + +### From Included PRs (since 0.3.0) + +#### PR #219: `registerAppTool` Signature Alignment + +- **File:** `src/server/index.ts` (+11/-6) +- **Change:** Made `registerAppTool` more interchangeable with `server.registerTool` +- **Purpose:** Better API consistency with the MCP TypeScript SDK + +#### PR #221: Build `app-with-deps` Target + +- **Files:** `build.bun.ts`, `package.json` +- **Feature:** Added a new build target that bundles the MCP Apps SDK with all dependencies for standalone use +- **Use Case:** Enables module imports from inlined apps (e.g., loading the SDK directly from unpkg CDN in HTML) + +#### PR #185: Build Order Fix + +- **Files:** `.github/workflows/ci.yml`, `package.json`, `scripts/check-versions.mjs` +- **Feature:** + - Added `preexamples:build` hook to ensure lib is built before examples + - Added version consistency check script +- **Purpose:** Ensures examples always type-check against the latest library code + +### Direct Changes in This PR + +#### Workflow Fix + +- Fixed the npm-publish workflow to include 6 previously missing example packages +- This ensures all examples are properly published to npm when releasing + +--- + +## 6. Bug Fixes + +### Direct Fixes in This PR + +1. **Missing Examples in Publish Workflow** + - Several example packages were not being published to npm + - Added: `basic-server-preact`, `basic-server-solid`, `basic-server-svelte`, `basic-server-vue`, `sheet-music-server`, `video-resource-server` + - Removed `basic-host` (correctly classified as a host demo, not a server) + +### Included from Prior PRs + +2. **PR #185: Build Order Issue** + - Fixed: Examples were being built before the library, causing type-check failures + - Solution: Added `preexamples:build` hook + +3. **PR #219: API Signature Inconsistency** + - Fixed: `registerAppTool` signature didn't match `server.registerTool` + - Aligned the API for better consistency + +--- + +## Commits + +| Commit | Message | Date | +| ------- | ------------------------------------------------------------------------------- | -------------------- | +| b2242d0 | `chore: release 0.3.1` | 2026-01-09T17:28:00Z | +| ac2ac41 | `fix: add missing examples to publish-examples workflow` | 2026-01-09T17:33:55Z | +| 3d7c88c | `fix: remove basic-host from publish-examples (it's a host demo, not a server)` | 2026-01-09T18:13:59Z | + +--- + +## Summary + +PR #224 is a **release PR** that: + +1. Bumps version to 0.3.1 across all packages +2. Fixes the npm-publish workflow to include 6 missing example packages +3. Consolidates improvements from PRs #185, #219, and #221 +4. Contains no breaking changes in itself (though #219's API alignment could affect existing users) + +The release improves API consistency, adds a standalone bundle option, fixes build ordering, and ensures all examples are properly published. diff --git a/intermediate-outputs/release-notes-analysis.md b/intermediate-outputs/release-notes-analysis.md new file mode 100644 index 00000000..e7e8cacc --- /dev/null +++ b/intermediate-outputs/release-notes-analysis.md @@ -0,0 +1,241 @@ +# ext-apps Release Notes Analysis (v0.2.0 - v0.3.1) + +## Summary + +This document analyzes the releases of the `modelcontextprotocol/ext-apps` repository from version 0.2.0 through 0.3.1, covering a period from December 16, 2024 to January 9, 2025. + +--- + +## Release Timeline + +| Version | Release Date | Key Theme | +| ------- | ----------------- | ------------------------------------------------- | +| v0.2.0 | December 16, 2024 | SSE transport, peer dependencies, API refactoring | +| v0.2.1 | December 17, 2024 | Bug fixes, custom fonts, style variables | +| v0.2.2 | December 17, 2024 | Zod v3.25/v4 compatibility | +| v0.3.0 | January 9, 2025 | New examples, npm publishing, spec compliance | +| v0.3.1 | January 9, 2025 | SDK alignment, build improvements | + +--- + +## Detailed Release Notes + +### v0.2.0 (December 16, 2024) + +**Major Release - Foundation for Modern Architecture** + +#### New Features + +- **SSE Transport Support**: Added Server-Sent Events transport and shared server utilities +- **Styles Prop**: Introduced `styles` prop to host context for MCP Apps +- **Display Mode Request**: Apps can now request display mode instead of having it set externally +- **Server Helpers**: Added `connect()` which defaults to parent post transport +- **Peer Dependencies**: MCP SDK and React are now peer dependencies for better version flexibility + +#### API Changes + +- **Optional Client in AppBridge**: Enables custom forwarding scenarios +- **Improved Protocol Types**: Better typing for `AppRequest`, `AppNotification`, `AppResult` +- **Method Renaming**: Removed "send" prefix from request methods (breaking change) + +#### Improvements + +- "Bring your own Zod version" support for flexibility +- Enhanced basic app responsiveness for narrow viewports +- Windows compatibility improvements: + - Made Bun an optional dependency + - Added cross-env for examples + +#### Testing & Quality + +- Added Playwright E2E tests with screenshot golden testing +- Fixed E2E screenshot test consistency across CI and local environments +- Added pre-commit checks for private registry URLs in package-lock.json +- Marked `src/generated` as linguist-generated + +#### Fixes & Documentation + +- Added mimeType to resource declarations in examples +- Fixed sandbox-proxy-ready notification name in spec +- Corrected typos and formatting in spec header +- Improved tool visibility documentation + +**Full Changelog**: v0.1.0...v0.2.0 + +--- + +### v0.2.1 (December 17, 2024) + +**Patch Release - Bug Fixes and Enhancements** + +#### Changes + +1. **Tool Registration Fix** (`fix(examples)`) + - Corrected `server.registerTool` usage for non-UI tools + - Resolved missing import statements + +2. **Custom Font Support** + - Introduced the ability to pass custom fonts within MCP Apps + +3. **Style Variable Addition** + - Added a previously omitted style variable to the MCP Apps framework + +4. **Dependency Update** + - Widened `@oven/bun-*` version range for broader compatibility + +**Contributors**: @ochafik, @martinalong + +--- + +### v0.2.2 (December 17, 2024) + +**Patch Release - Zod Compatibility** + +#### Changes + +- **Zod Schema Compatibility**: Made Zod schemas compatible with both v3.25+ and v4 versions + - PR #178 by @ochafik + - This allows consumers to use either the stable Zod 3.x line or the newer Zod 4.x + +**Note**: 35 commits to main since this release, indicating significant development activity leading to v0.3.0. + +--- + +### v0.3.0 (January 9, 2025) + +**Minor Release - New Examples and NPM Publishing** + +#### Bug Fixes + +- Fixed build errors in examples (@jonathanhefner, #180) +- Moved prettier to dev dependency (@niclim, #179) +- Fixed tsc command to use tsconfig.json (@blackgirlbytes, #188) +- Added missing origin parameter to PostMessageTransport default constructor (@ochafik, #207) +- Made `toolInfo.id` optional per spec (@antonpk1, #216) +- Made example host more resilient to broken servers (@ochafik, #206) +- Added missing server-utils.ts to video-resource-server (@antonpk1, #205) + +#### Features & Enhancements + +- **New Examples**: + - Added `sheet-music-server` example (@jonathanhefner, #196) + - Added `video-resource-server` example (@antonpk1, #175) + - Added `basic-server-*` examples for Vue, Svelte, Preact, and Solid (@jonathanhefner, #141) + +- **NPM Publishing**: Published example servers to npm (@jerome3o-anthropic, #184) + +- **API Improvements**: + - Required `eventSource` in PostMessageTransport + added security tests (@ochafik, #208) + - Exported method names as constants (@idosal, #192) + - Made `ui.resourceUri` optional for tools that just need visibility (@ochafik, #210) + - Updated `viewport` type (@martinalong, #153) + +- **UX Improvements**: + - Added `safeAreaInsets` support to all example apps (@jonathanhefner, #202) + - Added npm `start` alias for `examples:start` (@jonathanhefner, #183) + +#### New Contributors + +- @niclim +- @blackgirlbytes + +--- + +### v0.3.1 (January 9, 2025) + +**Patch Release - SDK Alignment and Build Improvements** + +Released by @ochafik + +#### Changes + +1. **Fixed registerAppTool Signature** (PR #219) + - Aligned the function signature with the MCP TypeScript SDK specifications + - This is the key change addressing breaking API changes in the new SDK version + +2. **Added Build App-with-deps Target** (PR #221) + - Introduced a new build feature for applications with dependencies + +3. **Build Order Fix** (PR #185) + - Ensured the library builds before example applications + - Fixes build reliability issues + +4. **Release Automation** (PR #224) + - Completed the v0.3.1 release process + +**Commit**: `4c51eb0301aece4fe55ae6136bf6444f6a484569` (verified GPG signature) + +--- + +## Key Changes Between Versions + +### v0.2.0 -> v0.2.1 + +- Bug fixes for tool registration +- Added custom font support +- Added missing style variable +- Broadened Bun dependency version range + +### v0.2.1 -> v0.2.2 + +- Zod schema compatibility for v3.25+ and v4 + +### v0.2.2 -> v0.3.0 (Major Changes) + +- **New example servers**: sheet-music-server, video-resource-server, framework-specific examples +- **NPM publishing** of example servers +- **API changes**: + - `toolInfo.id` now optional per spec + - `ui.resourceUri` now optional for tools needing only visibility + - `eventSource` required in PostMessageTransport + - Method names exported as constants +- **Security**: Added security tests for PostMessageTransport +- **UX**: safeAreaInsets support across all examples + +### v0.3.0 -> v0.3.1 + +- **SDK Alignment**: Fixed `registerAppTool` signature to match MCP TypeScript SDK +- **Build System**: Improved build order and added app-with-deps target + +--- + +## Breaking Changes Summary + +### v0.2.0 + +- Method renaming: Removed "send" prefix from request methods +- MCP SDK and React changed to peer dependencies + +### v0.3.0 + +- `eventSource` now required in PostMessageTransport constructor (security hardening) + +### v0.3.1 + +- `registerAppTool` signature changed to align with MCP TypeScript SDK + - This is the "breaking API change" mentioned in the PR context + +--- + +## Notable Patterns + +1. **Rapid Iteration**: v0.2.1 and v0.2.2 were both released on December 17, showing quick bug fix turnaround +2. **Community Growth**: v0.3.0 welcomed new contributors (@niclim, @blackgirlbytes) +3. **Framework Expansion**: Added support for Vue, Svelte, Preact, and Solid in v0.3.0 +4. **SDK Alignment Focus**: v0.3.1 specifically addresses compatibility with the MCP TypeScript SDK + +--- + +## Analysis of registerAppTool Signature Change (v0.3.1) + +The key breaking change in v0.3.1 involves aligning `registerAppTool` with the MCP TypeScript SDK. Based on the release notes: + +- **PR #219**: "Fixed registerAppTool signature - Aligned the function signature with the MCP TypeScript SDK specifications" + +This change was necessary because the MCP TypeScript SDK updated its API, and ext-apps needed to match those changes for compatibility. This is the "breaking API change" referenced in the upgrade context. + +To understand the exact nature of this change, one would need to examine: + +1. The diff in PR #219 +2. The MCP TypeScript SDK changelog for the corresponding version +3. The before/after signatures of `registerAppTool` diff --git a/intermediate-outputs/resize-investigation-findings.md b/intermediate-outputs/resize-investigation-findings.md new file mode 100644 index 00000000..2a97d143 --- /dev/null +++ b/intermediate-outputs/resize-investigation-findings.md @@ -0,0 +1,96 @@ +# Resize Investigation Findings + +## Summary + +**Can apps resize themselves (both grow and shrink)?** + +| Dimension | Growing | Shrinking | Notes | +| ---------- | ------- | --------- | ------------------------------------------------ | +| **Height** | YES | YES | Works correctly - host uses `height` directly | +| **Width** | YES | LIMITED | Host uses `min-width`, treating width as a floor | + +## Detailed Analysis + +### Height Resizing (Works Fully) + +The host implementation (`examples/basic-host/src/implementation.ts:265-266`) uses `height` directly: + +```typescript +from.height = `${iframe.offsetHeight}px`; +iframe.style.height = to.height = `${height}px`; +``` + +This means height changes are applied immediately in both directions (growing and shrinking). + +**Tested and verified:** Added Hide/Show toggle to React and Vanilla JS examples. When controls are hidden, the iframe height shrinks correctly. + +### Width Resizing (Limited by Design) + +The host implementation (`examples/basic-host/src/implementation.ts:253-259`) uses `min-width` instead of `width`: + +```typescript +// Use min-width instead of width to allow responsive growing. +// With auto-resize (the default), the app reports its minimum content +// width; we honor that as a floor but allow the iframe to expand when +// the host layout allows. And we use `min(..., 100%)` so that the iframe +// shrinks with its container. +from.minWidth = `${iframe.offsetWidth}px`; +iframe.style.minWidth = to.minWidth = `min(${width}px, 100%)`; +``` + +**Implications:** + +1. The reported content width is treated as a **floor** (minimum), not a fixed size +2. The iframe can grow beyond the reported width when the container allows +3. The `min(${width}px, 100%)` allows the iframe to shrink **with its container** (responsive) +4. However, if the content shrinks but the container doesn't, the width stays at the previous value + +**This is an intentional design choice** for responsive layouts, not a bug. The comment explicitly states this behavior. + +### If Width Shrinking Is Needed + +If an app truly needs width shrinking independent of container size, the host would need to use `width` instead of `min-width`: + +```typescript +// Alternative: strict width control +iframe.style.width = to.width = `min(${width}px, 100%)`; +``` + +However, this would prevent the current responsive behavior where apps can expand to fill available container space. + +## Changes Made + +### React Example (`basic-server-react`) + +- Added `showControls` state +- Added "Hide Controls" / "Show Controls" toggle button +- Conditionally renders all action sections + +### Vanilla Example (`basic-server-vanillajs`) + +- Added `#controls` wrapper div in HTML +- Added toggle button and JS event handler +- Added CSS for `#controls` container spacing + +## Files Modified + +1. `examples/basic-server-react/src/mcp-app.tsx` - Added toggle state and conditional rendering +2. `examples/basic-server-vanillajs/mcp-app.html` - Added toggle button and controls wrapper +3. `examples/basic-server-vanillajs/src/mcp-app.ts` - Added toggle event handler +4. `examples/basic-server-vanillajs/src/mcp-app.css` - Added `#controls` CSS rules + +## Test Results + +Tested by running `npm run examples:start` and interacting with the host at `http://localhost:8080`: + +1. **React app**: Clicking "Hide Controls" correctly shrinks the iframe height +2. **Vanilla JS app**: Clicking "Hide Controls" correctly shrinks the iframe height +3. Both apps correctly grow when "Show Controls" is clicked + +## Conclusion + +Apps CAN resize themselves, and shrinking IS possible: + +- **Height shrinking works out of the box** +- **Width shrinking is intentionally limited** to support responsive layouts where the container controls expansion +- The current behavior is by design, not a limitation of the protocol diff --git a/package-lock.json b/package-lock.json index 659cbd81..453eb948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -481,7 +481,6 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", - "cross-env": "^10.1.0", "express": "^5.1.0", "typescript": "^5.9.3", "vite": "^6.0.0", diff --git a/simple.ts b/simple.ts new file mode 100644 index 00000000..6ab5e0a2 --- /dev/null +++ b/simple.ts @@ -0,0 +1,100 @@ +/** + npx -y http-server -p 8111 --cors + npm run build + bun simple.ts + */ +import { + RESOURCE_MIME_TYPE, + registerAppResource, + registerAppTool, +} from "@modelcontextprotocol/ext-apps/server"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; + +export function getExampleInlinedAppServerInstance(): McpServer { + const server = new McpServer({ name: "Example Server", version: "1.0.0" }); + const uiHtml = ` + + + + + +
+ + + + `; + const resourceUri = "ui://page"; + + registerAppResource( + server, + "page", + resourceUri, + { + mimeType: RESOURCE_MIME_TYPE, + _meta: { + ui: {}, + }, + }, + () => ({ + contents: [ + { + mimeType: RESOURCE_MIME_TYPE, + text: uiHtml, + uri: resourceUri, + _meta: { + ui: { + csp: { + connectDomains: ["https://unpkg.com"], + resourceDomains: ["https://unpkg.com"], + }, + }, + }, + }, + ], + }), + ); + + registerAppTool( + server, + "show-example", + { + inputSchema: { message: z.string() }, + outputSchema: { message: z.string() }, + _meta: { + ui: { resourceUri }, + }, + }, + ({ message }: { message: string }) => ({ + content: [], + structuredContent: { message: `Server received message: ${message}` }, + _meta: { info: "example metadata" }, + }), + ); + + return server; +} + +async function main() { + const server = getExampleInlinedAppServerInstance(); + await server.server.connect(new StdioServerTransport()); +} + +main().catch((err) => { + console.error("Error running example MCP server:", err); + process.exit(1); +}); diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index a6c29bed..e448bb9f 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -187,6 +187,12 @@ interface UIResourceMeta { * Maps to Permission Policy `geolocation` feature */ geolocation?: boolean, + /** + * Request clipboard write access + * + * Maps to Permission Policy `clipboard-write` feature + */ + clipboardWrite?: boolean, }, /** * Dedicated origin for widget @@ -236,6 +242,7 @@ The resource content is returned via `resources/read`: camera?: boolean; // Request camera access microphone?: boolean; // Request microphone access geolocation?: boolean; // Request geolocation access + clipboardWrite?: boolean; // Request clipboard write access }; domain?: string; prefersBorder?: boolean; @@ -606,11 +613,12 @@ interface HostCapabilities { logging?: {}; /** Sandbox configuration applied by the host. */ sandbox?: { - /** Permissions granted by the host (camera, microphone, geolocation). */ + /** Permissions granted by the host (camera, microphone, geolocation, clipboard-write). */ permissions?: { camera?: boolean; microphone?: boolean; geolocation?: boolean; + clipboardWrite?: boolean; }; /** CSP domains approved by the host. */ csp?: { @@ -1131,6 +1139,7 @@ These messages are reserved for web-based hosts that implement the recommended d camera?: boolean, microphone?: boolean, geolocation?: boolean, + clipboardWrite?: boolean, } } } diff --git a/src/generated/schema.json b/src/generated/schema.json index 884e2c24..8abf4923 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -109,6 +109,10 @@ "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", "type": "boolean" + }, + "clipboardWrite": { + "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", + "type": "boolean" } }, "additionalProperties": false @@ -2312,6 +2316,10 @@ "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", "type": "boolean" + }, + "clipboardWrite": { + "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", + "type": "boolean" } }, "additionalProperties": false @@ -3669,6 +3677,10 @@ "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", "type": "boolean" + }, + "clipboardWrite": { + "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", + "type": "boolean" } }, "additionalProperties": false @@ -3699,6 +3711,10 @@ "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", "type": "boolean" + }, + "clipboardWrite": { + "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", + "type": "boolean" } }, "additionalProperties": false @@ -3814,6 +3830,10 @@ "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", "type": "boolean" + }, + "clipboardWrite": { + "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", + "type": "boolean" } }, "additionalProperties": false diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 92af5ce1..996d3ee2 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -236,6 +236,13 @@ export const McpUiResourcePermissionsSchema = z.object({ .describe( "Request geolocation access (Permission Policy `geolocation` feature).", ), + /** @description Request clipboard write access (Permission Policy `clipboard-write` feature). */ + clipboardWrite: z + .boolean() + .optional() + .describe( + "Request clipboard write access (Permission Policy `clipboard-write` feature).", + ), }); /** diff --git a/src/spec.types.ts b/src/spec.types.ts index f0d8f44e..f9b6d968 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -519,6 +519,8 @@ export interface McpUiResourcePermissions { microphone?: boolean; /** @description Request geolocation access (Permission Policy `geolocation` feature). */ geolocation?: boolean; + /** @description Request clipboard write access (Permission Policy `clipboard-write` feature). */ + clipboardWrite?: boolean; } /** From 166aeb37a85943066677402d6584d9a78682b317 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 12 Jan 2026 13:16:48 +0000 Subject: [PATCH 5/7] rm files added by mistake --- .claude/commands/README.md | 99 ------- .claude/commands/setup-chatgpt-aggregator.md | 226 ---------------- .mcp.json | 13 - intermediate-outputs/model-post-analysis.md | 222 ---------------- intermediate-outputs/pr-158-analysis.md | 174 ------------ intermediate-outputs/pr-204-analysis.md | 248 ------------------ intermediate-outputs/pr-224-analysis.md | 166 ------------ .../release-notes-analysis.md | 241 ----------------- .../resize-investigation-findings.md | 96 ------- 9 files changed, 1485 deletions(-) delete mode 100644 .claude/commands/README.md delete mode 100644 .claude/commands/setup-chatgpt-aggregator.md delete mode 100644 .mcp.json delete mode 100644 intermediate-outputs/model-post-analysis.md delete mode 100644 intermediate-outputs/pr-158-analysis.md delete mode 100644 intermediate-outputs/pr-204-analysis.md delete mode 100644 intermediate-outputs/pr-224-analysis.md delete mode 100644 intermediate-outputs/release-notes-analysis.md delete mode 100644 intermediate-outputs/resize-investigation-findings.md diff --git a/.claude/commands/README.md b/.claude/commands/README.md deleted file mode 100644 index 7234de14..00000000 --- a/.claude/commands/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Claude Code Custom Commands (Skills) - -This directory contains custom slash commands for Claude Code. - -## What are Skills/Commands? - -Claude Code commands are markdown files that get expanded as prompts when you invoke them with `/command-name`. They let you create reusable workflows. - -## Available Commands - -| Command | Description | -| --------------------------- | --------------------------------------------------------- | -| `/setup-chatgpt-aggregator` | Automate ChatGPT Apps connector setup with MCP Aggregator | - -## How to Use - -1. In Claude Code, type `/` followed by the command name -2. The command's markdown content becomes part of the conversation -3. Claude executes the workflow described in the command - -Example: - -``` -/setup-chatgpt-aggregator -``` - -## How to Package/Share - -Commands are just markdown files. To share: - -1. **Copy the file** - Share `.claude/commands/*.md` files -2. **Include in repo** - Commit to `.claude/commands/` in your project -3. **Global commands** - Put in `~/.claude/commands/` for all projects - -### Directory Structure - -``` -.claude/ -├── commands/ -│ ├── README.md # This file -│ └── setup-chatgpt-aggregator.md # ChatGPT setup skill -└── settings.local.json # Local permissions -``` - -## Prerequisites for setup-chatgpt-aggregator - -1. **Enable the chrome MCP server**: - - ``` - /mcp - # Select "chrome" → "Enable" - ``` - -2. **Install cloudflared**: - - ```bash - brew install cloudflare/cloudflare/cloudflared - ``` - -3. **Have a ChatGPT account** with developer mode access - -## Creating New Commands - -Create a new `.md` file in this directory: - -```markdown -# My Command Name - -Description of what this command does. - -## Steps - -1. First step... -2. Second step... -``` - -The filename (without `.md`) becomes the command name: - -- `my-command.md` → `/my-command` - -## MCP Server Dependency - -The `setup-chatgpt-aggregator` skill requires the `chrome` MCP server defined in `.mcp.json`: - -```json -{ - "mcpServers": { - "chrome": { - "command": "npx", - "args": ["-y", "chrome-devtools-mcp@latest", "--headless=false"] - } - } -} -``` - -## More Info - -- [Claude Code Docs](https://docs.anthropic.com/en/docs/claude-code) -- [MCP Protocol](https://modelcontextprotocol.io) diff --git a/.claude/commands/setup-chatgpt-aggregator.md b/.claude/commands/setup-chatgpt-aggregator.md deleted file mode 100644 index ba0d2cba..00000000 --- a/.claude/commands/setup-chatgpt-aggregator.md +++ /dev/null @@ -1,226 +0,0 @@ -# Setup ChatGPT with MCP Aggregator Server - -Automate the setup of ChatGPT Apps connector with the local MCP Aggregator server. - -## FIRST: Enable the Chrome MCP Server - -Before running this skill, enable the chrome MCP server: - -1. Run `/mcp` in Claude Code -2. Select "chrome" from the list -3. Choose "Enable" - -Or manually add to `~/.claude.json` under the project's `enabledMcpjsonServers` array. - -## Overview - -This skill automates: - -1. Starting the aggregator server (port 3100) with all example backend servers -2. Creating a cloudflared tunnel to expose the aggregator -3. Opening ChatGPT.com and guiding through authentication -4. Configuring the Apps connector with the tunnel URL -5. Testing each tool from the aggregator - -## Prerequisites - -- `cloudflared` installed (`brew install cloudflare/cloudflare/cloudflared`) -- Chrome browser available -- ChatGPT account with developer mode access -- `chrome` MCP server enabled (see above) - -## Execution Steps - -### Step 1: Start Backend Servers and Aggregator - -First, start the example servers that will feed into the aggregator: - -```bash -# Start all example servers in background -npm run examples:start & - -# Wait for servers to be ready -sleep 5 - -# Start the aggregator server pointing to all backends -BACKEND_SERVERS='["http://localhost:3101/mcp","http://localhost:3102/mcp","http://localhost:3103/mcp","http://localhost:3104/mcp","http://localhost:3105/mcp","http://localhost:3106/mcp","http://localhost:3107/mcp","http://localhost:3108/mcp","http://localhost:3109/mcp"]' \ -PORT=3100 bun examples/aggregator-server/server.ts & - -# Wait for aggregator -sleep 3 -``` - -### Step 2: Start Cloudflared Tunnel - -```bash -# Start tunnel and capture the URL -cloudflared tunnel --url http://localhost:3100 2>&1 | tee /tmp/cloudflared.log & - -# Wait for tunnel URL to appear -sleep 5 - -# Extract the tunnel URL -TUNNEL_URL=$(grep -oE 'https://[a-z0-9-]+\.trycloudflare\.com' /tmp/cloudflared.log | head -1) -echo "Tunnel URL: $TUNNEL_URL" -``` - -### Step 3: Get Tool List from Aggregator - -Query the aggregator to get all available tools: - -```bash -# Initialize session and get tools -curl -s -X POST http://localhost:3100/mcp \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' \ - | jq -r '.result.sessionId // empty' > /tmp/mcp-session-id - -SESSION_ID=$(cat /tmp/mcp-session-id) - -# List tools -curl -s -X POST http://localhost:3100/mcp \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -H "mcp-session-id: $SESSION_ID" \ - -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \ - | jq -r '.result.tools[].name' > /tmp/aggregator-tools.txt - -echo "Available tools:" -cat /tmp/aggregator-tools.txt -``` - -### Step 4: Browser Automation with Chrome DevTools MCP - -Use the `chrome` MCP server to automate the ChatGPT setup. The MCP tools available are: - -- `browser_navigate` - Navigate to a URL -- `browser_click` - Click an element -- `browser_type` - Type text into an element -- `browser_snapshot` - Take accessibility snapshot -- `browser_wait` - Wait for element or timeout - -#### 4.1 Open ChatGPT and Authenticate - -``` -# Navigate to ChatGPT -Use chrome MCP: browser_navigate to "https://chatgpt.com" - -# Run audio prompt for user -Run: say "Please authenticate to ChatGPT. Press any key when done." -Read a line from stdin to wait. - -# Take snapshot to verify logged in -Use chrome MCP: browser_snapshot -Verify the page shows logged-in state (look for profile menu or chat input) -``` - -#### 4.2 Check for Developer Mode - -``` -# Take snapshot of the main input area -Use chrome MCP: browser_snapshot - -# Look for "developer mode" indicator near the input box -# This may appear as a badge or overlay on the input -``` - -#### 4.3 Navigate to Settings > Apps & Connectors - -``` -# Click the profile/settings menu (usually top-right) -Use chrome MCP: browser_click on the profile menu button - -# Click "Settings" in the dropdown -Use chrome MCP: browser_click on "Settings" - -# Wait for settings panel -Use chrome MCP: browser_wait for settings panel - -# Click "Apps & Connectors" or similar tab -Use chrome MCP: browser_click on "Apps & Connectors" -``` - -#### 4.4 Create New App Connector - -``` -# Click "Create App" or "Add Connector" button -Use chrome MCP: browser_click on the create/add button - -# Fill in the form: -# - Name: "Aggregator" -Use chrome MCP: browser_type "Aggregator" in the name field - -# - URL: The tunnel URL + /mcp -Use chrome MCP: browser_type "$TUNNEL_URL/mcp" in the URL field - -# Click continue/next if there's a confirmation -Use chrome MCP: browser_click on continue button - -# Select "No authentication" option -Use chrome MCP: browser_click on "None" or "No auth" option - -# Click "Create" to finalize -Use chrome MCP: browser_click on "Create" button -``` - -#### 4.5 Test the Connector - -``` -# Go back to main chat -Use chrome MCP: browser_navigate to "https://chatgpt.com" - -# Click the input box -Use chrome MCP: browser_click on the chat input - -# Type @Aggregator to trigger autocomplete -Use chrome MCP: browser_type "@Aggregator" - -# Wait for autocomplete dropdown -Use chrome MCP: browser_wait for autocomplete suggestions - -# Verify "Aggregator" appears in suggestions -Use chrome MCP: browser_snapshot to verify - -# Press Enter to select -Use chrome MCP: browser_type with Enter key - -# Verify it shows as selected (badge below input) -Use chrome MCP: browser_snapshot to verify selection badge -``` - -#### 4.6 Test Each Tool - -For each tool in `/tmp/aggregator-tools.txt`: - -``` -# Type a test prompt for this tool -Use chrome MCP: browser_type "use the {TOOL_NAME} mcp" - -# Submit the message -Use chrome MCP: browser_type with Enter key - -# Wait for response -Use chrome MCP: browser_wait for response to appear - -# Take snapshot to verify -Use chrome MCP: browser_snapshot -``` - -## Cleanup - -When done: - -```bash -# Kill background processes -pkill -f cloudflared -pkill -f "bun examples/aggregator-server" -pkill -f "npm run examples:start" -``` - -## Notes - -- The exact selectors for ChatGPT UI elements may change; use `browser_snapshot` to inspect the current DOM -- Developer mode access is required for Apps & Connectors -- The tunnel URL changes each time cloudflared starts -- All tools from the aggregator are prefixed with the backend server name (e.g., `basic-mcp-app-server-react/get-time`) diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index 8c43524f..00000000 --- a/.mcp.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "mcpServers": { - "chrome": { - "command": "npx", - "args": [ - "-y", - "chrome-devtools-mcp@latest", - "--headless=false", - "--userDataDir=/tmp/chatgpt-setup-chrome-profile" - ] - } - } -} diff --git a/intermediate-outputs/model-post-analysis.md b/intermediate-outputs/model-post-analysis.md deleted file mode 100644 index 82617572..00000000 --- a/intermediate-outputs/model-post-analysis.md +++ /dev/null @@ -1,222 +0,0 @@ -# Analysis of Model Discussion Post #181 - -**Source**: https://github.com/modelcontextprotocol/ext-apps/discussions/181 -**Title**: "Changes from 0.1.x to 0.2.2" -**Author**: @ochafik -**Category**: Announcements -**Date**: 2025-12-18 - ---- - -## 1. Overall Structure and Format - -The post follows a **changelog-style announcement** pattern with: - -1. **Opening paragraph** - Brief, friendly intro explaining context (multiple versions released quickly) -2. **Warning callout** - Critical migration note at the top -3. **Categorized sections** - Logically grouped changes under headers -4. **Footer link** - Full changelog reference - -### Section Order: - -1. Intro paragraph -2. Warning callout (GitHub alert syntax) -3. `### Highlights` - Major new features -4. `### API Changes` - Breaking/behavioral changes -5. `### Platform & DX` - Developer experience improvements -6. `### Bug Fixes` - Fixes -7. Full changelog link - ---- - -## 2. Tone and Style - -- **Friendly and casual**: Opens with "Hi all!" -- **Direct and informative**: Gets straight to the point -- **Scannable**: Heavy use of bold for key terms, bullet points for details -- **Professional but approachable**: Uses "we've" and conversational language - ---- - -## 3. Key Sections and Formatting Conventions - -### Opening Paragraph - -```markdown -Hi all! We've just pushed @modelcontextprotocol/ext-apps versions [0.2.0](...), [0.2.1](...) and [0.2.2](...) in quick succession, so here's what's changed since 0.1.x -``` - -- Links to each version's release page -- Explains the "why" briefly - -### Warning Callout (for Breaking Changes) - -```markdown -> [!WARNING] -> The `registerAppTool` helper makes sure defining `_meta.ui.resourceUri` is backward-compatible w/ clients expecting `"ui/resourceUri"` -``` - -- Uses GitHub's alert syntax `> [!WARNING]` -- Placed prominently at the top, before section headers -- Explains the migration path, not just the breaking change - -### Section Headers - -- Uses `###` (H3) for main sections -- Section names are short and descriptive: - - `Highlights` - - `API Changes` - - `Platform & DX` - - `Bug Fixes` - -### Bullet Point Format - -Each bullet follows this pattern: - -```markdown -- **Feature name** — Description with context. Links [#123](url) -``` - -Key conventions: - -- **Bold** for the feature/change name (2-4 words) -- **Em dash** (`—`) as separator (not hyphen or colon) -- Description explains the "what" and "why" -- PR link at the end with `[#123](url)` format -- Multiple related PRs can be listed - -### Examples of Well-Formatted Bullets: - -**New Feature:** - -```markdown -- **Server helpers** — New `registerAppTool()` and `registerAppResource()` simplify server setup with proper type safety. `connect()` now defaults to `PostMessageTransport(window.parent)`, enabling simpler initialization with just `await app.connect()` [#165](...) -``` - -**Breaking Change:** - -```markdown -- **MCP SDK as peer dependency** — Consumers control their SDK version, reducing duplication [#168](...) -``` - -**Bug Fix:** - -```markdown -- **Responsive UIs** — Fixed narrow viewport handling for mobile and sidebar experiences [#135](...) -``` - ---- - -## 4. How Breaking Changes vs New Features are Presented - -### Breaking Changes Get Special Treatment: - -1. **Warning callout at top** - The most critical breaking change uses `> [!WARNING]` syntax -2. **Placed in "API Changes" section** - Not hidden, but clearly separated from "Highlights" -3. **Migration path included** - Notes how backward compatibility is maintained -4. **Explains the benefit** - e.g., "Consumers control their SDK version, reducing duplication" - -### New Features (Highlights): - -- Listed first, emphasizing value -- Focus on capability gains -- More descriptive explanations - -### API Changes (Breaking/Behavioral): - -- Listed after highlights -- Focus on what changed and why -- Often includes deprecated alias notes: "Deprecated aliases maintained" - -### The Distinction: - -| Section | Focus | Tone | -| ------------- | -------------------- | --------------------------------- | -| Highlights | New capabilities | "Enables X", "New Y" | -| API Changes | What changed | "Now X", "Renamed Y", "Removed Z" | -| Platform & DX | Developer experience | "Support for X", "Widened Y" | -| Bug Fixes | What was broken | "Fixed X" | - ---- - -## 5. Conventions Used - -### Formatting Elements: - -- **Bold** (`**text**`) — Feature/change names -- **Em dash** (`—`) — Separator after bold title -- **Backticks** (`` `code` ``) — Code, function names, config keys -- **Links** (`[text](url)`) — Version releases, PR references -- **GitHub Alert** (`> [!WARNING]`) — Critical migration notes - -### No Emojis - -The post does **not** use emojis anywhere. It maintains a clean, professional look relying on: - -- Bold text for emphasis -- Section headers for organization -- Warning callouts for critical notes - -### PR Reference Format: - -```markdown -[#123](https://github.com/modelcontextprotocol/ext-apps/pull/123) -``` - -- Number only, no description -- Full URL (not shorthand) -- Placed at end of bullet - -### Footer: - -```markdown -**Full Changelog**: https://github.com/modelcontextprotocol/ext-apps/compare/v0.1.1...v0.2.2 -``` - -- Bold label -- Compare URL for full diff - ---- - -## 6. Template for Future Posts - -```markdown -Hi all! We've just released [package] version [X.Y.Z](release-url), here's what's changed since [previous version]. - -> [!WARNING] -> [Critical migration note if any breaking changes] - -### Highlights - -- **Feature Name** — Description of what it does and why it matters [#PR](url) -- **Another Feature** — Description [#PR](url) - -### API Changes - -- **Breaking Change** — What changed and how to migrate. Old behavior deprecated [#PR](url) -- **Renamed Method** — Old `sendFoo()` is now `foo()`. Deprecated aliases maintained [#PR](url) - -### Platform & DX - -- **Improvement** — Description of developer experience improvement [#PR](url) - -### Bug Fixes - -- **Fix Title** — Fixed [what was broken] [#PR](url) - -**Full Changelog**: [compare-url] -``` - ---- - -## 7. Key Takeaways for Writing Similar Posts - -1. **Lead with context** - Explain why the post exists (multiple versions, major release, etc.) -2. **Highlight breaking changes early** - Use `> [!WARNING]` callout before sections -3. **Organize by impact category** - Highlights > API Changes > DX > Fixes -4. **Be consistent with formatting** - Bold title, em dash, description, PR link -5. **Explain the "why"** - Don't just list changes, explain benefits -6. **Provide migration paths** - For breaking changes, show how to adapt -7. **Keep it scannable** - Bold keywords, bullets, headers -8. **No emojis** - Professional, clean style -9. **Link everything** - Releases, PRs, changelogs diff --git a/intermediate-outputs/pr-158-analysis.md b/intermediate-outputs/pr-158-analysis.md deleted file mode 100644 index 8ade61a9..00000000 --- a/intermediate-outputs/pr-158-analysis.md +++ /dev/null @@ -1,174 +0,0 @@ -# PR #158 Analysis: Enhance Sandbox Capability Negotiation - -## Summary - -**Title:** feat: enhance sandbox capability negotiation -**Author:** Ido Salomon (@idosal) -**State:** OPEN -**Branch:** `feat/sandbox-capabilities` -> `main` -**Related Issue:** https://github.com/modelcontextprotocol/ext-apps/issues/58 - ---- - -## 1. What the PR is Trying to Achieve - -This PR enhances the MCP Apps sandbox capability negotiation system by adding support for: - -### 1.1 New CSP Directives - -- **`frameDomains`**: Controls which origins can be used for nested iframes (maps to CSP `frame-src` directive) -- **`baseUriDomains`**: Controls allowed base URIs for the document (maps to CSP `base-uri` directive) - -### 1.2 Browser Permissions - -Introduces a `permissions` attribute (`ui/permissions`) in Resource Metadata supporting: - -- `camera`: Request camera access -- `microphone`: Request microphone access -- `geolocation`: Request geolocation access - -These map to the iframe's `allow` attribute for Permission Policy features. - -### 1.3 Host Capability Negotiation - -Adds `csp` to Host<>App capability negotiation so apps can detect at runtime which CSP features and permissions the host supports (since this is non-trivial to detect otherwise). - ---- - -## 2. Comment History and Discussions - -### Key Discussion Points: - -1. **@matteo8p (Dec 22)**: Asked where iFrame permissions are enforced and suggested using `document.featurePolicy.allowedFeatures()` as a runtime source of truth instead of negotiation. - -2. **@idosal (Jan 7)**: Responded that permissions were originally excluded from negotiation because they could be detected directly, but the negotiation approach assumes less about the app runtime and developer's awareness of browser APIs. Community input needed. - -3. **@matteo8p (Dec 22)**: Shared that OpenAI currently allows `local-network-access *; microphone *; midi *` in their iframe permissions. - -4. **@aharvard (Jan 8)**: Announced implementation of `frameDomains` and `baseUriDomains` in Goose: https://github.com/block/goose/pull/6399 - -5. **@ochafik (Jan 11)**: Created PR #234 for reference CSP handling in the sandbox server with `frameDomains` and `baseUriDomains`. Suggested this PR focus on sandbox permissions only. - -6. **@ochafik (Jan 12)**: Created a speech example (PR #240) relying on microphone and clipboard-write. Offered to help refresh/push to this branch. - -### Reviews: - -- **Copilot reviewer**: Generated automated review with 4 comments (overview only) -- **@ochafik**: Requested changes - "Thanks Ido!" and later commented "We should tell app what permissions were granted (and potentially which domains were allowed)" -- **@matteo8p**: Commented on permissions detection approach -- **@domfarolino**: Commented (no content shown in review body) - ---- - -## 3. What Changes Are Currently in the PR - -### 3.1 Specification Changes (`specification/draft/apps.mdx`) - -- Added `frameDomains` and `baseUriDomains` to `UIResourceMeta.csp` -- Added `permissions` object with `camera`, `microphone`, `geolocation` booleans -- Updated sandbox requirements documentation -- Added new `HostCapabilities` interface with `sandbox` section including permissions and CSP support - -### 3.2 Type System Changes - -**`src/spec.types.ts`**: - -- Extended `McpUiSandboxResourceReadyNotification` with `frameDomains`, `baseUriDomains`, and `permissions` -- Added `sandbox` to `McpUiHostCapabilities` -- Extended `McpUiResourceCsp` with `frameDomains` and `baseUriDomains` -- Added new `McpUiResourcePermissions` interface -- Extended `McpUiResourceMeta` with `permissions` - -**`src/generated/schema.ts`**: - -- Added `McpUiResourcePermissionsSchema` (camera, microphone, geolocation booleans) -- Extended `McpUiResourceCspSchema` with frameDomains, baseUriDomains -- Extended `McpUiHostCapabilitiesSchema` with sandbox section -- Updated `McpUiResourceMetaSchema` to include permissions -- Updated `McpUiSandboxResourceReadyNotificationSchema` with new fields - -**`src/types.ts`**: - -- Exported new `McpUiResourcePermissions` type and `McpUiResourcePermissionsSchema` - -### 3.3 Implementation Changes - -**`examples/basic-host/src/implementation.ts`**: - -- Extended `UiResourceData` interface with `frameDomains`, `baseUriDomains`, and `permissions` -- Updated resource extraction to include permissions metadata -- Updated `sendSandboxResourceReady` call to include permissions - -**`examples/basic-host/src/sandbox.ts`**: - -- Extended `buildCspMetaTag()` to handle `frameDomains` and `baseUriDomains` -- Added `buildAllowAttribute()` function for Permission Policy -- Updated message handler to set iframe `allow` attribute based on permissions - -**`examples/simple-host/sandbox.html`** (NEW): - -- Added new sandbox proxy HTML implementation -- Includes `buildAllowAttribute()` function -- Uses `document.write()` pattern instead of srcdoc for CSP compatibility -- Note: CSP handled via HTTP response headers in serve.ts, not meta tags - -### 3.4 Package Changes - -- Added `cross-env` dependency (^10.1.0) to multiple example packages -- Various package-lock.json updates (removing "peer" markers from several dependencies) - -### 3.5 Schema/Test Updates - -- Updated `src/generated/schema.json` with new types -- Updated `src/generated/schema.test.ts` with new type inference tests - ---- - -## 4. Issues, Conflicts, and Open Questions - -### 4.1 Potential Overlap with Other PRs - -- **PR #234**: @ochafik's PR for CSP handling in sandbox server already includes `frameDomains` and `baseUriDomains`. This creates potential overlap/conflict. -- **Suggestion**: Focus this PR on sandbox permissions only, let PR #234 handle CSP domain extensions - -### 4.2 Open Design Questions - -1. **Runtime detection vs negotiation**: Should apps use `document.featurePolicy.allowedFeatures()` at runtime instead of relying on host capability negotiation for permissions? -2. **Permission scope**: Currently limited to camera, microphone, geolocation. Other permissions (like `midi` in OpenAI's setup, or `clipboard-write` for the speech example) may be needed. - -### 4.3 Missing Implementation - -- E2E tests not yet added (author mentioned will add before merging) -- No verification that all example hosts properly implement the new features - -### 4.4 Collaboration Request - -- @ochafik offered to help refresh/push to this branch (Jan 12) - ---- - -## 5. Files Changed Summary - -| File | Changes | -| ------------------------------------------- | ----------------------------------------------------------- | -| `specification/draft/apps.mdx` | +137 lines - New CSP fields and permissions documentation | -| `src/spec.types.ts` | +38 lines - New interfaces for permissions and extended CSP | -| `src/generated/schema.ts` | +117 lines - Zod schemas for new types | -| `src/generated/schema.json` | +195 lines - JSON schema updates | -| `src/generated/schema.test.ts` | +26/-20 lines - Type test updates | -| `src/types.ts` | +2 lines - New exports | -| `examples/basic-host/src/implementation.ts` | +10/-3 lines - Permissions extraction | -| `examples/basic-host/src/sandbox.ts` | +40/-5 lines - Permission Policy implementation | -| `examples/simple-host/sandbox.html` | +128 lines (NEW) - New sandbox proxy | -| Multiple `package.json` files | +1 line each - cross-env dependency | -| `package-lock.json` | Various updates | - ---- - -## 6. Recommendations - -1. **Coordinate with PR #234**: Clarify scope division between this PR and the CSP handling PR -2. **Add E2E tests**: As author noted, tests needed before merge -3. **Consider additional permissions**: `clipboard-write`, `midi`, etc. based on real-world usage -4. **Document runtime detection option**: Even if negotiation is preferred, apps should know about `featurePolicy.allowedFeatures()` as fallback -5. **Review simple-host sandbox.html**: New file needs careful security review diff --git a/intermediate-outputs/pr-204-analysis.md b/intermediate-outputs/pr-204-analysis.md deleted file mode 100644 index d623f6d5..00000000 --- a/intermediate-outputs/pr-204-analysis.md +++ /dev/null @@ -1,248 +0,0 @@ -# PR #204 Analysis: chore: release 0.3.0 - -**Repository:** modelcontextprotocol/ext-apps -**PR URL:** https://github.com/modelcontextprotocol/ext-apps/pull/204 -**Author:** ochafik (Olivier Chafik) -**State:** MERGED -**Created:** 2026-01-07T11:36:03Z -**Merged:** 2026-01-09T00:55:45Z -**Base Branch:** main -**Head Branch:** ochafik/release-0.3.0 - ---- - -## 1. Title and Description - -**Title:** `chore: release 0.3.0` - -**Summary:** This PR bumps the version from 0.2.2 to 0.3.0 and introduces release notes documenting all changes since the previous version. - ---- - -## 2. Files Changed - -**Total:** 19 files changed (+207 additions, -33 deletions) - -### New Files - -| File | Additions | Purpose | -| ---------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------- | -| `RELEASES.md` | +64 lines | New release notes document covering 0.3.0 and 0.2.2 changes | -| `examples/video-resource-server/src/server-utils.ts` | +110 lines | Shared utilities for running MCP servers with various transports (stdio and Streamable HTTP) | - -### Version Bumps (package.json files) - -All example servers had their `@modelcontextprotocol/ext-apps` dependency bumped from `^0.2.2` to `^0.3.0`: - -1. `examples/basic-server-preact/package.json` -2. `examples/basic-server-react/package.json` -3. `examples/basic-server-solid/package.json` -4. `examples/basic-server-svelte/package.json` -5. `examples/basic-server-vanillajs/package.json` -6. `examples/basic-server-vue/package.json` -7. `examples/budget-allocator-server/package.json` -8. `examples/cohort-heatmap-server/package.json` -9. `examples/customer-segmentation-server/package.json` -10. `examples/scenario-modeler-server/package.json` -11. `examples/sheet-music-server/package.json` -12. `examples/system-monitor-server/package.json` -13. `examples/threejs-server/package.json` -14. `examples/video-resource-server/package.json` -15. `examples/wiki-explorer-server/package.json` - -### Root Package Updates - -- `package.json` - version bump to 0.3.0 -- `package-lock.json` - updated lockfile reflecting version changes - ---- - -## 3. Purpose/Goal of the PR - -The primary purpose is to **release version 0.3.0** of the MCP Apps SDK. This involves: - -1. **Version bump** from 0.2.2 to 0.3.0 -2. **Creating comprehensive release notes** (RELEASES.md) documenting all changes -3. **Updating all example server dependencies** to use the new version -4. **Adding shared server utilities** for the video-resource-server example - ---- - -## 4. Breaking Changes - -Two breaking changes are documented in this release: - -### 4.1 `viewport` replaced with `containerDimensions` (PR #153) - -- **Before:** Host context had a `viewport` property -- **After:** Replaced with `containerDimensions` which provides clearer semantics -- **Impact:** The new type allows specifying either: - - `height` OR `maxHeight` - - `width` OR `maxWidth` -- **Reason:** Provides clearer distinction between fixed dimensions and maximum constraints -- **Migration:** Apps using `viewport` must update to use `containerDimensions` instead - -### 4.2 `eventSource` now required in `PostMessageTransport` (PR #208) - -- **Before:** `eventSource` parameter was optional in constructor -- **After:** `eventSource` parameter is now required -- **Impact:** Security improvement - enforces validation of message sources -- **Reason:** Prevents potential cross-app message spoofing attacks -- **Migration:** Callers must explicitly provide the `eventSource` parameter - ---- - -## 5. New Features and Improvements - -### 5.1 New Framework Examples - -- **Vue, Svelte, Preact, and Solid examples** (PR #141) - - `basic-server-vue` - - `basic-server-svelte` - - `basic-server-preact` - - `basic-server-solid` - -### 5.2 `safeAreaInsets` Support (PR #202) - -- Host context now includes safe area insets -- Allows apps to properly handle device notches and system UI elements -- All example apps updated to use this for proper padding - -### 5.3 `ui.resourceUri` Optional (PR #210) - -- Tools that need visibility but don't require a UI resource can now omit `resourceUri` in the schema - -### 5.4 Method Names Exported as Constants (PR #192) - -- MCP method names are now exported as typed constants -- Enables easier external library integration without brittle schema introspection - -### 5.5 Example Servers Publishable to npm (PR #184) - -- All 15 example servers are now published to npm under `@modelcontextprotocol` scope -- Users can run examples directly via `npx @modelcontextprotocol/server-basic-react` etc. - -### 5.6 `toolInfo.id` Optional (PR #216) - -- Aligned TypeScript types with spec by making `toolInfo.id` optional -- Allows hosts to pass just the tool definition - -### 5.7 New Example Servers - -- **Video resource server** (PR #175) - Demonstrates video resource handling with proper mimeType declarations -- **Sheet music server** (PR #196) - Interactive sheet music notation example - -### 5.8 Developer Experience Improvements - -- **`npm start` alias** (PR #183) - Added as alias for `npm run examples:start` -- **Examples cleanup** (PR #182) - Improved consistency across example servers -- **Documentation fixes** (PR #188) - Fixed tsc command in docs to use tsconfig.json - ---- - -## 6. Bug Fixes - -### 6.1 Dependency Fixes - -- **Move prettier to dev dependency** (PR #179) - Fixed incorrect dependency classification -- **Fix build errors in examples** (PR #180) - Resolved build issues across example servers - -### 6.2 Host Resilience Improvements (PR #206) - -- Server connections now use `Promise.allSettled` instead of `Promise.all` -- One server failure no longer crashes the entire UI - -### 6.3 E2E Test Reliability (PR #206) - -- Fixed flaky Three.js E2E tests with reliable canvas ID masking - ---- - -## 7. Security Fixes - -### 7.1 PostMessageTransport Origin Verification (PR #207) - -- Added proper source validation to default constructor -- Added validation to sandbox proxy -- Prevents cross-origin message spoofing - -### 7.2 Security E2E Tests (PR #208) - -- Added 14 comprehensive tests for: - - Origin validation - - Cross-app message injection protection - ---- - -## 8. New Server Utilities - -A new `server-utils.ts` file was added to the video-resource-server example, providing: - -### `startServer(createServer)` - -Main entry point that detects transport mode from CLI args: - -- If `--stdio` flag: Uses stdio transport -- Otherwise: Uses Streamable HTTP transport - -### `startStdioServer(createServer)` - -- Connects server using `StdioServerTransport` - -### `startStreamableHttpServer(createServer)` - -- Runs server with Streamable HTTP transport in stateless mode -- Each request creates fresh server and transport instances -- Listens on PORT env var (default: 3001) -- Includes proper cleanup on response end -- CORS enabled -- Graceful shutdown handling (SIGINT, SIGTERM) - ---- - -## 9. Related PRs Referenced - -The release notes document changes from multiple PRs: - -| PR | Description | -| ---- | -------------------------------------------------- | -| #153 | `viewport` → `containerDimensions` breaking change | -| #208 | `eventSource` required in `PostMessageTransport` | -| #141 | Framework examples (Vue, Svelte, Preact, Solid) | -| #202 | `safeAreaInsets` support | -| #210 | `ui.resourceUri` optional | -| #192 | Method names exported as constants | -| #184 | Example servers publishable to npm | -| #216 | `toolInfo.id` optional | -| #175 | Video resource server example | -| #196 | Sheet music server example | -| #183 | `npm start` alias | -| #182 | Examples cleanup | -| #188 | Documentation fixes | -| #179 | Prettier to dev dependency | -| #180 | Fix build errors in examples | -| #206 | Host resilience + E2E test reliability | -| #207 | PostMessageTransport origin verification | - ---- - -## 10. Backward Compatibility Note - -The PR description mentions: - -> A backward compatibility shim for the deprecated `viewport` property is available in PR #TBD (branch `ochafik/viewport-compat-shim`). Consider merging that first if backward compat is needed. - -This suggests that the `viewport` → `containerDimensions` breaking change may have a compatibility shim available if needed. - ---- - -## 11. Summary - -This is a **release preparation PR** that: - -1. Bumps version to 0.3.0 (semantic versioning minor bump due to breaking changes) -2. Documents comprehensive release notes in RELEASES.md -3. Updates all example dependencies to the new version -4. Adds shared server utilities for the video-resource example - -The release includes 2 breaking changes, 8+ new features, multiple bug fixes, and security improvements. All 15 example servers are updated and can be published to npm. diff --git a/intermediate-outputs/pr-224-analysis.md b/intermediate-outputs/pr-224-analysis.md deleted file mode 100644 index c4f56462..00000000 --- a/intermediate-outputs/pr-224-analysis.md +++ /dev/null @@ -1,166 +0,0 @@ -# PR #224 Analysis: chore: release 0.3.1 - -**Repository:** [modelcontextprotocol/ext-apps](https://github.com/modelcontextprotocol/ext-apps) -**PR URL:** https://github.com/modelcontextprotocol/ext-apps/pull/224 -**Author:** Olivier Chafik (@ochafik) -**State:** MERGED -**Merged At:** 2026-01-09T18:22:27Z - ---- - -## 1. Title and Description - -### Title - -`chore: release 0.3.1` - -### Description - -This PR bumps the version from 0.3.0 to 0.3.1 and consolidates several prior changes into a release. - ---- - -## 2. Purpose/Goal of the PR - -The primary goal is to **release version 0.3.1** of the MCP Apps SDK. This release: - -1. Consolidates changes from PRs #219, #221, and #185 since version 0.3.0 -2. Bumps the version across the main package and all example packages -3. Fixes the npm-publish workflow to include previously missing examples - ---- - -## 3. All Changes Made - -### Files Changed (18 total) - -| File | Additions | Deletions | Change Type | -| ---------------------------------------------------- | --------- | --------- | ---------------- | -| `.github/workflows/npm-publish.yml` | 6 | 0 | CI/CD Fix | -| `package.json` | 1 | 1 | Version Bump | -| `package-lock.json` | 85 | 17 | Lockfile Update | -| `examples/basic-server-preact/package.json` | 1 | 1 | Dep Version Bump | -| `examples/basic-server-react/package.json` | 1 | 1 | Dep Version Bump | -| `examples/basic-server-solid/package.json` | 1 | 1 | Dep Version Bump | -| `examples/basic-server-svelte/package.json` | 1 | 1 | Dep Version Bump | -| `examples/basic-server-vanillajs/package.json` | 1 | 1 | Dep Version Bump | -| `examples/basic-server-vue/package.json` | 1 | 1 | Dep Version Bump | -| `examples/budget-allocator-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/cohort-heatmap-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/customer-segmentation-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/scenario-modeler-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/sheet-music-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/system-monitor-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/threejs-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/video-resource-server/package.json` | 1 | 1 | Dep Version Bump | -| `examples/wiki-explorer-server/package.json` | 1 | 1 | Dep Version Bump | - -**Total:** +107 additions, -33 deletions - -### Detailed Changes - -#### 1. Version Bump (package.json, package-lock.json) - -- Main package version: `0.3.0` -> `0.3.1` -- All example packages updated to depend on `@modelcontextprotocol/ext-apps: "^0.3.1"` - -#### 2. npm-publish.yml Workflow Fix - -Added 6 missing examples to the publish-examples matrix: - -- `basic-server-preact` -- `basic-server-solid` -- `basic-server-svelte` -- `basic-server-vue` -- `sheet-music-server` -- `video-resource-server` - -Note: `basic-host` was initially added but then removed in a follow-up commit (3d7c88c) since it's a host demo, not a server. - ---- - -## 4. Breaking Changes - -**No breaking changes** are introduced in this PR itself. - -However, this release **includes** PR #219 which aligns the `registerAppTool` signature with the MCP TypeScript SDK. This could be considered a **potential breaking change** for existing users if: - -- They were using `registerAppTool` with the old signature -- The signature alignment with the MCP TS SDK changes how parameters are passed - ---- - -## 5. New Features or Improvements - -### From Included PRs (since 0.3.0) - -#### PR #219: `registerAppTool` Signature Alignment - -- **File:** `src/server/index.ts` (+11/-6) -- **Change:** Made `registerAppTool` more interchangeable with `server.registerTool` -- **Purpose:** Better API consistency with the MCP TypeScript SDK - -#### PR #221: Build `app-with-deps` Target - -- **Files:** `build.bun.ts`, `package.json` -- **Feature:** Added a new build target that bundles the MCP Apps SDK with all dependencies for standalone use -- **Use Case:** Enables module imports from inlined apps (e.g., loading the SDK directly from unpkg CDN in HTML) - -#### PR #185: Build Order Fix - -- **Files:** `.github/workflows/ci.yml`, `package.json`, `scripts/check-versions.mjs` -- **Feature:** - - Added `preexamples:build` hook to ensure lib is built before examples - - Added version consistency check script -- **Purpose:** Ensures examples always type-check against the latest library code - -### Direct Changes in This PR - -#### Workflow Fix - -- Fixed the npm-publish workflow to include 6 previously missing example packages -- This ensures all examples are properly published to npm when releasing - ---- - -## 6. Bug Fixes - -### Direct Fixes in This PR - -1. **Missing Examples in Publish Workflow** - - Several example packages were not being published to npm - - Added: `basic-server-preact`, `basic-server-solid`, `basic-server-svelte`, `basic-server-vue`, `sheet-music-server`, `video-resource-server` - - Removed `basic-host` (correctly classified as a host demo, not a server) - -### Included from Prior PRs - -2. **PR #185: Build Order Issue** - - Fixed: Examples were being built before the library, causing type-check failures - - Solution: Added `preexamples:build` hook - -3. **PR #219: API Signature Inconsistency** - - Fixed: `registerAppTool` signature didn't match `server.registerTool` - - Aligned the API for better consistency - ---- - -## Commits - -| Commit | Message | Date | -| ------- | ------------------------------------------------------------------------------- | -------------------- | -| b2242d0 | `chore: release 0.3.1` | 2026-01-09T17:28:00Z | -| ac2ac41 | `fix: add missing examples to publish-examples workflow` | 2026-01-09T17:33:55Z | -| 3d7c88c | `fix: remove basic-host from publish-examples (it's a host demo, not a server)` | 2026-01-09T18:13:59Z | - ---- - -## Summary - -PR #224 is a **release PR** that: - -1. Bumps version to 0.3.1 across all packages -2. Fixes the npm-publish workflow to include 6 missing example packages -3. Consolidates improvements from PRs #185, #219, and #221 -4. Contains no breaking changes in itself (though #219's API alignment could affect existing users) - -The release improves API consistency, adds a standalone bundle option, fixes build ordering, and ensures all examples are properly published. diff --git a/intermediate-outputs/release-notes-analysis.md b/intermediate-outputs/release-notes-analysis.md deleted file mode 100644 index e7e8cacc..00000000 --- a/intermediate-outputs/release-notes-analysis.md +++ /dev/null @@ -1,241 +0,0 @@ -# ext-apps Release Notes Analysis (v0.2.0 - v0.3.1) - -## Summary - -This document analyzes the releases of the `modelcontextprotocol/ext-apps` repository from version 0.2.0 through 0.3.1, covering a period from December 16, 2024 to January 9, 2025. - ---- - -## Release Timeline - -| Version | Release Date | Key Theme | -| ------- | ----------------- | ------------------------------------------------- | -| v0.2.0 | December 16, 2024 | SSE transport, peer dependencies, API refactoring | -| v0.2.1 | December 17, 2024 | Bug fixes, custom fonts, style variables | -| v0.2.2 | December 17, 2024 | Zod v3.25/v4 compatibility | -| v0.3.0 | January 9, 2025 | New examples, npm publishing, spec compliance | -| v0.3.1 | January 9, 2025 | SDK alignment, build improvements | - ---- - -## Detailed Release Notes - -### v0.2.0 (December 16, 2024) - -**Major Release - Foundation for Modern Architecture** - -#### New Features - -- **SSE Transport Support**: Added Server-Sent Events transport and shared server utilities -- **Styles Prop**: Introduced `styles` prop to host context for MCP Apps -- **Display Mode Request**: Apps can now request display mode instead of having it set externally -- **Server Helpers**: Added `connect()` which defaults to parent post transport -- **Peer Dependencies**: MCP SDK and React are now peer dependencies for better version flexibility - -#### API Changes - -- **Optional Client in AppBridge**: Enables custom forwarding scenarios -- **Improved Protocol Types**: Better typing for `AppRequest`, `AppNotification`, `AppResult` -- **Method Renaming**: Removed "send" prefix from request methods (breaking change) - -#### Improvements - -- "Bring your own Zod version" support for flexibility -- Enhanced basic app responsiveness for narrow viewports -- Windows compatibility improvements: - - Made Bun an optional dependency - - Added cross-env for examples - -#### Testing & Quality - -- Added Playwright E2E tests with screenshot golden testing -- Fixed E2E screenshot test consistency across CI and local environments -- Added pre-commit checks for private registry URLs in package-lock.json -- Marked `src/generated` as linguist-generated - -#### Fixes & Documentation - -- Added mimeType to resource declarations in examples -- Fixed sandbox-proxy-ready notification name in spec -- Corrected typos and formatting in spec header -- Improved tool visibility documentation - -**Full Changelog**: v0.1.0...v0.2.0 - ---- - -### v0.2.1 (December 17, 2024) - -**Patch Release - Bug Fixes and Enhancements** - -#### Changes - -1. **Tool Registration Fix** (`fix(examples)`) - - Corrected `server.registerTool` usage for non-UI tools - - Resolved missing import statements - -2. **Custom Font Support** - - Introduced the ability to pass custom fonts within MCP Apps - -3. **Style Variable Addition** - - Added a previously omitted style variable to the MCP Apps framework - -4. **Dependency Update** - - Widened `@oven/bun-*` version range for broader compatibility - -**Contributors**: @ochafik, @martinalong - ---- - -### v0.2.2 (December 17, 2024) - -**Patch Release - Zod Compatibility** - -#### Changes - -- **Zod Schema Compatibility**: Made Zod schemas compatible with both v3.25+ and v4 versions - - PR #178 by @ochafik - - This allows consumers to use either the stable Zod 3.x line or the newer Zod 4.x - -**Note**: 35 commits to main since this release, indicating significant development activity leading to v0.3.0. - ---- - -### v0.3.0 (January 9, 2025) - -**Minor Release - New Examples and NPM Publishing** - -#### Bug Fixes - -- Fixed build errors in examples (@jonathanhefner, #180) -- Moved prettier to dev dependency (@niclim, #179) -- Fixed tsc command to use tsconfig.json (@blackgirlbytes, #188) -- Added missing origin parameter to PostMessageTransport default constructor (@ochafik, #207) -- Made `toolInfo.id` optional per spec (@antonpk1, #216) -- Made example host more resilient to broken servers (@ochafik, #206) -- Added missing server-utils.ts to video-resource-server (@antonpk1, #205) - -#### Features & Enhancements - -- **New Examples**: - - Added `sheet-music-server` example (@jonathanhefner, #196) - - Added `video-resource-server` example (@antonpk1, #175) - - Added `basic-server-*` examples for Vue, Svelte, Preact, and Solid (@jonathanhefner, #141) - -- **NPM Publishing**: Published example servers to npm (@jerome3o-anthropic, #184) - -- **API Improvements**: - - Required `eventSource` in PostMessageTransport + added security tests (@ochafik, #208) - - Exported method names as constants (@idosal, #192) - - Made `ui.resourceUri` optional for tools that just need visibility (@ochafik, #210) - - Updated `viewport` type (@martinalong, #153) - -- **UX Improvements**: - - Added `safeAreaInsets` support to all example apps (@jonathanhefner, #202) - - Added npm `start` alias for `examples:start` (@jonathanhefner, #183) - -#### New Contributors - -- @niclim -- @blackgirlbytes - ---- - -### v0.3.1 (January 9, 2025) - -**Patch Release - SDK Alignment and Build Improvements** - -Released by @ochafik - -#### Changes - -1. **Fixed registerAppTool Signature** (PR #219) - - Aligned the function signature with the MCP TypeScript SDK specifications - - This is the key change addressing breaking API changes in the new SDK version - -2. **Added Build App-with-deps Target** (PR #221) - - Introduced a new build feature for applications with dependencies - -3. **Build Order Fix** (PR #185) - - Ensured the library builds before example applications - - Fixes build reliability issues - -4. **Release Automation** (PR #224) - - Completed the v0.3.1 release process - -**Commit**: `4c51eb0301aece4fe55ae6136bf6444f6a484569` (verified GPG signature) - ---- - -## Key Changes Between Versions - -### v0.2.0 -> v0.2.1 - -- Bug fixes for tool registration -- Added custom font support -- Added missing style variable -- Broadened Bun dependency version range - -### v0.2.1 -> v0.2.2 - -- Zod schema compatibility for v3.25+ and v4 - -### v0.2.2 -> v0.3.0 (Major Changes) - -- **New example servers**: sheet-music-server, video-resource-server, framework-specific examples -- **NPM publishing** of example servers -- **API changes**: - - `toolInfo.id` now optional per spec - - `ui.resourceUri` now optional for tools needing only visibility - - `eventSource` required in PostMessageTransport - - Method names exported as constants -- **Security**: Added security tests for PostMessageTransport -- **UX**: safeAreaInsets support across all examples - -### v0.3.0 -> v0.3.1 - -- **SDK Alignment**: Fixed `registerAppTool` signature to match MCP TypeScript SDK -- **Build System**: Improved build order and added app-with-deps target - ---- - -## Breaking Changes Summary - -### v0.2.0 - -- Method renaming: Removed "send" prefix from request methods -- MCP SDK and React changed to peer dependencies - -### v0.3.0 - -- `eventSource` now required in PostMessageTransport constructor (security hardening) - -### v0.3.1 - -- `registerAppTool` signature changed to align with MCP TypeScript SDK - - This is the "breaking API change" mentioned in the PR context - ---- - -## Notable Patterns - -1. **Rapid Iteration**: v0.2.1 and v0.2.2 were both released on December 17, showing quick bug fix turnaround -2. **Community Growth**: v0.3.0 welcomed new contributors (@niclim, @blackgirlbytes) -3. **Framework Expansion**: Added support for Vue, Svelte, Preact, and Solid in v0.3.0 -4. **SDK Alignment Focus**: v0.3.1 specifically addresses compatibility with the MCP TypeScript SDK - ---- - -## Analysis of registerAppTool Signature Change (v0.3.1) - -The key breaking change in v0.3.1 involves aligning `registerAppTool` with the MCP TypeScript SDK. Based on the release notes: - -- **PR #219**: "Fixed registerAppTool signature - Aligned the function signature with the MCP TypeScript SDK specifications" - -This change was necessary because the MCP TypeScript SDK updated its API, and ext-apps needed to match those changes for compatibility. This is the "breaking API change" referenced in the upgrade context. - -To understand the exact nature of this change, one would need to examine: - -1. The diff in PR #219 -2. The MCP TypeScript SDK changelog for the corresponding version -3. The before/after signatures of `registerAppTool` diff --git a/intermediate-outputs/resize-investigation-findings.md b/intermediate-outputs/resize-investigation-findings.md deleted file mode 100644 index 2a97d143..00000000 --- a/intermediate-outputs/resize-investigation-findings.md +++ /dev/null @@ -1,96 +0,0 @@ -# Resize Investigation Findings - -## Summary - -**Can apps resize themselves (both grow and shrink)?** - -| Dimension | Growing | Shrinking | Notes | -| ---------- | ------- | --------- | ------------------------------------------------ | -| **Height** | YES | YES | Works correctly - host uses `height` directly | -| **Width** | YES | LIMITED | Host uses `min-width`, treating width as a floor | - -## Detailed Analysis - -### Height Resizing (Works Fully) - -The host implementation (`examples/basic-host/src/implementation.ts:265-266`) uses `height` directly: - -```typescript -from.height = `${iframe.offsetHeight}px`; -iframe.style.height = to.height = `${height}px`; -``` - -This means height changes are applied immediately in both directions (growing and shrinking). - -**Tested and verified:** Added Hide/Show toggle to React and Vanilla JS examples. When controls are hidden, the iframe height shrinks correctly. - -### Width Resizing (Limited by Design) - -The host implementation (`examples/basic-host/src/implementation.ts:253-259`) uses `min-width` instead of `width`: - -```typescript -// Use min-width instead of width to allow responsive growing. -// With auto-resize (the default), the app reports its minimum content -// width; we honor that as a floor but allow the iframe to expand when -// the host layout allows. And we use `min(..., 100%)` so that the iframe -// shrinks with its container. -from.minWidth = `${iframe.offsetWidth}px`; -iframe.style.minWidth = to.minWidth = `min(${width}px, 100%)`; -``` - -**Implications:** - -1. The reported content width is treated as a **floor** (minimum), not a fixed size -2. The iframe can grow beyond the reported width when the container allows -3. The `min(${width}px, 100%)` allows the iframe to shrink **with its container** (responsive) -4. However, if the content shrinks but the container doesn't, the width stays at the previous value - -**This is an intentional design choice** for responsive layouts, not a bug. The comment explicitly states this behavior. - -### If Width Shrinking Is Needed - -If an app truly needs width shrinking independent of container size, the host would need to use `width` instead of `min-width`: - -```typescript -// Alternative: strict width control -iframe.style.width = to.width = `min(${width}px, 100%)`; -``` - -However, this would prevent the current responsive behavior where apps can expand to fill available container space. - -## Changes Made - -### React Example (`basic-server-react`) - -- Added `showControls` state -- Added "Hide Controls" / "Show Controls" toggle button -- Conditionally renders all action sections - -### Vanilla Example (`basic-server-vanillajs`) - -- Added `#controls` wrapper div in HTML -- Added toggle button and JS event handler -- Added CSS for `#controls` container spacing - -## Files Modified - -1. `examples/basic-server-react/src/mcp-app.tsx` - Added toggle state and conditional rendering -2. `examples/basic-server-vanillajs/mcp-app.html` - Added toggle button and controls wrapper -3. `examples/basic-server-vanillajs/src/mcp-app.ts` - Added toggle event handler -4. `examples/basic-server-vanillajs/src/mcp-app.css` - Added `#controls` CSS rules - -## Test Results - -Tested by running `npm run examples:start` and interacting with the host at `http://localhost:8080`: - -1. **React app**: Clicking "Hide Controls" correctly shrinks the iframe height -2. **Vanilla JS app**: Clicking "Hide Controls" correctly shrinks the iframe height -3. Both apps correctly grow when "Show Controls" is clicked - -## Conclusion - -Apps CAN resize themselves, and shrinking IS possible: - -- **Height shrinking works out of the box** -- **Width shrinking is intentionally limited** to support responsive layouts where the container controls expansion -- The current behavior is by design, not a limitation of the protocol From d2f53a6cda83eb8ad5316c7c84d50dc64d17d55a Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 12 Jan 2026 13:17:45 +0000 Subject: [PATCH 6/7] refactor: change McpUiResourcePermissions properties to empty objects --- src/generated/schema.json | 80 +++++++++++++++++++++++++++++---------- src/generated/schema.ts | 8 ++-- src/spec.types.ts | 8 ++-- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/generated/schema.json b/src/generated/schema.json index 8abf4923..61385e4a 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -100,19 +100,27 @@ "properties": { "camera": { "description": "Request camera access (Permission Policy `camera` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "microphone": { "description": "Request microphone access (Permission Policy `microphone` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "clipboardWrite": { "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false } }, "additionalProperties": false @@ -2307,19 +2315,27 @@ "properties": { "camera": { "description": "Request camera access (Permission Policy `camera` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "microphone": { "description": "Request microphone access (Permission Policy `microphone` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "clipboardWrite": { "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false } }, "additionalProperties": false @@ -3668,19 +3684,27 @@ "properties": { "camera": { "description": "Request camera access (Permission Policy `camera` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "microphone": { "description": "Request microphone access (Permission Policy `microphone` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "clipboardWrite": { "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false } }, "additionalProperties": false @@ -3702,19 +3726,27 @@ "properties": { "camera": { "description": "Request camera access (Permission Policy `camera` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "microphone": { "description": "Request microphone access (Permission Policy `microphone` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "clipboardWrite": { "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false } }, "additionalProperties": false @@ -3821,19 +3853,27 @@ "properties": { "camera": { "description": "Request camera access (Permission Policy `camera` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "microphone": { "description": "Request microphone access (Permission Policy `microphone` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "geolocation": { "description": "Request geolocation access (Permission Policy `geolocation` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false }, "clipboardWrite": { "description": "Request clipboard write access (Permission Policy `clipboard-write` feature).", - "type": "boolean" + "type": "object", + "properties": {}, + "additionalProperties": false } }, "additionalProperties": false diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 996d3ee2..8de9bb5a 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -219,26 +219,26 @@ export const McpUiResourceCspSchema = z.object({ export const McpUiResourcePermissionsSchema = z.object({ /** @description Request camera access (Permission Policy `camera` feature). */ camera: z - .boolean() + .object({}) .optional() .describe("Request camera access (Permission Policy `camera` feature)."), /** @description Request microphone access (Permission Policy `microphone` feature). */ microphone: z - .boolean() + .object({}) .optional() .describe( "Request microphone access (Permission Policy `microphone` feature).", ), /** @description Request geolocation access (Permission Policy `geolocation` feature). */ geolocation: z - .boolean() + .object({}) .optional() .describe( "Request geolocation access (Permission Policy `geolocation` feature).", ), /** @description Request clipboard write access (Permission Policy `clipboard-write` feature). */ clipboardWrite: z - .boolean() + .object({}) .optional() .describe( "Request clipboard write access (Permission Policy `clipboard-write` feature).", diff --git a/src/spec.types.ts b/src/spec.types.ts index f9b6d968..3e872094 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -514,13 +514,13 @@ export interface McpUiResourceCsp { */ export interface McpUiResourcePermissions { /** @description Request camera access (Permission Policy `camera` feature). */ - camera?: boolean; + camera?: {}; /** @description Request microphone access (Permission Policy `microphone` feature). */ - microphone?: boolean; + microphone?: {}; /** @description Request geolocation access (Permission Policy `geolocation` feature). */ - geolocation?: boolean; + geolocation?: {}; /** @description Request clipboard write access (Permission Policy `clipboard-write` feature). */ - clipboardWrite?: boolean; + clipboardWrite?: {}; } /** From 88c9ed43ce993cc51abbf51424aab8f69db935ad Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 12 Jan 2026 13:20:06 +0000 Subject: [PATCH 7/7] chore: remove scratch files and redundant cross-env dependency --- .gitignore | 1 - examples/basic-host/package.json | 1 - examples/simple-host/sandbox.html | 128 ------------------------------ package-lock.json | 1 - simple.ts | 100 ----------------------- 5 files changed, 231 deletions(-) delete mode 100644 examples/simple-host/sandbox.html delete mode 100644 simple.ts diff --git a/.gitignore b/.gitignore index 1aae3dab..1d78df59 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,3 @@ intermediate-findings/ # Playwright playwright-report/ test-results/ -.npm-cache.tmp diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index cb775a64..78cf826f 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -25,7 +25,6 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", - "cross-env": "^10.1.0", "express": "^5.1.0", "prettier": "^3.6.2", "vite": "^6.0.0", diff --git a/examples/simple-host/sandbox.html b/examples/simple-host/sandbox.html deleted file mode 100644 index 14849447..00000000 --- a/examples/simple-host/sandbox.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - MCP-UI Proxy - - - - - - diff --git a/package-lock.json b/package-lock.json index 453eb948..8b74a9ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,7 +86,6 @@ "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", "cors": "^2.8.5", - "cross-env": "^10.1.0", "express": "^5.1.0", "prettier": "^3.6.2", "typescript": "^5.9.3", diff --git a/simple.ts b/simple.ts deleted file mode 100644 index 6ab5e0a2..00000000 --- a/simple.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - npx -y http-server -p 8111 --cors - npm run build - bun simple.ts - */ -import { - RESOURCE_MIME_TYPE, - registerAppResource, - registerAppTool, -} from "@modelcontextprotocol/ext-apps/server"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { z } from "zod"; - -export function getExampleInlinedAppServerInstance(): McpServer { - const server = new McpServer({ name: "Example Server", version: "1.0.0" }); - const uiHtml = ` - - - - - -
- - - - `; - const resourceUri = "ui://page"; - - registerAppResource( - server, - "page", - resourceUri, - { - mimeType: RESOURCE_MIME_TYPE, - _meta: { - ui: {}, - }, - }, - () => ({ - contents: [ - { - mimeType: RESOURCE_MIME_TYPE, - text: uiHtml, - uri: resourceUri, - _meta: { - ui: { - csp: { - connectDomains: ["https://unpkg.com"], - resourceDomains: ["https://unpkg.com"], - }, - }, - }, - }, - ], - }), - ); - - registerAppTool( - server, - "show-example", - { - inputSchema: { message: z.string() }, - outputSchema: { message: z.string() }, - _meta: { - ui: { resourceUri }, - }, - }, - ({ message }: { message: string }) => ({ - content: [], - structuredContent: { message: `Server received message: ${message}` }, - _meta: { info: "example metadata" }, - }), - ); - - return server; -} - -async function main() { - const server = getExampleInlinedAppServerInstance(); - await server.server.connect(new StdioServerTransport()); -} - -main().catch((err) => { - console.error("Error running example MCP server:", err); - process.exit(1); -});