Skip to content

Commit b51ccef

Browse files
committed
feat: add requestDisplayMode API for display mode changes
Add ui/request-display-mode JSON-RPC method that allows apps to request display mode changes from the host. - Add McpUiRequestDisplayModeRequest and McpUiRequestDisplayModeResult types - Add requestDisplayMode() method to App class for guest UI - Add onrequestdisplaymode handler to AppBridge for host-side handling - Update specification documentation with new JSON-RPC method - Regenerate schemas
1 parent 3a30e94 commit b51ccef

File tree

9 files changed

+366
-0
lines changed

9 files changed

+366
-0
lines changed

package-lock.json

Lines changed: 98 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

specification/draft/apps.mdx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,48 @@ MCP Apps introduces additional JSON-RPC methods for UI-specific functionality:
496496

497497
Host SHOULD open the URL in the user's default browser or a new tab.
498498

499+
`ui/request-display-mode` - Request display mode change
500+
501+
```typescript
502+
// Request
503+
{
504+
jsonrpc: "2.0",
505+
id: 2,
506+
method: "ui/request-display-mode",
507+
params: {
508+
displayMode: "inline" | "fullscreen" | "pip"
509+
}
510+
}
511+
512+
// Success Response
513+
{
514+
jsonrpc: "2.0",
515+
id: 2,
516+
result: {
517+
success: boolean, // Whether the request was honored
518+
currentDisplayMode: string // The actual display mode after the request
519+
}
520+
}
521+
522+
// Error Response (if denied or failed)
523+
{
524+
jsonrpc: "2.0",
525+
id: 2,
526+
error: {
527+
code: -32000, // Implementation-defined error
528+
message: "Display mode change denied" | "Unsupported display mode"
529+
}
530+
}
531+
```
532+
533+
Guest UI can request a display mode change from the Host. The Host maintains full control and MAY deny the request based on:
534+
- User preferences or policies
535+
- Platform constraints (e.g., mobile platforms may not support fullscreen)
536+
- Current application state
537+
- Security considerations
538+
539+
The Host SHOULD return `success: true` and update `currentDisplayMode` if the request is honored. If denied, the Host SHOULD return `success: false` with the current (unchanged) display mode. The Host MAY send a `ui/notifications/host-context-changed` notification after honoring the request to update all context fields.
540+
499541
`ui/message` - Send message content to the host's chat interface
500542

501543
```typescript

src/app-bridge.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ import {
5454
McpUiOpenLinkRequest,
5555
McpUiOpenLinkRequestSchema,
5656
McpUiOpenLinkResult,
57+
McpUiRequestDisplayModeRequest,
58+
McpUiRequestDisplayModeRequestSchema,
59+
McpUiRequestDisplayModeResult,
5760
McpUiResourceTeardownRequest,
5861
McpUiResourceTeardownResultSchema,
5962
McpUiSandboxProxyReadyNotification,
@@ -465,6 +468,48 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
465468
);
466469
}
467470

471+
/**
472+
* Register a handler for display mode requests from the Guest UI.
473+
*
474+
* The Guest UI sends `ui/request-display-mode` requests when it wants to change
475+
* its display mode (e.g., fullscreen, picture-in-picture). The handler should
476+
* evaluate the request based on the host's capabilities and user preferences,
477+
* then return the result indicating success or denial.
478+
*
479+
* @param callback - Handler that receives display mode params and returns a result
480+
* - params.displayMode - Requested display mode ("inline", "fullscreen", "pip")
481+
* - extra - Request metadata (abort signal, session info)
482+
* - Returns: Promise<McpUiRequestDisplayModeResult> with success flag and current mode
483+
*
484+
* @example
485+
* ```typescript
486+
* bridge.onrequestdisplaymode = async ({ displayMode }, extra) => {
487+
* if (displayMode === "fullscreen" && !hostSupportsFullscreen()) {
488+
* return { success: false, currentDisplayMode: "inline" };
489+
* }
490+
*
491+
* await setAppDisplayMode(displayMode);
492+
* return { success: true, currentDisplayMode: displayMode };
493+
* };
494+
* ```
495+
*
496+
* @see {@link McpUiRequestDisplayModeRequest} for the request type
497+
* @see {@link McpUiRequestDisplayModeResult} for the result type
498+
*/
499+
set onrequestdisplaymode(
500+
callback: (
501+
params: McpUiRequestDisplayModeRequest["params"],
502+
extra: RequestHandlerExtra,
503+
) => Promise<McpUiRequestDisplayModeResult>,
504+
) {
505+
this.setRequestHandler(
506+
McpUiRequestDisplayModeRequestSchema,
507+
async (request, extra) => {
508+
return callback(request.params, extra);
509+
},
510+
);
511+
}
512+
468513
/**
469514
* Register a handler for logging messages from the Guest UI.
470515
*

src/app.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import {
3232
McpUiMessageResultSchema,
3333
McpUiOpenLinkRequest,
3434
McpUiOpenLinkResultSchema,
35+
McpUiRequestDisplayModeRequest,
36+
McpUiRequestDisplayModeResultSchema,
3537
McpUiResourceTeardownRequest,
3638
McpUiResourceTeardownRequestSchema,
3739
McpUiResourceTeardownResult,
@@ -841,6 +843,48 @@ export class App extends Protocol<Request, Notification, Result> {
841843
);
842844
}
843845

846+
/**
847+
* Request the host to change the app's display mode.
848+
*
849+
* Apps can request different display modes (inline, fullscreen, pip) to optimize
850+
* their UI for various contexts. The host may accept or reject the request based
851+
* on user preferences or platform capabilities.
852+
*
853+
* @param params - Desired display mode
854+
* @param options - Request options (timeout, etc.)
855+
* @returns Result indicating success and the current display mode
856+
*
857+
* @throws {Error} If the host denies the request (e.g., unsupported mode)
858+
* @throws {Error} If the request times out or the connection is lost
859+
*
860+
* @example Request fullscreen mode
861+
* ```typescript
862+
* try {
863+
* const result = await app.requestDisplayMode({ displayMode: "fullscreen" });
864+
* if (result.success) {
865+
* console.log("Now in", result.currentDisplayMode, "mode");
866+
* }
867+
* } catch (error) {
868+
* console.error("Failed to change display mode:", error);
869+
* }
870+
* ```
871+
*
872+
* @see {@link McpUiRequestDisplayModeRequest} for request structure
873+
*/
874+
requestDisplayMode(
875+
params: McpUiRequestDisplayModeRequest["params"],
876+
options?: RequestOptions,
877+
) {
878+
return this.request(
879+
<McpUiRequestDisplayModeRequest>{
880+
method: "ui/request-display-mode",
881+
params,
882+
},
883+
McpUiRequestDisplayModeResultSchema,
884+
options,
885+
);
886+
}
887+
844888
/**
845889
* Notify the host of UI size changes.
846890
*

src/generated/schema.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,58 @@
15451545
},
15461546
"additionalProperties": {}
15471547
},
1548+
"McpUiRequestDisplayModeRequest": {
1549+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1550+
"type": "object",
1551+
"properties": {
1552+
"method": {
1553+
"type": "string",
1554+
"const": "ui/request-display-mode"
1555+
},
1556+
"params": {
1557+
"type": "object",
1558+
"properties": {
1559+
"displayMode": {
1560+
"description": "Requested display mode.",
1561+
"anyOf": [
1562+
{
1563+
"type": "string",
1564+
"const": "inline"
1565+
},
1566+
{
1567+
"type": "string",
1568+
"const": "fullscreen"
1569+
},
1570+
{
1571+
"type": "string",
1572+
"const": "pip"
1573+
}
1574+
]
1575+
}
1576+
},
1577+
"required": ["displayMode"],
1578+
"additionalProperties": false
1579+
}
1580+
},
1581+
"required": ["method", "params"],
1582+
"additionalProperties": false
1583+
},
1584+
"McpUiRequestDisplayModeResult": {
1585+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1586+
"type": "object",
1587+
"properties": {
1588+
"success": {
1589+
"description": "Whether the display mode change was successful.",
1590+
"type": "boolean"
1591+
},
1592+
"currentDisplayMode": {
1593+
"description": "The current display mode after the request.",
1594+
"type": "string"
1595+
}
1596+
},
1597+
"required": ["success", "currentDisplayMode"],
1598+
"additionalProperties": {}
1599+
},
15481600
"McpUiResourceCsp": {
15491601
"$schema": "https://json-schema.org/draft/2020-12/schema",
15501602
"type": "object",

src/generated/schema.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ export type McpUiOpenLinkResultSchemaInferredType = z.infer<
2727
typeof generated.McpUiOpenLinkResultSchema
2828
>;
2929

30+
export type McpUiRequestDisplayModeRequestSchemaInferredType = z.infer<
31+
typeof generated.McpUiRequestDisplayModeRequestSchema
32+
>;
33+
34+
export type McpUiRequestDisplayModeResultSchemaInferredType = z.infer<
35+
typeof generated.McpUiRequestDisplayModeResultSchema
36+
>;
37+
3038
export type McpUiMessageResultSchemaInferredType = z.infer<
3139
typeof generated.McpUiMessageResultSchema
3240
>;
@@ -123,6 +131,18 @@ expectType<spec.McpUiOpenLinkResult>(
123131
expectType<McpUiOpenLinkResultSchemaInferredType>(
124132
{} as spec.McpUiOpenLinkResult,
125133
);
134+
expectType<spec.McpUiRequestDisplayModeRequest>(
135+
{} as McpUiRequestDisplayModeRequestSchemaInferredType,
136+
);
137+
expectType<McpUiRequestDisplayModeRequestSchemaInferredType>(
138+
{} as spec.McpUiRequestDisplayModeRequest,
139+
);
140+
expectType<spec.McpUiRequestDisplayModeResult>(
141+
{} as McpUiRequestDisplayModeResultSchemaInferredType,
142+
);
143+
expectType<McpUiRequestDisplayModeResultSchemaInferredType>(
144+
{} as spec.McpUiRequestDisplayModeResult,
145+
);
126146
expectType<spec.McpUiMessageResult>({} as McpUiMessageResultSchemaInferredType);
127147
expectType<McpUiMessageResultSchemaInferredType>({} as spec.McpUiMessageResult);
128148
expectType<spec.McpUiSandboxProxyReadyNotification>(

0 commit comments

Comments
 (0)