10 KiB
MCP Apps Implementation Notes for Goose
Date Started: 2025-12-05
Specification: SEP-1865: MCP Apps
Objective: Implement MCP Apps support in Goose (Desktop + CLI)
Specification Summary
- Extension ID:
io.modelcontextprotocol/ui - URI Scheme:
ui:// - Content Type (MVP):
text/html;profile=mcp-app - Communication: JSON-RPC 2.0 over
postMessage(iframe ↔ host)
Reading Notes & Ideas
Key Insight: iframes are MCP Clients
The iframe isn't just a dumb renderer - it's actually an MCP client that can:
- Call
tools/callto execute tools on the server - Call
resources/readto fetch resources - Send
ui/messageto inject messages into the conversation - Do the whole
ui/initialize→ui/notifications/initializedhandshake
Architecture:
MCP Server <---> Goose (Host) <---> iframe (MCP Client)
^
|
postMessage bridge
Goose becomes a broker between the iframe and the MCP server:
- Listen for
postMessagefrom the iframe - Validate the JSON-RPC messages
- Proxy allowed requests (
tools/call,resources/read, etc.) to the MCP server - Send responses back to the iframe
This is where the security model really matters - Goose is the gatekeeper deciding what the iframe is allowed to do.
Deprecation Strategy (MCP-UI → MCP Apps)
- Detection approach: Currently we check if URI starts with
ui://prefix. We can also look for the new MIME typetext/html;profile=mcp-appto distinguish between old MCP-UI and new MCP Apps. - Code path fork: Use MIME type detection to load a totally different renderer for MCP Apps, leaving existing MCP-UI code in place during transition.
- Warning banner update: Update the existing "experimental" warning bar for MCP-UI to include:
- Clearer messaging that MCP-UI is being deprecated
- A target date for when MCP-UI support will be removed
- Encourage extension authors to migrate to the new MCP Apps spec
Open Questions
-
Does the Rust MCP SDK handle
resources/readrequests? (Need to verify before implementing UI resource fetching) -
Does the Rust MCP SDK's resource response include all the fields we need?
uri✅mimeType✅ (asmime_type: Option<String>)textorblob✅ (enum variantsTextResourceContents/BlobResourceContents)_meta✅ (asmeta: Option<Meta>where Meta wraps JsonObject)ui.csp.connectDomains- Access viameta.0.get("ui")...ui.csp.resourceDomains- Access viameta.0.get("ui")...ui.domain- Access viameta.0.get("ui")...ui.prefersBorder- Access viameta.0.get("ui")...
- CONFIRMED: All fields available in rmcp 0.9.1
-
Does Goose need to validate that the
textorblobcontent is a valid HTML5 document? Or just trust the MIME type and render it? -
Does Goose need to prefetch and cache UI resources for performance optimization? (Spec mentions hosts MAY do this)
- Follow-up idea: Prefetching could enable security review at connection time - scan HTML for suspicious patterns and surface warnings in the Goose UI before the tool is ever invoked. Pursue this as a fast-follow after MVP is working.
-
RPC methods: There are a lot of JSON-RPC methods to implement (lifecycle, notifications, requests). Will there be a client library that handles this, or do we need to build it ourselves? Look into:
ui/initialize,ui/notifications/initializedui/notifications/tool-input,ui/notifications/tool-input-partial,ui/notifications/tool-resultui/tool-cancelledui/resize,ui/messagetools/call,resources/read,ping- Check if MCP-UI SDK or a new official SDK will provide helpers for this
-
Hiding redundant text when UI is shown: The spec separates
content(text for model context / text-only hosts) fromstructuredContent(for UI rendering). But it doesn't explicitly say hosts SHOULD hide the textcontentwhen rendering a UI. Should Goose suppress the text output when displaying an MCP App to avoid redundancy? Worth raising for clarification in the spec. -
Private tools: The spec mentions something about private tools - worth noting as a concept to be aware of.
Implementation Considerations
Desktop (Electron)
-
Sandbox proxy: The spec has a pretty exhaustive checklist for sandbox proxy requirements (different origins, permissions, message forwarding, CSP enforcement, etc.). Should be relatively straightforward to ensure we're complying with everything - just follow the checklist.
- Must implement:
- Sandbox sends
ui/notifications/sandbox-readywhen ready - Host sends raw HTML via
ui/notifications/sandbox-resource-readyonce sandbox is ready
- Sandbox sends
- Must implement:
-
HostContext interface: We've started sending
themeandplatformto the UI. Need to expand this to the fullHostContextinterface from the spec:toolInfo,theme,displayMode,availableDisplayModes,viewport,locale,timeZone,userAgent,platform,deviceCapabilities,safeAreaInsets- Confusion about
platform: The spec comment says it's for "responsive design" but the options (web,desktop,mobile) seem more about the host environment (browser vs Electron vs mobile app), not screen size. Need clarity.- Action: Check existing issues/discussions on the ext-apps repo to see if this has been raised. If not, open an issue to clarify the intent of
platform.
- Action: Check existing issues/discussions on the ext-apps repo to see if this has been raised. If not, open an issue to clarify the intent of
-
hostInfo: This is where we identify ourselves. Set
name: "Goose Desktop"and include the version number. -
prefersBorderhandling: When a UI resource hasprefersBorder: true, we can respond by:- Removing all padding from the current container
- Possibly removing the background element entirely
- Let the iframe content define its own visual boundaries
-
CSP enforcement: Map the CSP settings from the server's resource
_meta.ui.cspto the HTML proxy's<head>(via<meta http-equiv="Content-Security-Policy">). Need to construct the CSP string fromconnectDomainsandresourceDomainsarrays.- Default to restrictive (block everything not explicitly declared)
- MUST NOT allow undeclared domains
- Question: Spec mentions "audit trail" - should log CSP configs for review. Does this just mean writing to goose logs? Or something more formal?
-
Future idea: "Stats for Nerds" for MCP Apps - A playful way to let users inspect/hover over an MCP App to see resource details like:
- Resource URI
- Declared
connectDomainsandresourceDomains - Extension name / source
- Could be a little info icon or expandable panel - make it fun and on-brand for Goose
CLI
Core (goose crate)
-
Capability negotiation: Goose must advertise MCP Apps support in its
initializerequest to MCP servers:{ "capabilities": { "extensions": { "io.modelcontextprotocol/ui": { "mimeTypes": ["text/html;profile=mcp-app"] } } } }- This tells servers that Goose can render UI resources
- Servers that don't see this capability should fall back to text-only responses
-
Tool → Resource linking: When a tool is initialized and has
_meta["ui/resourceUri"], we need to:- Detect that metadata field on the tool
- Use the Rust MCP SDK to call
resources/readwith that URI - Fetch the UI resource content (HTML + metadata) to pass to the renderer
- This needs to happen at tool initialization time, not tool execution time (for prefetching/caching)
goose-server
Server-Side Notes (Axon / Agentic Commerce)
-
Capability-based response routing: Our server will know if a host (like Goose) supports MCP Apps via the
extensions["io.modelcontextprotocol/ui"]capability in the initialize request. -
Dual-mode support: This gives us a way to maintain two strategies side-by-side:
- OpenAI ChatGPT SDK + MCP-UI - Current approach for hosts that don't support the new spec
- MCP Apps (SEP-1865) - New approach for hosts that advertise
text/html;profile=mcp-appsupport
-
What changes per mode:
- Resource registration strategy
- Tool result embedding (how we return
contentvsstructuredContent) - Possibly different UI resource URIs or templates
-
Implementation: Set up conditional logic at initialization time to route to the appropriate resource registration strategy based on detected host capabilities. Don't know exactly how to do this yet, but that's the goal.
-
Simpler alternative: Maybe we can just drop ChatGPT SDK support and drop MCP-UI entirely. Follow the spec's recommendation:
- Check if host has
io.modelcontextprotocol/uicapability - If yes → register the UI-enabled tools (with
ui/resourceUrimetadata) and resources - If no → don't register those tools/resources at all, fall back to text-only tools
- This is cleaner than maintaining dual-mode logic. Just one path forward.
- Check if host has
Dependencies & Related Work
- Goose is already listed as an MCP-UI adopter in the spec
- Reference: MCP-UI project (mcpui.dev)
- Prerequisite: SEP-1724 - Goose must implement client-server capability negotiation (extensions capability mechanism) before MCP Apps can work. Need to verify if this is already shipped or needs to be added.
- Finding: The
rmcpcrate (even v0.10.0) does NOT have anextensionsfield onClientCapabilities. It only has:experimental,roots,sampling,elicitation. - Options:
- Use
experimentalfield as a workaround (it's aBTreeMap<String, JsonObject>) - Contribute the
extensionsfield to rmcp upstream - Fork/patch rmcp locally
- Use
- Need to decide which approach to take
- Finding: The
Action Items
- Review existing MCP implementation in Goose
- Identify where UI resource handling would fit
- Design iframe sandbox approach for Electron
- Plan capability negotiation implementation
- ...