diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index cfd383bc..d98ce532 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -505,6 +505,27 @@ 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 +### App Capabilities in `ui/initialize` + +When the View sends an `ui/initialize` request to the Host, it MUST include its capabilities in the `appCapabilities` field: + +```typescript +interface McpUiAppCapabilities { + /** Experimental features (structure TBD). */ + experimental?: {}; + /** App exposes MCP-style tools that the host can call. */ + tools?: { + /** App supports tools/list_changed notifications. */ + listChanged?: boolean; + }; + /** + * Display modes the app supports. See Display Modes section for details. + * @example ["inline", "fullscreen"] + */ + availableDisplayModes?: Array<"inline" | "fullscreen" | "pip">; +} +``` + ### Host Context in `McpUiInitializeResult` When the View sends an `ui/initialize` request to the Host, the Host SHOULD include UI-specific context in the `McpUiInitializeResult`'s `hostContext` field: @@ -709,6 +730,62 @@ bridge.onsizechange = ({ width, height }) => { Apps using the SDK automatically send size-changed notifications via ResizeObserver when `autoResize` is enabled (the default). The notifications are debounced and only sent when dimensions actually change. +### Display Modes + +Views can be displayed in different modes depending on the host's capabilities and the view's declared support. + +```typescript +type McpUiDisplayMode = "inline" | "fullscreen" | "pip"; +``` + +- **inline**: Default mode, embedded within the host's content flow +- **fullscreen**: View takes over the full screen/window +- **pip**: Picture-in-picture, floating overlay + +#### Declaring Support + +**View (`appCapabilities.availableDisplayModes`):** + +Views declare which display modes they support in the `ui/initialize` request via `appCapabilities.availableDisplayModes`. This allows hosts to only offer display mode options that the view can handle. + +```typescript +// Example: View declares support for inline and fullscreen +{ + method: "ui/initialize", + params: { + appCapabilities: { + availableDisplayModes: ["inline", "fullscreen"] + }, + // ... + } +} +``` + +**Host (`HostContext.availableDisplayModes`):** + +Hosts declare which display modes they support in `HostContext.availableDisplayModes`. Views should check this before requesting a mode change. + +#### Requesting Changes + +Views request display mode changes via `ui/request-display-mode`. See the Requests section for message format. + +#### Notifying Changes + +Hosts notify views of display mode changes via `ui/notifications/host-context-changed` with the `displayMode` field. + +#### Requirements + +**View behavior:** +- View MUST declare all display modes it supports in `appCapabilities.availableDisplayModes` during initialization. +- View MUST check if the requested mode is in `availableDisplayModes` from host context before requesting a mode change. +- View MUST handle the response mode differing from the requested mode. + +**Host behavior:** +- Host MUST NOT switch the View to a display mode that does not appear in its `appCapabilities.availableDisplayModes`, if set. +- Host MUST return the resulting mode (whether updated or not) in the response to `ui/request-display-mode`. +- If the requested mode is not available, Host SHOULD return the current display mode in the response. +- Host MAY decline display mode requests from Views that did not declare said modes in their capabilities. + ### Theming Hosts can optionally pass CSS custom properties via `HostContext.styles.variables` for visual cohesion with the host environment. @@ -977,15 +1054,7 @@ Host behavior: } ``` -Host behavior: -* App MUST check if the requested mode is in `availableDisplayModes` from host context. -* It is up to the host whether it switches to the requested mode, but the host MUST return the resulting mode (whether updated or not) in the response. -* If the requested mode is not available, Host SHOULD return the current display mode in the response. -* Host MAY coerce modes on certain platforms (e.g., "pip" to "fullscreen" on mobile). - -View behavior: -* View SHOULD check `availableDisplayModes` in host context before requesting a mode change. -* View MUST handle the response mode differing from the requested mode. +See the Display Modes section for detailed behavior requirements. `ui/update-model-context` - Update the model context diff --git a/src/generated/schema.json b/src/generated/schema.json index a89dcba8..bdbb058e 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -24,6 +24,27 @@ } }, "additionalProperties": false + }, + "availableDisplayModes": { + "description": "Display modes the app supports.", + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "const": "inline" + }, + { + "type": "string", + "const": "fullscreen" + }, + { + "type": "string", + "const": "pip" + } + ], + "description": "Display mode for UI presentation." + } } }, "additionalProperties": false @@ -882,7 +903,21 @@ "description": "Display modes the host supports.", "type": "array", "items": { - "type": "string" + "anyOf": [ + { + "type": "string", + "const": "inline" + }, + { + "type": "string", + "const": "fullscreen" + }, + { + "type": "string", + "const": "pip" + } + ], + "description": "Display mode for UI presentation." } }, "containerDimensions": { @@ -1635,7 +1670,21 @@ "description": "Display modes the host supports.", "type": "array", "items": { - "type": "string" + "anyOf": [ + { + "type": "string", + "const": "inline" + }, + { + "type": "string", + "const": "fullscreen" + }, + { + "type": "string", + "const": "pip" + } + ], + "description": "Display mode for UI presentation." } }, "containerDimensions": { @@ -2284,6 +2333,27 @@ } }, "additionalProperties": false + }, + "availableDisplayModes": { + "description": "Display modes the app supports.", + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "const": "inline" + }, + { + "type": "string", + "const": "fullscreen" + }, + { + "type": "string", + "const": "pip" + } + ], + "description": "Display mode for UI presentation." + } } }, "additionalProperties": false, @@ -3172,7 +3242,21 @@ "description": "Display modes the host supports.", "type": "array", "items": { - "type": "string" + "anyOf": [ + { + "type": "string", + "const": "inline" + }, + { + "type": "string", + "const": "fullscreen" + }, + { + "type": "string", + "const": "pip" + } + ], + "description": "Display mode for UI presentation." } }, "containerDimensions": { diff --git a/src/generated/schema.ts b/src/generated/schema.ts index edbd4652..eaf8278b 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -470,6 +470,11 @@ export const McpUiAppCapabilitiesSchema = z.object({ }) .optional() .describe("App exposes MCP-style tools that the host can call."), + /** @description Display modes the app supports. */ + availableDisplayModes: z + .array(McpUiDisplayModeSchema) + .optional() + .describe("Display modes the app supports."), }); /** @@ -668,7 +673,7 @@ export const McpUiHostContextSchema = z ), /** @description Display modes the host supports. */ availableDisplayModes: z - .array(z.string()) + .array(McpUiDisplayModeSchema) .optional() .describe("Display modes the host supports."), /** diff --git a/src/spec.types.ts b/src/spec.types.ts index fc587eb6..711d8f0d 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -321,7 +321,7 @@ export interface McpUiHostContext { /** @description How the UI is currently displayed. */ displayMode?: McpUiDisplayMode; /** @description Display modes the host supports. */ - availableDisplayModes?: string[]; + availableDisplayModes?: McpUiDisplayMode[]; /** * @description Container dimensions. Represents the dimensions of the iframe or other * container holding the app. Specify either width or maxWidth, and either height or maxHeight. @@ -487,6 +487,8 @@ export interface McpUiAppCapabilities { /** @description App supports tools/list_changed notifications. */ listChanged?: boolean; }; + /** @description Display modes the app supports. */ + availableDisplayModes?: McpUiDisplayMode[]; } /**