feat: add mcp app renderer
Summary
Implements the front-end foundation for MCP Apps in Goose Desktop, following the MCP Apps specification. This PR sets up the rendering infrastructure, communication bridge, and mock handlers—preparing for a subsequent PR that will connect to actual MCP server resources.
Related: https://github.com/block/goose/discussions/6069
What This PR Adds
New Components (ui/desktop/src/components/McpApps/)
McpAppRenderer.tsx— Renders MCP App UI resources in sandboxed iframes with security boundariesuseSandboxBridge.ts— React hook managing the postMessage-based JSON-RPC 2.0 communication between host (Goose) and guest (iframe), including:- Sandbox initialization lifecycle (
sandbox-ready→initialize→initialized) - Host context propagation (theme, viewport, locale, timezone, platform)
- Tool lifecycle notifications (
tool-input,tool-input-partial,tool-result,tool-cancelled) - Resource teardown on unmount
- Sandbox initialization lifecycle (
types.ts— TypeScript types for JSON-RPC messages, MCP App resources (ui://scheme), host context, and all guest message typesutils.ts— Message creation helpers and stubbed handlers for MCP protocol methodsmockAppData.ts— Demo MCP App HTML with interactive buttons to test all protocol methods
Spec Alignment
- Uses
ui://URI scheme for UI resources - Implements
text/html;profile=mcp-appMIME type - Supports CSP metadata (
connectDomains,resourceDomains,prefersBorder) - Full initialization handshake per spec
- Host context with theme, viewport, locale, platform, device capabilities
- CSP enforcement on the outer sandbox proxy
Current State
The McpAppRenderer integration in ToolCallWithResponse.tsx is commented out because Goose needs to dynamically fetch UI resources via resources/read when a tool's _meta.ui.resourceUri is present. The mock data demonstrates the expected flow.
Handlers Status
| Method | Status | Action Required |
|---|---|---|
ui/open-link |
✅ Implemented | — |
ui/message |
✅ Implemented | — |
ui/notifications/size-changed |
✅ Implemented | — |
tools/call |
🔜 Stubbed | Forward to MCP server; enforce "app" visibility |
resources/list |
🔜 Stubbed | Return available resources from MCP server |
resources/read |
🔜 Stubbed | Fetch resource content from MCP server |
resources/templates/list |
🔜 Stubbed | Return resource templates from MCP server |
prompts/list |
🔜 Stubbed | Return available prompts from MCP server |
notifications/message |
🔜 Stubbed | Forward log message to MCP server |
ping |
🔜 Stubbed | Forward to MCP server and return response |
Future: @mcp-ui/client SDK
The useSandboxBridge hook is a temporary implementation. Once the @mcp-ui/client package provides its AppRenderer component, we can refactor to pass props directly to that component—which will internalize the sandbox communication logic. The handler functions in utils.ts are designed to be reusable and can be plugged directly into the SDK component's callback props.
Next Steps (Subsequent PRs)
- Resource fetching — Wire up
resources/readto fetch UI resources when_meta.ui.resourceUriis present on tool results - Handler integration — Connect stubbed handlers to forward requests to the appropriate MCP server
- Tool visibility — Implement filtering for
["model"]vs["app"]visibility - Host theming — Provide
hostContext.styles.variableswith standardized CSS custom properties (colors, typography, border radius) so MCP Apps can match Goose's visual style - Custom fonts — Provide
hostContext.styles.css.fontswith@font-facerules for Goose's fonts so MCP Apps can use them