From 86d72db632aadd778d94b97a681ab03be2d5ede9 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 15:09:30 -0600 Subject: [PATCH 01/11] Standardize notification naming and fix formatting in MCP Apps spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates the MCP Apps specification to consistently use the `ui/notifications/` prefix for all notification methods (e.g., `ui/notifications/sandbox-ready`, `ui/notifications/tool-result`). Also removes trailing whitespace, fixes tab/space inconsistencies, improves code formatting with proper backticks, and clarifies terminology throughout the document. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index ace683a..28b3229 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -1,8 +1,8 @@ # SEP-1865: MCP Apps: Interactive User Interfaces for MCP -**Track:** Extensions -**Authors:** Ido Salomon, Liad Yosef, Olivier Chafik, Jerome Swannack, Jonathan Hefner, Anton Pidkuiko, Nick Cooper, Bryan Ashley, Alexei Christakis -**Status:** Draft +**Track:** Extensions +**Authors:** Ido Salomon, Liad Yosef, Olivier Chafik, Jerome Swannack, Jonathan Hefner, Anton Pidkuiko, Nick Cooper, Bryan Ashley, Alexei Christakis +**Status:** Draft **Created:** 2025-11-21 ## Abstract @@ -86,7 +86,7 @@ The resource content is returned via `resources/read`: connect_domains?: string[]; // Origins for fetch/XHR/WebSocket resource_domains?: string[]; // Origins for images, scripts, etc }; - "domain"?: string; + "domain"?: string; "prefersBorder"?: boolean; }; }; @@ -293,14 +293,14 @@ If the Host is a web page, it MUST wrap the Guest UI and communicate with it thr 1. The Host and the Sandbox MUST have different origins. 2. The Sandbox MUST have the following permissions: `allow-scripts`, `allow-same-origin`. -3. The Sandbox MUST send a `ui/sandbox-ready` notification to the host when it’s ready to process an `ui/sandbox-resource-ready` notification. -4. Once the Sandbox is ready, the Host MUST send the raw HTML resource to load in a `ui/sandbox-resource-ready` notification. +3. The Sandbox MUST send a `ui/notifications/sandbox-ready` notification to the host when it’s ready to process an `ui/sandbox-resource-ready` notification. +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'`) - Apply restrictive defaults if no CSP metadata is provided -6. The Sandbox MUST forward messages sent by the Host to the Guest UI, and vice versa, with any method that doesn’t start with `ui/sandbox-`. This includes lifecycle messages, e.g. `initialize` request & `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. +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. @@ -325,7 +325,7 @@ UI iframes can use the following subset of standard MCP protocol messages: - `ui/initialize` → `ui/notifications/initialized` - MCP-like handshake (replaces custom iframe-ready pattern in MCP-UI) - `ping` - Connection health check -### Host Context in McpUiInitializeResult +### Host Context in `McpUiInitializeResult` When the Guest UI sends an `ui/initialize` request to the Host, the Host SHOULD include UI-specific context in the `McpUiInitializeResult`’s `hostContext` field: @@ -486,7 +486,7 @@ Host SHOULD add the message to the conversation thread, preserving the specified } ``` -Host sends this notification after the Guest UI's initialize request completes, when tool arguments become available. This notification is sent at most once and is required before sending `ui/tool-result`. +Host sends this notification after the Guest UI's initialize request completes, when tool arguments become available. This notification is sent at most once and is required before sending `ui/notifications/tool-result`. `ui/notifications/tool-input-partial` - Host MAY send this notification zero or more times while the agent is streaming tool arguments, before `ui/notifications/tool-input` is sent. @@ -513,12 +513,12 @@ Guest UI behavior (optional): - MUST NOT rely on partial arguments for critical operations - SHOULD gracefully handle missing or changing fields between notifications -`ui/tool-result` - Tool execution result +`ui/notifications/tool-result` - Tool execution result ```json { "jsonrpc": "2.0", - "method": "ui/tool-result", + "method": "ui/notifications/tool-result", "params": "CallToolResult" // Standard MCP type } ``` @@ -603,22 +603,22 @@ Host MAY send this notification when any context field changes (e.g., theme togg These messages are reserved for web-based hosts that implement the recommended double-iframe sandbox architecture: -`ui/sandbox-ready` - Sandbox proxy is ready +`ui/notifications/sandbox-ready` (Sandbox Proxy → Host) - Sandbox proxy is ready ```json { "jsonrpc": "2.0", - "method": "ui/sandbox-ready", + "method": "ui/notifications/sandbox-ready", "params": {} } ``` -`ui/sandbox-resource-ready` - HTML resource ready to load +`ui/notifications/sandbox-resource-ready` (Host → Sandbox Proxy) - HTML resource ready to load ```json { "jsonrpc": "2.0", - "method": "ui/sandbox-resource-ready", + "method": "ui/notifications/sandbox-resource-ready", "params": { "html": "string", // HTML content to load "sandbox": "string" // Optional override for inner iframe `sandbox` attribute @@ -666,9 +666,9 @@ sequenceDiagram P -> P: Render inner iframe with HTML end - UI ->> H: initialize - H -->> UI: InitializeResult (e.g., host-context, capabilities, etc.) - UI ->> H: notifications/initialized + UI ->> H: ui/initialize + H -->> UI: McpUiInitializeResult (e.g., host-context, capabilities, etc.) + UI ->> H: ui/notifications/initialized opt Stream Tool input to UI H -->> UI: ui/notifications/tool-input-partial (0..n) end @@ -681,7 +681,7 @@ sequenceDiagram end ``` -Note: when the Guest UI is rendered inside a sandbox, the sandbox transparently passes events between the Guest UI and the Host (except for events that start with `sandbox-`) +Note: when the Guest UI is rendered inside a sandbox, the sandbox transparently passes messages between the Guest UI and the Host, except for messages named `ui/notifications/sandbox-*`. #### 5. Interactive Phase @@ -746,7 +746,7 @@ Note: Cleanup may be triggered at any point in the lifecycle following UI initia Tool execution results are passed to the UI through two mechanisms: -#### 1. Tool Input (via ui/notifications/tool-input notification) +#### 1. Tool Input (via `ui/notifications/tool-input` notification) The original tool call arguments: @@ -761,7 +761,7 @@ params: { } ``` -#### 2. Tool Result (via ui/tool-result notification) +#### 2. Tool Result (via `ui/notifications/tool-result` notification) The tool's execution result: @@ -783,7 +783,7 @@ The tool's execution result: } // UI receives: -Notification: ui/tool-result +Notification: ui/notifications/tool-result params: { content: [...], // Same as above structuredContent: { temperature: 72, ... }, From 31dccb95f419b347cd42876fecefc123ce05e4f6 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 15:19:05 -0600 Subject: [PATCH 02/11] Fix resource list change notification name format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrected `resources/notifications/list_changed` to `notifications/resources/list_changed` on line 208 to follow standard MCP notification naming pattern (`notifications/{category}/{event}`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- 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 28b3229..f805c1e 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -219,7 +219,7 @@ Example: - Resource MUST exist on the server - Host MUST use `resources/read` to fetch the referenced resource URI. - Host MAY prefetch and cache UI resource content for performance optimization -- Since UI resources are primarily discovered through tool metadata, Servers MAY omit UI-only resources from `resources/list` and `resources/notifications/list_changed` +- Since UI resources are primarily discovered through tool metadata, Servers MAY omit UI-only resources from `resources/list` and `notifications/resources/list_changed` #### Benefits: From 06c37b045bf145583076f88758af4a9f2a4a4016 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 15:32:31 -0600 Subject: [PATCH 03/11] Convert notification examples from JSON to TypeScript format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes code blocks for notification examples to use `typescript` fence instead of `json`. This properly represents TypeScript types (like Record, CallToolResult, Partial) without quotes, matching the format used elsewhere in the specification. Affected notifications: - ui/notifications/tool-input - ui/notifications/tool-input-partial - ui/notifications/tool-result - ui/tool-cancelled - ui/resource-teardown - ui/size-change - ui/host-context-change - ui/notifications/sandbox-ready - ui/notifications/sandbox-resource-ready 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 106 +++++++++++++++++------------------ 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index f805c1e..497b4e5 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -476,12 +476,12 @@ Host SHOULD add the message to the conversation thread, preserving the specified `ui/notifications/tool-input` - Host MUST send this notification with the complete tool arguments after the Guest UI's initialize request completes. -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/notifications/tool-input", - "params": { - "arguments": "Record" // Tool input arguments + jsonrpc: "2.0", + method: "ui/notifications/tool-input", + params: { + arguments: Record // Tool input arguments } } ``` @@ -490,12 +490,12 @@ Host sends this notification after the Guest UI's initialize request completes, `ui/notifications/tool-input-partial` - Host MAY send this notification zero or more times while the agent is streaming tool arguments, before `ui/notifications/tool-input` is sent. -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/notifications/tool-input-partial", - "params": { - "arguments": "Record" // Tool input arguments + jsonrpc: "2.0", + method: "ui/notifications/tool-input-partial", + params: { + arguments: Record // Tool input arguments } } ``` @@ -515,11 +515,11 @@ Guest UI behavior (optional): `ui/notifications/tool-result` - Tool execution result -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/notifications/tool-result", - "params": "CallToolResult" // Standard MCP type + jsonrpc: "2.0", + method: "ui/notifications/tool-result", + params: CallToolResult // Standard MCP type } ``` @@ -527,12 +527,12 @@ Host MUST send this notification when tool execution completes (if UI is display `ui/tool-cancelled` - Tool execution was cancelled -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/tool-cancelled", - "params": { - "reason": "string" + jsonrpc: "2.0", + method: "ui/tool-cancelled", + params: { + reason: string } } ``` @@ -541,30 +541,30 @@ Host MUST send this notification if the tool execution was cancelled, for any re `ui/resource-teardown` - Host notifies UI before teardown -```json +```typescript { - "jsonrpc": "2.0", - "id": 1, - "method": "ui/resource-teardown", - "params": { - "reason": "string" + jsonrpc: "2.0", + id: 1, + method: "ui/resource-teardown", + params: { + reason: string } } // Success Response { - "jsonrpc": "2.0", - "id": 1, - "result": {} // Empty result on success + jsonrpc: "2.0", + id: 1, + result: {} // Empty result on success } // Error Response (if failed) { - "jsonrpc": "2.0", - "id": 1, - "error": { - "code": -32000, // Implementation-defined error - "message": "Teardown error" + jsonrpc: "2.0", + id: 1, + error: { + code: -32000, // Implementation-defined error + message: "Teardown error" } } ``` @@ -574,13 +574,13 @@ Host SHOULD wait for a response before tearing down the resource (to prevent dat `ui/size-change` - UI’s size changed -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/size-change", - "params": { - "width": "number", // Viewport width in pixels - "height": "number" // Viewport height in pixels + jsonrpc: "2.0", + method: "ui/size-change", + params: { + width: number, // Viewport width in pixels + height: number // Viewport height in pixels } } ``` @@ -589,11 +589,11 @@ Guest UI SHOULD send this notification when rendered content body size changes ( `ui/host-context-change` - Host context has changed -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/host-context-change", - "params": "Partial" // See HostContext type above + jsonrpc: "2.0", + method: "ui/host-context-change", + params: Partial // See HostContext type above } ``` @@ -605,23 +605,23 @@ These messages are reserved for web-based hosts that implement the recommended d `ui/notifications/sandbox-ready` (Sandbox Proxy → Host) - Sandbox proxy is ready -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/notifications/sandbox-ready", - "params": {} + jsonrpc: "2.0", + method: "ui/notifications/sandbox-ready", + params: {} } ``` `ui/notifications/sandbox-resource-ready` (Host → Sandbox Proxy) - HTML resource ready to load -```json +```typescript { - "jsonrpc": "2.0", - "method": "ui/notifications/sandbox-resource-ready", - "params": { - "html": "string", // HTML content to load - "sandbox": "string" // Optional override for inner iframe `sandbox` attribute + jsonrpc: "2.0", + method: "ui/notifications/sandbox-resource-ready", + params: { + html: string, // HTML content to load + sandbox: string // Optional override for inner iframe `sandbox` attribute } } ``` From 4aa2eb8dbe037cd2a06d08e9298907fccdde26fa Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 15:39:34 -0600 Subject: [PATCH 04/11] Remove unnecessary quotes from TypeScript property names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unquote ui, csp, domain, and prefersBorder properties in the resources/read response TypeScript example to follow standard TypeScript syntax conventions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 497b4e5..fbb1143 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -81,13 +81,13 @@ The resource content is returned via `resources/read`: text?: string; // HTML content as string blob?: string; // OR base64-encoded HTML _meta?: { - "ui"?: { - "csp"?: { + ui?: { + csp?: { connect_domains?: string[]; // Origins for fetch/XHR/WebSocket resource_domains?: string[]; // Origins for images, scripts, etc }; - "domain"?: string; - "prefersBorder"?: boolean; + domain?: string; + prefersBorder?: boolean; }; }; }]; From 1eaf7377ca53c665f0e6e20f338949d3838cab23 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 15:52:24 -0600 Subject: [PATCH 05/11] Fix indentation in Mermaid sequence diagrams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrects the indentation of nested block structures (alt, opt, par, loop) in all four Mermaid sequence diagrams. Content inside nested blocks is now properly indented relative to the block keywords, making the hierarchical structure clearer and more readable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 142 +++++++++++++++++------------------ 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index fbb1143..9401f8e 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -636,10 +636,10 @@ The typical lifecycle for rendering a UI resource: ```mermaid sequenceDiagram - participant H as Host - participant S as MCP Server + participant H as Host + participant S as MCP Server - autonumber + autonumber S -->> H: resources/list (includes ui:// resources) S -->> H: tools/list (includes tools with ui/resourceUri metadata) ``` @@ -648,37 +648,37 @@ sequenceDiagram ```mermaid sequenceDiagram - participant H as Host - participant UI as Guest UI (iframe) - participant P as Sandbox Proxy - participant S as MCP Server + participant H as Host + participant UI as Guest UI (iframe) + participant P as Sandbox Proxy + participant S as MCP Server - autonumber - par UI Tool call - H ->> S: tools/call to Tool with ui/resourceUri metadata - and UI initialization - alt Desktop/Native hosts - H ->> H: Render Guest UI in an iframe (HTML from the ui:// resource) - else Web hosts - H ->> H: Render Sandbox Proxy in an iframe (different origin) - P ->> H: ui/notifications/sandbox-ready - H -->> P: ui/notifications/sandbox-resource-ready (HTML content) - P -> P: Render inner iframe with HTML + autonumber + par UI Tool call + H ->> S: tools/call to Tool with ui/resourceUri metadata + and UI initialization + alt Desktop/Native hosts + H ->> H: Render Guest UI in an iframe (HTML from the ui:// resource) + else Web hosts + H ->> H: Render Sandbox Proxy in an iframe (different origin) + P ->> H: ui/notifications/sandbox-ready + H -->> P: ui/notifications/sandbox-resource-ready (HTML content) + P -> P: Render inner iframe with HTML - end - UI ->> H: ui/initialize - H -->> UI: McpUiInitializeResult (e.g., host-context, capabilities, etc.) - UI ->> H: ui/notifications/initialized - opt Stream Tool input to UI - H -->> UI: ui/notifications/tool-input-partial (0..n) - end - H -->> UI: ui/notifications/tool-input (complete) - end - alt Tool complete - H -->> UI: ui/notifications/tool-result - else Tool cancelled - H -->> UI: ui/notifications/tool-cancelled - end + end + UI ->> H: ui/initialize + H -->> UI: McpUiInitializeResult (e.g., host-context, capabilities, etc.) + UI ->> H: ui/notifications/initialized + opt Stream Tool input to UI + H -->> UI: ui/notifications/tool-input-partial (0..n) + end + H -->> UI: ui/notifications/tool-input (complete) + end + alt Tool complete + H -->> UI: ui/notifications/tool-result + else Tool cancelled + H -->> UI: ui/notifications/tool-cancelled + end ``` Note: when the Guest UI is rendered inside a sandbox, the sandbox transparently passes messages between the Guest UI and the Host, except for messages named `ui/notifications/sandbox-*`. @@ -687,51 +687,51 @@ Note: when the Guest UI is rendered inside a sandbox, the sandbox transparently ```mermaid sequenceDiagram - actor U as User / Agent - participant H as Host - participant UI as Guest UI (iframe) - participant S as MCP Server - loop Interactive phase - U ->> UI: interaction (e.g., click) - alt Tool call - UI ->> H: tools/call - H ->> S: tools/call - opt Stream Tool input to UI - H -->> UI: ui/notifications/tool-input-partial (0..n) - end - H -->> UI: ui/notifications/tool-input (complete) - H-->>UI: ui/notifications/tool-result - else Message - UI ->> H: ui/message - H -->> H: Process message and follow up - else Notify - UI ->> H: notifications/message - H ->> H: Process notification and store in context - else Resource read - UI ->> H: resources/read - H ->> S: resources/read - S --> H: resources/read response - H --> UI: resources/read response - end - opt UI notifications - UI ->> H: notifications (e.g., ui/notifications/size-change) - end - opt Host notifications - H ->> UI: notifications (e.g., ui/notifications/host-context-change) - end - end + actor U as User / Agent + participant H as Host + participant UI as Guest UI (iframe) + participant S as MCP Server + loop Interactive phase + U ->> UI: interaction (e.g., click) + alt Tool call + UI ->> H: tools/call + H ->> S: tools/call + opt Stream Tool input to UI + H -->> UI: ui/notifications/tool-input-partial (0..n) + end + H -->> UI: ui/notifications/tool-input (complete) + H-->>UI: ui/notifications/tool-result + else Message + UI ->> H: ui/message + H -->> H: Process message and follow up + else Notify + UI ->> H: notifications/message + H ->> H: Process notification and store in context + else Resource read + UI ->> H: resources/read + H ->> S: resources/read + S --> H: resources/read response + H --> UI: resources/read response + end + opt UI notifications + UI ->> H: notifications (e.g., ui/notifications/size-change) + end + opt Host notifications + H ->> UI: notifications (e.g., ui/notifications/host-context-change) + end + end ``` #### 6. Cleanup ```mermaid sequenceDiagram - participant H as Host - participant UI as Guest UI (iframe) - H ->> UI: ui/resource-teardown - UI --> UI: Graceful termination - UI -->> H: ui/resource-teardown response - H -x H: Tear down iframe and listeners + participant H as Host + participant UI as Guest UI (iframe) + H ->> UI: ui/resource-teardown + UI --> UI: Graceful termination + UI -->> H: ui/resource-teardown response + H -x H: Tear down iframe and listeners ``` Note: Cleanup may be triggered at any point in the lifecycle following UI initialization. From cc08467772adfc556733fc731d1eecf2242d3982 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 16:04:52 -0600 Subject: [PATCH 06/11] Complete JSON-RPC message examples in Data Passing section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tool input and tool result examples now show fully formed JSON-RPC messages with all required fields (jsonrpc, id/method, result/params) to better demonstrate the protocol format. Also fixes MDX parsing error in Client\<>Server heading by escaping angle brackets. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 81 +++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 9401f8e..b257256 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -751,13 +751,28 @@ Tool execution results are passed to the UI through two mechanisms: The original tool call arguments: ```typescript -// Tool was called with: -tools/call("get_weather", { location: "San Francisco" }) +// Tool was called with (JSON-RPC request from Host to Server): +{ + jsonrpc: "2.0", + id: 1, + method: "tools/call", + params: { + name: "get_weather", + arguments: { + location: "San Francisco" + } + } +} -// UI receives: -notification: ui/notifications/tool-input -params: { - arguments: { location: "San Francisco" } +// UI receives (JSON-RPC notification from Host to UI): +{ + jsonrpc: "2.0", + method: "ui/notifications/tool-input", + params: { + arguments: { + location: "San Francisco" + } + } } ``` @@ -766,28 +781,44 @@ params: { The tool's execution result: ```typescript -// Server returns from tool execution: +// Server returns from tool execution (JSON-RPC response): { - content: [ - { type: "text", text: "Current weather: Sunny, 72°F" } - ], - structuredContent: { - temperature: 72, - conditions: "sunny", - humidity: 45 - }, - _meta: { - timestamp: "2025-11-10T15:30:00Z", - source: "weather-api" + jsonrpc: "2.0", + id: 1, // Matches the tools/call request id + result: { + content: [ + { type: "text", text: "Current weather: Sunny, 72°F" } + ], + structuredContent: { + temperature: 72, + conditions: "sunny", + humidity: 45 + }, + _meta: { + timestamp: "2025-11-10T15:30:00Z", + source: "weather-api" + } } } -// UI receives: -Notification: ui/notifications/tool-result -params: { - content: [...], // Same as above - structuredContent: { temperature: 72, ... }, - _meta: { timestamp: "...", ... } +// UI receives (JSON-RPC notification): +{ + jsonrpc: "2.0", + method: "ui/notifications/tool-result", + params: { + content: [ + { type: "text", text: "Current weather: Sunny, 72°F" } + ], + structuredContent: { + temperature: 72, + conditions: "sunny", + humidity: 45 + }, + _meta: { + timestamp: "2025-11-10T15:30:00Z", + source: "weather-api" + } + } } ``` @@ -812,7 +843,7 @@ This pattern enables interactive, self-updating widgets. Note: The called tool may not appear in `tools/list` responses. MCP servers MAY expose private tools specifically designed for UI interaction that are not visible to the agent. UI implementations SHOULD attempt to call tools by name regardless of discoverability. The specification for Private Tools will be covered in a future SEP. -### Client<>Server Capability Negotiation +### Client\<\>Server Capability Negotiation Clients and servers negotiate MCP Apps support through the standard MCP extensions capability mechanism (defined in SEP-1724). From 0d654585522b550b68f7c28d5501dd8411f11edc Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 16:29:02 -0600 Subject: [PATCH 07/11] Improve CSP example formatting and clarity in apps specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change CSP code block from yaml to typescript fence, add context showing where csp variable originates (resource.\_meta?.ui?.csp), rename cspHeader to cspValue for accuracy, and remove Content-Security-Policy: prefix since the variable holds just the policy value, not a complete HTTP header. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index b257256..7701afa 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -1044,17 +1044,20 @@ Hosts MUST enforce Content Security Policies based on resource metadata. **CSP Construction from Metadata:** -```yaml -Content-Security-Policy: +```typescript +const csp = resource._meta?.ui?.csp; + +const cspValue = ` default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; - connect-src 'self' ${csp.connect_domains?.join(' ') || ''}; - img-src 'self' data: ${csp.resource_domains?.join(' ') || ''}; - font-src 'self' ${csp.resource_domains?.join(' ') || ''}; + connect-src 'self' ${csp?.connect_domains?.join(' ') || ''}; + img-src 'self' data: ${csp?.resource_domains?.join(' ') || ''}; + font-src 'self' ${csp?.resource_domains?.join(' ') || ''}; frame-src 'none'; object-src 'none'; base-uri 'self'; +`; ``` **Security Requirements:** From a8107a673306767fadba0ccc08b3039b5b2e5fdc Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 16:41:37 -0600 Subject: [PATCH 08/11] Convert UiResourceMeta documentation to TypeDoc comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move metadata field documentation from separate prose section into TypeDoc comments on the UiResourceMeta interface properties. This consolidates the documentation and eliminates duplication while making it more maintainable and IDE-friendly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 76 +++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 7701afa..57c67d7 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -61,11 +61,56 @@ interface UIResource { } interface UIResourceMeta { + /** + * Content Security Policy configuration + * + * Servers declare which external origins their UI needs to access. + * Hosts use this to enforce appropriate CSP headers. + */ csp?: { + /** + * Origins for network requests + * + * - Empty or omitted = no external connections (secure default) + * - Maps to CSP `connect-src` directive + * + * @example + * ["https://api.weather.com", "wss://realtime.service.com"] + */ connectDomains?: string[], + /** + * Origins for static resources (images, scripts, stylesheets, fonts) + * + * - Empty or omitted = no external resources (secure default) + * - Wildcard subdomains supported: `https://*.example.com` + * - Maps to CSP `img-src`, `script-src`, `style-src`, `font-src` directives + * + * @example + * ["https://cdn.jsdelivr.net", "https://*.cloudflare.com"] + */ resourceDomains?: string[], }, + /** + * Dedicated origin for widget + * + * Optional domain for the widget's sandbox origin. Useful when widgets need + * dedicated origins for API key allowlists or cross-origin isolation. + * + * If omitted, Host uses default sandbox origin. + * + * @example + * "https://weather-widget.example.com" + */ domain?: string, + /** + * Visual boundary preference + * + * Boolean indicating the UI prefers a visible border. Useful for widgets + * that might blend with host background. + * + * - `true`: Request visible border (host decides styling) + * - `false` or omitted: No preference + */ prefersBorder?: boolean, } ``` @@ -101,37 +146,6 @@ The resource content is returned via `resources/read`: - Content MUST be provided via either `text` (string) or `blob` (base64-encoded) - Content MUST be valid HTML5 document -#### Metadata Fields: - -**`ui.csp` - Content Security Policy configuration** - -Servers declare which external origins their UI needs to access. Hosts use this to enforce appropriate CSP headers. - -- `connect_domains`: Origins for network requests - - Example: `["https://api.weather.com", "wss://realtime.service.com"]` - - Empty or omitted = no external connections (secure default) - - Maps to CSP `connect-src` directive - -- `resource_domains`: Origins for static resources (images, scripts, stylesheets, fonts) - - Example: `["https://cdn.jsdelivr.net", "https://*.cloudflare.com"]` - - Empty or omitted = no external resources (secure default) - - Wildcard subdomains supported: `https://*.example.com` - - Maps to CSP `img-src`, `script-src`, `style-src`, `font-src` directives - -**`ui.domain` - Dedicated origin for widget** - -Optional domain for the widget's sandbox origin. Useful when widgets need dedicated origins for API key allowlists or cross-origin isolation. - -- Example: `"https://weather-widget.example.com"` -- If omitted, Host uses default sandbox origin - -**`ui.prefersBorder` - Visual boundary preference** - -Boolean indicating the UI prefers a visible border. Useful for widgets that might blend with host background. - -- `true`: Request visible border (host decides styling) -- `false` or omitted: No preference - #### Host Behavior: - **CSP Enforcement:** Host MUST construct CSP headers based on declared domains From ae9d87f53020cdbfb796faf9b95f755e7516ab95 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Fri, 21 Nov 2025 16:45:49 -0600 Subject: [PATCH 09/11] Convert UIResource documentation to TypeDoc comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace inline comments with comprehensive TypeDoc comments that explain the purpose and usage of each property. Add examples and clarify constraints (MUST/SHOULD) for better developer understanding. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 54 ++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 57c67d7..f88e7ca 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -53,11 +53,55 @@ UI resources are declared using the standard MCP resource pattern with specific ```typescript interface UIResource { - uri: string; // MUST start with 'ui://' - name: string; // Human-readable identifier - description?: string; // Description of the UI resource - mimeType: string; // SHOULD be 'text/html+mcp' in MVP - _meta?: UIResourceMeta // Resource Metadata + /** + * Unique identifier for the UI resource + * + * MUST use the `ui://` URI scheme to distinguish UI resources from other + * MCP resource types. + * + * @example + * "ui://weather-dashboard" + */ + uri: string; + + /** + * Human-readable display name for the UI resource + * + * Used for listing and identifying the resource in host interfaces. + * + * @example + * "Weather Dashboard" + */ + name: string; + + /** + * Optional description of the UI resource's purpose and functionality + * + * Provides context about what the UI does and when to use it. + * + * @example + * "Interactive weather visualization with real-time updates" + */ + description?: string; + + /** + * MIME type of the UI content + * + * SHOULD be `text/html+mcp` for HTML-based UIs in the initial MVP. + * Other content types are reserved for future extensions. + * + * @example + * "text/html+mcp" + */ + mimeType: string; + + /** + * Resource metadata for security and rendering configuration + * + * Includes Content Security Policy configuration, dedicated domain settings, + * and visual preferences. + */ + _meta?: UIResourceMeta } interface UIResourceMeta { From 9aa129fe34f616517c6e7bbbd4c21aa7f6428f6c Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Mon, 24 Nov 2025 08:45:44 -0600 Subject: [PATCH 10/11] Fix indentation of domain and prefersBorder in UiResourceMeta example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The domain and prefersBorder properties are now properly indented to be at the same hierarchical level as csp within the ui object. Addresses PR comment: https://github.com/modelcontextprotocol/ext-apps/pull/5#discussion_r2553064940 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index f88e7ca..7379fb4 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -175,8 +175,8 @@ The resource content is returned via `resources/read`: connect_domains?: string[]; // Origins for fetch/XHR/WebSocket resource_domains?: string[]; // Origins for images, scripts, etc }; - domain?: string; - prefersBorder?: boolean; + domain?: string; + prefersBorder?: boolean; }; }; }]; From 120a673904d680341fd2f124644335d9d423bf58 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Mon, 24 Nov 2025 08:51:53 -0600 Subject: [PATCH 11/11] Fix UIResource \_meta type to match actual usage pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UIResource interface incorrectly specified `_meta?: UIResourceMeta` when it should be `_meta?: { ui?: UIResourceMeta }` to match the resources/read response format and all usage examples throughout the document. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- specification/draft/apps.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 7379fb4..24d63aa 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -101,7 +101,9 @@ interface UIResource { * Includes Content Security Policy configuration, dedicated domain settings, * and visual preferences. */ - _meta?: UIResourceMeta + _meta?: { + ui?: UIResourceMeta; + } } interface UIResourceMeta {