Skip to content

Conversation

@martinalong
Copy link
Collaborator

Document the availableDisplayModes field in McpUiAppCapabilities that allows apps to declare which display modes they support. This enables hosts to compute the intersection of host and app capabilities and only offer appropriate display mode controls.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 23, 2026

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/ext-apps@331

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-react@331

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-vanillajs@331

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-budget-allocator@331

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-cohort-heatmap@331

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-customer-segmentation@331

@modelcontextprotocol/server-map

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-map@331

@modelcontextprotocol/server-pdf

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-pdf@331

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-scenario-modeler@331

@modelcontextprotocol/server-shadertoy

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-shadertoy@331

@modelcontextprotocol/server-sheet-music

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-sheet-music@331

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-system-monitor@331

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-threejs@331

@modelcontextprotocol/server-transcript

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-transcript@331

@modelcontextprotocol/server-video-resource

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-video-resource@331

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-wiki-explorer@331

commit: 3d182cc

Copy link
Contributor

@ochafik ochafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Martina!


### App Capabilities in `ui/initialize`

When the Guest UI sends an `ui/initialize` request to the Host, it MUST include its capabilities in the `appCapabilities` field:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now just "view" (cf. #325)

*
* @example ["inline", "fullscreen"]
*/
availableDisplayModes?: Array<"inline" | "fullscreen" | "pip">;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would clarify the purpose of this (which is to allow the host to change the view's display mode).

In the carousel app example you mentioned, the app might support fullscreen but only on its own terms (e.g. when user clicks somewhere specific in their UI), although not sure if that's a use-case worth the complexity.

@idosal wdyt?

Copy link
Collaborator

@idosal idosal Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the intent, we should remove the requirement for the host to validate intersections, and instead suggest that it honors requests for its declared supported display modes (Host SHOULD honor requests for its declared supported display modes). We may enforce validation (e.g., Host MUST reject the request if the app's display mode isn't supported).

EDIT: I saw you've addressed this in your other comments. Completely agree :)

Copy link
Collaborator

@liady liady Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which is to allow the host to change the view's display mode

Yes, this part is not clear from this PR. Also the dynamic use case (availableDisplayModes changing as a result of user interaction like you mentioned) is not covered here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, didn't consider a dynamic value for this (this might be even more niche), more like the app doesn't want the host to initiate the fullscreen for it, it wants to be in control of when it wants to go fullscreen. But then again, maybe a bit niche, let's forget it for now ;-)

**Host Behavior:**

- Host SHOULD intersect its own `availableDisplayModes` with the app's declared modes
- When responding to `ui/request-display-mode`, host SHOULD only allow modes both support
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for separation of concerns, host should only bother checking against its own supported modes. Would be good to spec out what happens when receiving an invalid mode, not sure it that's already covererd in the other availableDisplayModes section

}
```

#### `availableDisplayModes`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#### `availableDisplayModes`
#### View `availableDisplayModes`

(have clear local disambiguation w/ the other

#### Host `availableDisplayModes`

- Host SHOULD intersect its own `availableDisplayModes` with the app's declared modes
- When responding to `ui/request-display-mode`, host SHOULD only allow modes both support
- Host MAY include the computed available modes in `HostContext.availableDisplayModes`
- Host MAY assume that all apps support `inline` display mode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

host should only assume this if the spec says they MUST support inline above (which I'm not think if fair, e.g. some apps might wanna start in pip from the get go)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. We shouldn't force inline

@ochafik ochafik requested review from idosal and liady January 23, 2026 16:02

### App Capabilities in `ui/initialize`

When the Guest UI sends an `ui/initialize` request to the Host, it MUST include its capabilities in the `appCapabilities` field:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When the Guest UI sends an `ui/initialize` request to the Host, it MUST include its capabilities in the `appCapabilities` field:
When the View sends an `ui/initialize` request to the Host, it MUST include its capabilities in the `appCapabilities` field:

@martinalong martinalong requested review from liady and ochafik January 23, 2026 17:32
@martinalong
Copy link
Collaborator Author

Made those changes!

@martinalong
Copy link
Collaborator Author

Oop I realized that the reason this whole thing was missing in the first place was b/c my PR to add it to the app capabilities was merged into the wrong branch earlier. Added a commit that actually adds this field to app capabilities!

@martinalong
Copy link
Collaborator Author

cc @idosal @liady @ochafik for re-review!

Copy link
Contributor

@ochafik ochafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @martinalong !


**Host Behavior:**

- Host SHOULD honor requests for its declared supported display modes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like this belongs to the Host availableDisplayModes / ui/request-display-mode sections no?

- If omitted, hosts MAY assume the app supports all modes, or only `inline`. Apps that do not specify this field may not be able to take over fullscreen automatically.
- Apps MUST handle graceful degradation if placed in an unsupported mode

**Host Behavior:**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The host MUST NOT switch the view to a display mode that does not appear in its AppCapabilities.availableDisplayModes, if it is set.

}
```

#### View `availableDisplayModes`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there's already an untitled section:

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.

Could we merge it and the new material to a #### Display Modes section?

Starts w/ the types

  • Declaring support from View (AppCapabilities.availableDisplayModes)
  • Declaring support from Host (HostCapabilities.availableDisplayModes)
    Then the methods:
  • Requesting change from View (ui/request-display-mode)
  • Notifiying change from Host (ui/notifications/host-context-changed)
    Then the MUSTs and SHOULDs.

And this can be referenced from this and other sections.

*
* @example ["inline", "fullscreen"]
*/
availableDisplayModes?: Array<"inline" | "fullscreen" | "pip">;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, didn't consider a dynamic value for this (this might be even more niche), more like the app doesn't want the host to initiate the fullscreen for it, it wants to be in control of when it wants to go fullscreen. But then again, maybe a bit niche, let's forget it for now ;-)


- Apps SHOULD declare all display modes they are designed to support
- If omitted, hosts MAY assume the app supports all modes, or only `inline`. Apps that do not specify this field may not be able to take over fullscreen automatically.
- Apps MUST handle graceful degradation if placed in an unsupported mode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that from inline to fullscreen, they just need to be reactive and respect insets (which they need to do anyway).

But anyway, why wouldn't we guarantee to an app we won't go against its express wishes?


**App Behavior:**

- Apps SHOULD declare all display modes they are designed to support
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd even say MUST, and say that Hosts MAY decline display mode requests from apps that didn't declare said modes in the capabilities.

ochafik
ochafik previously approved these changes Jan 23, 2026
Copy link
Contributor

@ochafik ochafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last nit otherwise looks good, thanks @martinalong !

- 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 coerce modes on certain platforms (e.g., "pip" to "fullscreen" on mobile).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this conflicts w/ host MUST NOT switch to unsupported display mode above, just drop?

martinalong and others added 7 commits January 23, 2026 12:54
Document the availableDisplayModes field in McpUiAppCapabilities that allows
apps to declare which display modes they support. This enables hosts to
compute the intersection of host and app capabilities and only offer
appropriate display mode controls.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Change "Guest UI" to "View" terminology
- Add "View" prefix to availableDisplayModes heading for disambiguation
- Remove intersection logic - host only checks its own supported modes
- Remove assumption that apps must support inline mode
- Simplify host behavior to honor/reject based on its own capabilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add `availableDisplayModes` field to `McpUiAppCapabilities` so apps can
  declare which display modes they support
- Change `HostContext.availableDisplayModes` type from `string[]` to
  `McpUiDisplayMode[]` for type safety
- Regenerate schemas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Remove separate "App Capabilities" section for availableDisplayModes
- Add display mode requirements to existing ui/request-display-mode section:
  - Host MUST NOT switch View to mode not in appCapabilities.availableDisplayModes
  - Host MAY decline requests from Views that didn't declare modes
  - View MUST declare supported modes during initialization

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Restore McpUiAppCapabilities interface documentation with availableDisplayModes
- Restore "View MUST check if the requested mode is in availableDisplayModes
  from host context" clause that was accidentally removed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Consolidate all display mode documentation into a single section:
- Display mode types (inline, fullscreen, pip)
- Declaring support from View (appCapabilities.availableDisplayModes)
- Declaring support from Host (HostContext.availableDisplayModes)
- Requesting changes (ui/request-display-mode)
- Notifying changes (ui/notifications/host-context-changed)
- All MUST/SHOULD requirements for View and Host behavior

The ui/request-display-mode in Requests section now references
the Display Modes section for behavior details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@martinalong martinalong force-pushed the martinalong/app-capabilities-docs branch from 30c2c7c to b4a9a5e Compare January 23, 2026 20:54
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@ochafik ochafik merged commit 89f58bf into main Jan 23, 2026
18 of 19 checks passed
ochafik pushed a commit that referenced this pull request Jan 26, 2026
… spec (#331)

* Add appCapabilities.availableDisplayModes documentation to spec

Document the availableDisplayModes field in McpUiAppCapabilities that allows
apps to declare which display modes they support. This enables hosts to
compute the intersection of host and app capabilities and only offer
appropriate display mode controls.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Address PR review feedback

- Change "Guest UI" to "View" terminology
- Add "View" prefix to availableDisplayModes heading for disambiguation
- Remove intersection logic - host only checks its own supported modes
- Remove assumption that apps must support inline mode
- Simplify host behavior to honor/reject based on its own capabilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Add appCapabilities.availableDisplayModes to types

- Add `availableDisplayModes` field to `McpUiAppCapabilities` so apps can
  declare which display modes they support
- Change `HostContext.availableDisplayModes` type from `string[]` to
  `McpUiDisplayMode[]` for type safety
- Regenerate schemas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Address Olivier's feedback - consolidate display mode docs

- Remove separate "App Capabilities" section for availableDisplayModes
- Add display mode requirements to existing ui/request-display-mode section:
  - Host MUST NOT switch View to mode not in appCapabilities.availableDisplayModes
  - Host MAY decline requests from Views that didn't declare modes
  - View MUST declare supported modes during initialization

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Restore McpUiAppCapabilities docs and fix display mode clause

- Restore McpUiAppCapabilities interface documentation with availableDisplayModes
- Restore "View MUST check if the requested mode is in availableDisplayModes
  from host context" clause that was accidentally removed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Create unified Display Modes section

Consolidate all display mode documentation into a single section:
- Display mode types (inline, fullscreen, pip)
- Declaring support from View (appCapabilities.availableDisplayModes)
- Declaring support from Host (HostContext.availableDisplayModes)
- Requesting changes (ui/request-display-mode)
- Notifying changes (ui/notifications/host-context-changed)
- All MUST/SHOULD requirements for View and Host behavior

The ui/request-display-mode in Requests section now references
the Display Modes section for behavior details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Remove mode coercion clause from display modes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Regenerate schemas with correct version

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: Claude <[email protected]>
ochafik added a commit that referenced this pull request Jan 26, 2026
* Adding a simple (bad) pdf icon for directory (#330)

* chore: bump version to 0.4.2

- Update package version from 0.4.1 to 0.4.2
- Pin @types/node to 22.10.0 for dev compatibility
- Override seroval to ^1.4.1 to fix security vulnerabilities
- Use '*' for workspace SDK dependency (works with unpublished versions)

* fix(e2e): update Map Server display name to CesiumJS Map Server (#324)

* fix(test): update map-server test to expect 'CesiumJS Map Server'

The server name was renamed but the E2E test config was not updated.

* fix: change in another file

---------

Co-authored-by: Claude <[email protected]>
Co-authored-by: Olivier Chafik <[email protected]>

* feat(server): add getUiCapability helper for capability negotiation (#313)

* feat(server): add hasUiSupport/getUiCapability for experimental+extensions

Support double-tagging for MCP Apps capability negotiation:
- Check both experimental and extensions fields in client capabilities
- Add hasUiSupport() to easily check if client supports MCP Apps
- Add getUiCapability() to retrieve the capability settings
- Update spec to document both capability locations

This enables forward compatibility as MCP transitions from experimental
to the extensions field (SEP-1724).

Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 0
Claude-Permission-Prompts: 0
Claude-Escapes: 0

* fix: update @types/node version in package-lock.json

Replace invalid 22.19.5/22.19.6/22.19.7 versions with valid 22.19.3

* docs(spec): clarify hosts SHOULD double-tag, servers SHOULD check both

- Hosts SHOULD advertise capabilities in both experimental and extensions
- Servers SHOULD check both locations, preferring extensions when present
- Updated example to show double-tagging pattern

* chore: remove double-tagging recommendation, keep hasUiSupport helper

Reverts the spec changes that recommended clients double-tag capabilities
in both experimental and extensions fields. The helper functions remain
as they're useful for checking capability in either location.

* refactor(server): simplify to only getUiCapability, remove hasUiSupport

- Remove hasUiSupport function (use getUiCapability directly)
- Remove double-tagging logic (only check extensions field)
- Add ClientCapabilitiesWithExtensions type using SDK's ClientCapabilities
- Update spec to use getUiCapability
- Simplify tests

* fix: update @see link to getUiCapability

* refactor(server): inline ClientCapabilitiesWithExtensions type in getUiCapability

* refactor: move McpUiClientCapabilities to spec.types.ts

* fix: update playwright docker image to v1.58.0

* fix: use -i instead of -it for docker commands (no TTY needed)

* fix: Python server dependency resolution and Docker host validation (#333)

* fix(say-server): add --index flag to uv run for PyPI resolution

The pocket-tts dependency wasn't resolving without explicit --index flag
when invoked via npm scripts.

* fix(qr-server): allow Docker bridge IP in transport security

Add 172.17.0.1:* to allowed_hosts to fix 421 Misdirected Request errors
when the server is accessed from Docker containers.

* feat(basic-host): Add theme toggle and MCP style variables (#336)

* feat(basic-host): add theme toggle and MCP style variables

Add theme support to basic-host example:
- theme.ts: Global theme manager with light/dark toggle
- host-styles.ts: Full set of MCP CSS variables using light-dark()
- ThemeToggle component with sun/moon icons
- Theme-aware CSS using CSS custom properties
- Pass theme + styles to apps via hostContext
- Notify apps when theme changes via sendHostContextChange

Apps now receive:
- hostContext.theme: 'light' | 'dark'
- hostContext.styles.variables: All 60+ MCP CSS variables
- onhostcontextchanged notifications when theme changes

* test: update e2e goldens for theme changes

* [MCP Apps] Add appCapabilities.availableDisplayModes documentation to spec (#331)

* Add appCapabilities.availableDisplayModes documentation to spec

Document the availableDisplayModes field in McpUiAppCapabilities that allows
apps to declare which display modes they support. This enables hosts to
compute the intersection of host and app capabilities and only offer
appropriate display mode controls.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Address PR review feedback

- Change "Guest UI" to "View" terminology
- Add "View" prefix to availableDisplayModes heading for disambiguation
- Remove intersection logic - host only checks its own supported modes
- Remove assumption that apps must support inline mode
- Simplify host behavior to honor/reject based on its own capabilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Add appCapabilities.availableDisplayModes to types

- Add `availableDisplayModes` field to `McpUiAppCapabilities` so apps can
  declare which display modes they support
- Change `HostContext.availableDisplayModes` type from `string[]` to
  `McpUiDisplayMode[]` for type safety
- Regenerate schemas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Address Olivier's feedback - consolidate display mode docs

- Remove separate "App Capabilities" section for availableDisplayModes
- Add display mode requirements to existing ui/request-display-mode section:
  - Host MUST NOT switch View to mode not in appCapabilities.availableDisplayModes
  - Host MAY decline requests from Views that didn't declare modes
  - View MUST declare supported modes during initialization

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Restore McpUiAppCapabilities docs and fix display mode clause

- Restore McpUiAppCapabilities interface documentation with availableDisplayModes
- Restore "View MUST check if the requested mode is in availableDisplayModes
  from host context" clause that was accidentally removed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Create unified Display Modes section

Consolidate all display mode documentation into a single section:
- Display mode types (inline, fullscreen, pip)
- Declaring support from View (appCapabilities.availableDisplayModes)
- Declaring support from Host (HostContext.availableDisplayModes)
- Requesting changes (ui/request-display-mode)
- Notifying changes (ui/notifications/host-context-changed)
- All MUST/SHOULD requirements for View and Host behavior

The ui/request-display-mode in Requests section now references
the Display Modes section for behavior details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Remove mode coercion clause from display modes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* Regenerate schemas with correct version

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: Claude <[email protected]>

* feat(say-server): add light/dark theme support (#338)

- Add importmap entry for main SDK exports (applyDocumentTheme, etc.)
- Import and apply host theme and styles on context changes
- Apply initial host context theme/styles when app connects
- Update CSS to use [data-theme="dark"] selector for host-provided themes
- Keep @media prefers-color-scheme as fallback for standalone mode
- Add color-scheme meta tag for native element theming

* fix: followup app -> view terminology (#339)

* chore: husky env fix, version pins, update-lock:docker script (#341)

* fix(husky): load Node.js env for GUI apps + add update-lock:docker script

Cherry-picked non-e2e changes from ochafik/fix-e2e-flaky-tests:
- Load nvm/fnm/volta/homebrew paths in pre-commit for GitHub Desktop
- Add update-lock:docker script to regenerate lockfile with public registry

* pin package versions for dev compatibility

- solid-js: 1.9.10
- caniuse-lite: 1.0.30001763
- cheerio: 1.1.2
- electron-to-chromium: 1.5.267
- @playwright/test: 1.57.0
- playwright: 1.57.0
- playwright-core: 1.57.0

* regen

* fix: pin @types/node to existing version and regenerate lockfile

- Fix @types/node 20.19.28 -> 20.19.27 (typo, version didn't exist)
- Regenerate package-lock.json to resolve versions available on both
  public npm and Anthropic artifactory registries

* fix(basic-server-solid): bump solid-js 1.9.10 -> 1.9.11 to fix seroval vulnerabilities

* fix: pin seroval to 1.4.1 for dev compatibility

* Refactor server startup functions

Rename `startServer` to `startStreamableHTTPServer` and add a new
`startStdioServer` helper to make the transport type explicit. Remove
the `ServerOptions` interface in favor of reading `PORT` directly from
the environment inside the function (defaulting to `3001`).

Also update Python examples to default to port `3001` for consistency.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Support full-file inclusion in synced code snippets

Previously, the script only supported extracting regions from `.ts/.tsx`
files using the `#regionName` syntax. Now it also supports including
entire files without a region specifier, enabling sync of any file type
(JSON, YAML, shell scripts, etc.) into documentation.

Changes:
- Make `#regionName` optional in the `source=""` attribute
- Accept any fence language (not just `ts`/`tsx`)
- Simplify cache to flat map with composite keys
- Add validation error for region extraction on non-TypeScript files

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Rewrite Quickstart guide with type-checked code examples

Rewrites the Quickstart tutorial to provide a more focused, step-by-step
introduction to building MCP Apps. The guide now uses synced code fences
that pull from `examples/quickstart/`, ensuring all code shown in the
documentation is type-checked and tested.

Key improvements:
- Clearer project setup instructions with explicit commands
- Streamlined explanations of the tool + resource registration pattern
- Added screenshot showing the completed app
- E2E test coverage validates the example works end-to-end

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Add MCP Apps Overview document

Adds `docs/overview.md` covering the core concepts: architecture,
lifecycle, UI resources, bidirectional communication, theming, display
modes, and security. Includes mermaid flowchart and sequence diagrams.

Also adds the `@boneskull/typedoc-plugin-mermaid` TypeDoc plugin for
mermaid diagram support. However, the plugin handles HTML entities from
TypeDoc incorrectly, so `scripts/typedoc-plugin-fix-mermaid-entities.mjs`
decodes HTML entities before the plugin processes them.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Add Agent Skills installation guide

Add `docs/agent-skills.md` with installation instructions for multiple AI
coding agents including Claude Code, VS Code/Copilot, Gemini CLI, Cline,
and Goose. The guide provides three installation methods: Claude Code
plugin, Vercel Skills CLI (`npx skills add`), and manual installation.

Update `README.md` to link to the new guide and reframe the plugin
section around the broader "Agent Skills" concept.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Polish OpenAI migration reference for readability

Add framing prose to the migration guide to improve navigation:
- Expanded intro describing document purpose and how to use it
- Added note about features marked "Not yet implemented"
- Added brief transition sentences before Server-Side and Client-Side sections

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Fix mermaid diagram arrowheads not rendering

The `@boneskull/typedoc-plugin-mermaid` creates both dark and light theme
variants of each diagram, each with identical marker IDs (e.g., `#arrowhead`).
When mermaid renders SVGs at runtime, lines reference `url(#arrowhead)`, but
the browser resolves this to the first match in document order — the hidden
dark-theme SVG — causing arrows to be invisible.

The fix removes dark-theme `<div class="mermaid dark">` elements at build time,
leaving only one SVG per diagram with unique marker IDs. A CSS filter
(`filter: invert(1) hue-rotate(180deg)`) handles dark mode styling instead.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(docs): improve OpenAI migration guide accuracy (#349)

- Add missing setOpenInAppUrl API to 'Not Yet in MCP Apps' section
- Clarify Event Handling table: specify exact OpenAI properties
  (toolInput/toolOutput) instead of vague 'window.openai.*'
- Improve notes to distinguish sync property reads vs callbacks

* Add Testing MCP Apps guide

Document how to test MCP Apps using `basic-host` and MCP Apps-compatible
hosts like Claude.ai and VS Code Copilot. Cover debugging features for
`basic-host`, and using `cloudflared` to expose local servers for remote
hosts.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Fix `onhostcontextchanged` handler invocation in examples

`App.onhostcontextchanged` is a setter-only property with no getter.
TypeScript doesn't prevent reading from setter-only properties, so
`app.onhostcontextchanged?.(ctx)` compiles but silently returns
`undefined` at runtime.

Extract the handler into a named `handleHostContextChanged()` function
and call it directly after `connect()`, matching the pattern used in
`basic-server-vanillajs` and other examples.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Expand Agent Skills guide

Add intro explaining what Agent Skills are and link to the two skill
files (`create-mcp-app`, `migrate-oai-app`). Reorganize headings and add
"Invoke a Skill" and "Test Your App" sections. Update `README.md` link
text and add tip to quickstart suggesting agent skills as an alternative.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(e2e): improve test stability and developer experience (#337)

* fix(e2e): add masks for dynamic content and increase timeouts for slow servers

- Add HOST_MASKS for Tool Input/Result panels with dynamic timestamps
- Add say-server to SLOW_SERVERS (10s) for TTS model download
- Add say-server to EXTRA_WAIT_MS (30s) in generate-grid-screenshots

* feat: add update-lock:docker script to regenerate lockfile with public registry

* pin package versions for dev compatibility

* feat(basic-host): add ?theme=hide query param to hide theme toggle in screenshots

- Add hideThemeToggle param to getQueryParams()
- Conditionally render ThemeToggle based on ?theme=hide
- Update e2e tests to use ?theme=hide for consistent screenshots

* update screenshots

* update screenshots

* fix(husky): load Node.js environment for GUI apps like GitHub Desktop

* fix(e2e): increase map-server wait time to 15s for tile loading

* fix(e2e): increase waitForAppLoad timeout to 30s for nested iframe loading

* ci: add workflow to update e2e snapshots

* ci: trigger snapshot update on push to this branch

* chore: update e2e snapshots [skip ci]

* ci: trigger CI after snapshot update

* style: fix prettier formatting in update-snapshots workflow

* fix: pin seroval, seroval-plugins, and solid-js for dev compatibility

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Improve README structure and clarity

Add "Why MCP Apps?" section to explain the motivation upfront. Reorganize
the "How It Works" section to lead with tool definition. Rename "Overview"
to "Using the SDK" and add "For" prefix to audience headings.

Restructure the Examples section with clearer subsections for running
examples with `basic-host` vs MCP clients using stdio transport. Move the
`qr` server note outside the collapsed details block.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* Add Codex and Goose to documentation

Add Codex to the list of agents supporting skills in `agent-skills.md`.
Add Goose to the list of supported clients in `testing-mcp-apps.md` and
rename "VS Code Copilot" to "VS Code (Insiders)" for accuracy.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: regenerate lockfile with public registry, use ^0.4.2 for ext-apps deps

* fix: update quickstart example to use workspace reference

* chore: remove internal registry references from lockfile

* feat(ci): add /update-snapshots comment trigger for PRs

- Comment '/update-snapshots' on any PR to update E2E snapshots
- Adds rocket reaction to acknowledge the command
- Comments back with result (updated or no changes needed)
- Removes hardcoded branch trigger
- Keeps workflow_dispatch for manual runs

* docs: document /update-snapshots PR comment trigger

* fix: pin @hono/node-server to 1.19.7 for dev compatibility

SDK 1.25.3 requires @hono/node-server@^1.19.9 which doesn't exist
on the public npm registry (latest is 1.19.7).

* fix: pin @modelcontextprotocol/sdk to 1.25.2

SDK 1.25.3 has broken dependency on @hono/node-server@^1.19.9
which doesn't exist on public npm registry.

* fix: restore flexible SDK peerDep range (^1.24.0)

devDependencies stays pinned to 1.25.2 for our builds,
but peerDependencies should allow users flexibility.

* fix: pin @types/node to avoid unpublished versions

- Pin quickstart @types/node to 22.19.5 (latest public 22.x)
- Add @types/node override to root package.json
- Regenerate lockfile with public registry only

---------

Co-authored-by: Tobin South <[email protected]>
Co-authored-by: Liad Yosef <[email protected]>
Co-authored-by: Claude <[email protected]>
Co-authored-by: martinalong <[email protected]>
Co-authored-by: Ido Salomon <[email protected]>
Co-authored-by: Jonathan Hefner <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants