Repository navigation
Conversation
A View can name, in ui/initialize, the display mode in which it prefers to be first shown. The preference is a hint: the Host may ignore it, and after the View's first showing does not use it to move the View without a user action. Co-authored-by: ochafik <ochafik@anthropic.com>
Add the type, regenerate the schemas, and cover it in the App/AppBridge tests: the bridge receives the field, the View learns from host context that it was applied, and an unknown value is refused. JSDoc examples show how a View declares the preference and how a host reads it from getAppCapabilities(). Co-authored-by: ochafik <ochafik@anthropic.com>
Move a View that prefers fullscreen, and lists it as available, to fullscreen once it has initialized. Fullscreen now shows a host-owned bar with the app's name and an exit button, since a View can start there before the user has seen it. Co-authored-by: ochafik <ochafik@anthropic.com>
Co-authored-by: ochafik <ochafik@anthropic.com>
Mention it in the fullscreen pattern and in the OpenAI migration guide. Co-authored-by: ochafik <ochafik@anthropic.com>
📖 Docs Preview Deployed
Includes drafts and future-dated posts. All pages served with |
@modelcontextprotocol/ext-apps
@modelcontextprotocol/server-basic-preact
@modelcontextprotocol/server-basic-react
@modelcontextprotocol/server-basic-solid
@modelcontextprotocol/server-basic-svelte
@modelcontextprotocol/server-basic-vanillajs
@modelcontextprotocol/server-basic-vue
@modelcontextprotocol/server-budget-allocator
@modelcontextprotocol/server-cohort-heatmap
@modelcontextprotocol/server-customer-segmentation
@modelcontextprotocol/server-debug
@modelcontextprotocol/server-lazy-auth
@modelcontextprotocol/server-map
@modelcontextprotocol/server-pdf
@modelcontextprotocol/server-scenario-modeler
@modelcontextprotocol/server-shadertoy
@modelcontextprotocol/server-sheet-music
@modelcontextprotocol/server-system-monitor
@modelcontextprotocol/server-threejs
@modelcontextprotocol/server-transcript
@modelcontextprotocol/server-video-resource
@modelcontextprotocol/server-wiki-explorer
commit: |
Co-authored-by: ochafik <ochafik@anthropic.com>
|
Generated by Claude Code |
Co-authored-by: ochafik <ochafik@anthropic.com>
This reverts commit 7a724a0. Co-authored-by: ochafik <ochafik@anthropic.com>
|
On the "unused function" and "unused variable" findings from the code-quality bot: I am leaving these as they are. They are all in the type-checked example files ( Generated by Claude Code |
|
thank you for the proposal! We learned from our users that changing from one view to another during render sometimes feels a bit jumpy, so our proposal is to put the available and preferred modes on the resource so we know before render where to display. |
|
@ochafik let's move |
Note
Draft proposal, opened for discussion. The open questions at the end are real ones.
Problem
A View that is mainly useful on a large surface (an editor, a dashboard, a map) cannot say so. It starts in the Host's default display mode, usually
inline, then has to sendui/request-display-modeand be moved, which the user sees as a jump.Prior art and credit
The field comes from OpenAI's public MCP extensions specification. Credit to its authors. This PR takes the name and the meaning from there, and differs in where the field lives:
_meta["openai/ui"].preferredDisplayMode, "Preferred initial display mode"appCapabilities.preferredDisplayMode, same meaningavailableDisplayModesthereui/initialize, beside the View's ownavailableDisplayModes"inline" | "fullscreen"preferredDisplayModewhen choosing the initial display mode"Their stated reason for putting it on the resource is that
appCapabilities.availableDisplayModesis known only after the app has initialized, which takes time; "Alternatives considered" below discusses that placement. The spec section carries a note that points to their document.Proposal
This change adds one optional field,
appCapabilities.preferredDisplayMode, sent inui/initializebesideavailableDisplayModes: the display mode in which the View prefers to be first shown. No new message, no new display mode. Draft spec only (specification/draft/apps.mdx).ui/initializebecause it does not offer the preferred mode, or because the View does not list it.hostContext.displayMode.ui/notifications/tool-result, unless the user has changed the View's display mode in the meantime. After that, it SHOULD NOT use the preference to change the mode without a user action.availableDisplayModes, and MUST handle being shown in another mode.Schema change
interface McpUiAppCapabilities { // ... availableDisplayModes?: Array<"inline" | "fullscreen" | "pip">; + /** + * Display mode in which the app prefers to be first shown. A hint: the host MAY ignore it. + * See Display Modes section for details. + * @example "fullscreen" + */ + preferredDisplayMode?: "inline" | "fullscreen" | "pip"; }What is in the diff
src/spec.types.tsand the regenerated schemas. No new API: a View declares the preference in theAppcapabilities, and a Host reads it withAppBridge.getAppCapabilities()once the View has initialized. Both get a type-checked JSDoc example.examples/basic-host): applies afullscreenpreference once, when the View has initialized and before it sends tool input, and only if the View also listsfullscreeninavailableDisplayModes. Fullscreen now shows a small host-owned bar that names the app and offers "Exit fullscreen", since a View can now start there before the user has seen it.docs/patterns.md, and a row indocs/migrate_from_openai_apps.md.Example
Compatibility
The field is optional: nothing changes for Views that do not send it. When a View does send it:
AppBridge, onmainbefore this change and in the published 1.7.5 (theext-apps-v1alias thatsrc/interop-v1.test.tsruns), accepts the request and strips the field (checked by hand: this branch'sAppagainst the 1.7.5AppBridgein memory, andmain's generated schema; not a test in this diff).schema.json(additionalProperties: false) refuses the wholeui/initializeuntil it updates, as for every field added toappCapabilities. A View cannot detect this in advance."inline" | "fullscreen" | "pip"does not validate: after this changeAppBridgerefuses thatui/initialize(-32602, covered by a test), as it already does for such a value inavailableDisplayModes. A new display mode would widen both fields at once.Security considerations
fullscreenorpip(a concern raised in Display mode negotiation is missing #41) gets a large or floating surface before the user has seen it, which can help it imitate Host UI. By the proposed text, a Host may decline such a start without a user action, and otherwise should keep its own controls visible.ui/request-display-mode.ui/initializefrom a View that is already shown does not renew it.Alternatives considered
_meta.ui(where the prior art puts it). Known before the View loads, which suits Hosts that lay out at tool-call time. But the Host would read it before it knows the View'sappCapabilities.availableDisplayModes, so it could not check the preference against that list unless the list moved to the resource too.Open questions
_meta.uialso carry the preference? If so, which wins?AppBridgegain a hook that sets the initial host context fromappCapabilities, so that an SDK Host can answer in theui/initializeresult instead of a follow-up notification?ui/notifications/tool-result" the right bound for a Host that has already shown the View? It allows a Host that renders the View inline while the tool runs to move it when the result lands; a stricter text would be "before it first shows the View" only.Related
#41 (display mode negotiation), #152 (
ui/request-display-mode), #739 (proposes another display mode and edits the same spec section).AI use
Drafted with AI assistance (Claude). Opened as a Draft: I have not finished reviewing every line yet, and will before marking it ready.
Generated by Claude Code