Skip to content

feat: add mcp app renderer#6095

Merged
DOsinga merged 36 commits intomainfrom
feat/add-mcp-app-renderer
Dec 22, 2025
Merged

feat: add mcp app renderer#6095
DOsinga merged 36 commits intomainfrom
feat/add-mcp-app-renderer

Conversation

@aharvard
Copy link
Collaborator

@aharvard aharvard commented Dec 12, 2025

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: #6069

What This PR Adds

New Components (ui/desktop/src/components/McpApps/)

  • McpAppRenderer.tsx — Renders MCP App UI resources in sandboxed iframes with security boundaries
  • useSandboxBridge.ts — React hook managing the postMessage-based JSON-RPC 2.0 communication between host (Goose) and guest (iframe), including:
    • Sandbox initialization lifecycle (sandbox-readyinitializeinitialized)
    • Host context propagation (theme, viewport, locale, timezone, platform)
    • Tool lifecycle notifications (tool-input, tool-input-partial, tool-result, tool-cancelled)
    • Resource teardown on unmount
  • types.ts — TypeScript types for JSON-RPC messages, MCP App resources (ui:// scheme), host context, and all guest message types
  • utils.ts — Message creation helpers and stubbed handlers for MCP protocol methods
  • mockAppData.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-app MIME 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)

  1. Resource fetching — Wire up resources/read to fetch UI resources when _meta.ui.resourceUri is present on tool results
  2. Handler integration — Connect stubbed handlers to forward requests to the appropriate MCP server
  3. Tool visibility — Implement filtering for ["model"] vs ["app"] visibility
  4. Host theming — Provide hostContext.styles.variables with standardized CSS custom properties (colors, typography, border radius) so MCP Apps can match Goose's visual style
  5. Custom fonts — Provide hostContext.styles.css.fonts with @font-face rules for Goose's fonts so MCP Apps can use them

- Enhanced `ToolCallWithResponse` to transform tool results and pass tool input to `McpAppRenderer`.
- Updated `McpAppRenderer` to accept and handle tool input and result props.
- Added utility functions to create notifications for tool input and result.
- Modified mock app data to display tool input and result in the UI.
- Updated documentation to reflect the implemented notifications.
@aharvard aharvard changed the title add mcp app renderer feat: add mcp app renderer Dec 15, 2025
Copy link
Collaborator

@DOsinga DOsinga left a comment

Choose a reason for hiding this comment

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

I wanted to finish this, but I'm fried, let's sync tomorrow?

if request.uri().path() == "/status"
|| request.uri().path() == "/mcp-ui-proxy"
|| request.uri().path() == "/mcp-app-proxy"
{
Copy link
Collaborator

Choose a reason for hiding this comment

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

is three the time we introduce an allow list?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

/mcp-ui-proxy will eventually go away, and I don't have strong opinions here. Maybe if we start opening up more routes?

@@ -0,0 +1,53 @@
use axum::{
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is very similar to mcp_ui_proxy.rs

I would suggest to just change the route to

/proxy/mcp_app and /proxy/mcp_ui (or /mcp-proxy/ui etc)

and then just serve the relvant HMTL based on the parameter

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Whoops... Yesterday, I dug pretty deep into the iframe approach and realized I messed some things up. I think I got it right now. I just pushed this new commit. Sorry for the churn.

Before: The outer sandbox had a static, permissive CSP that allowed any HTTPS domain, relying on JavaScript to build and inject a restrictive CSP into the inner iframe.

After: The outer sandbox CSP is now dynamically built in Rust based on the app's declared domains, making it the single source of truth — the inner iframe needs no CSP since it can't exceed the outer's restrictions.

Based on these changes, mcp_app_proxy is now unique from mcp_ui_proxy — and implemented closer to spec.

Do we consolidate code into a single route that handles both MCP-UI and MCP Apps (with CSP construction) HTML, or keep them as separate files? Thinking ahead to the MCP-UI deprecation, can we keep them separate?

console.error('Error fetching MCP App Proxy URL:', error);
return null;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

we should find a way to do this through the API instead of hand composing this, adding the secret, checking the secret by hand. for one thing we could extend the auto checker of the secret to also look in, say __secret

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do you think we should address that as a fast follow-up or incorporate it into this PR?

…erer

* origin/main: (26 commits)
  Don't persist ephemeral extensions when resuming sessions (#5974)
  chore(deps): bump mdast-util-to-hast from 13.2.0 to 13.2.1 in /ui/desktop (#5939)
  chore(deps): bump node-forge from 1.3.1 to 1.3.2 in /documentation (#5898)
  Add Scorecard supply-chain security workflow (#5810)
  Don't show subagent tool when we're a subagent (#6125)
  Fix keyboard shortcut conflict for Focus Goose Window (#5809)
  feat(goose-cli): add feature to disable update (#5886)
  workflow: enable docs-update-recipe-ref (#6132)
  fix: filter tools in Ollama streaming when chat mode is enabled (#6118)
  feat(mcp): platform extension for "code mode" MCP tool calling (#6030)
  workflow: auto-update recipe-reference on release (#5988)
  Document recipe slash commands feature (#6075)
  docs: add GitHub Copilot device flow authentication details (#6123)
  Disallow subagents with no extensions (#5825)
  chore(deps): bump js-yaml in /documentation (#6093)
  feat: external goosed server (#5978)
  fix: Make datetime info message more explicit to prevent LLM confusion about current year (#6101)
  refactor: unify subagent and subrecipe tools into single tool (#5893)
  goose repo is too big for the issue solver workflow worker (#6099)
  fix: use system not developer role in db (#6098)
  ...
- Create ThemeContext with single source of truth for theme state
- userThemePreference: what the user chose ('light' | 'dark' | 'system')
- resolvedTheme: the actual theme to apply ('light' | 'dark')
- Update ThemeSelector to use useTheme() hook
- Update App.tsx to wrap with ThemeProvider
- Update MCPUIResourceRenderer to use resolvedTheme (fixes bug where system theme was ignored)
- Remove duplicated theme handling logic from App.tsx

This eliminates brittle localStorage watching and provides type-safe,
reactive theme updates via React context.
- Use resolved theme value in broadcastThemeChange instead of empty string
- Add validation for IPC payload structure before processing theme-changed events
…nderer

* feat/centralize-theme-context:
  refactor: simplify IPC handler with typed payload
  refactor: consolidate theme application to single useEffect
  refactor: simplify applyThemeToDocument with ternary
  fix: address PR feedback
  feat: centralize theme management with ThemeContext
  Fix tokenState loading on new sessions (#6129)
  bump bedrock dep versions (#6090)
- Replace getCurrentTheme() with useTheme() hook from ThemeContext
- Remove brittle localStorage/StorageEvent watching (~30 lines)
- Theme changes now propagate reactively via React context
- Remove unused getCurrentTheme() function from utils.ts
@aharvard aharvard mentioned this pull request Dec 16, 2025
* main:
  fix: we don't need to warn about tool count when in code mode (#6149)
  deps: upgrade agent-client-protocol to 0.9.0 (#6109)
  fix(providers): fix for gemini-cli on windows to work around cmd's multiline prompt limitations #5911 (#5966)
  More slash commands (#5858)
  fix: MCP UI not rendering due to CallToolResult structure change (#6143)
  fix: display shell output as static text instead of spinner (#6041)
  fix : Custom providers with empty API keys show as configured in desktop (#6105)
  Add .agents/skills and ~/.config/agent/skills to skills discovery paths (#6139)
  fix: use instructions for system prompt and prompt for user message in subagents (#6121)
  Fix compaction loop for small models or large input (#5803)
  feat: Centralize theme management with ThemeContext (#6137)
  OpenRouter & Xai streaming (#5873)
  fix: resolve mcp-hermit cleanup path expansion issue (#5953)
  feat: add goose PR reviewer workflow (#6124)
  perf: Avoid repeated MCP queries during streaming responses (#6138)
  Fix YAML serialization for recipes with special characters (#5796)
  Add more posthog analytics (privacy aware) (#6122)
  docs: add Sugar MCP server to extensions registry (#6077)
…ractions. Removed unused appendMessage prop, added new message handlers for tools, resources, and notifications, and updated mock app data for improved testing. Adjusted types for better clarity and added logging for debugging purposes.
… handling. Updated McpAppRenderer to accept an append prop, modified handleMessage to utilize it, and improved mock app data for better logging and testing. Adjusted response handling in createSuccessResponse and createErrorResponse for optional IDs.
…tion headings for better context, restructured terminal sections to reflect host and tool data, and enhanced message handling for tool notifications. Adjusted initialization and rendering functions to accommodate new data structures.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements the front-end foundation for MCP Apps in Goose Desktop, following the MCP Apps specification. It establishes a sandboxed iframe rendering system with JSON-RPC 2.0 communication between the host (Goose Desktop) and guest (MCP App), supporting the full initialization lifecycle, host context propagation, and tool notifications. The implementation includes TypeScript types, React hooks, utility functions, and a Rust backend proxy endpoint with CSP enforcement. Handler functions for MCP protocol methods are stubbed for future integration with actual MCP servers.

Key changes:

  • New McpAppRenderer component with sandboxed iframe rendering and security boundaries
  • useSandboxBridge React hook managing postMessage-based JSON-RPC communication
  • TypeScript types for JSON-RPC messages, MCP App resources, host context, and protocol messages
  • Rust backend proxy endpoint with dynamic CSP construction based on MCP App resource metadata
  • Mock data and handlers demonstrating expected protocol flow (handlers currently stubbed)

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
ui/desktop/src/components/McpApps/types.ts TypeScript type definitions for JSON-RPC messages, MCP App resources, host context, and all protocol message types
ui/desktop/src/components/McpApps/utils.ts JSON-RPC helper functions, message creators, and stubbed handler implementations for MCP protocol methods
ui/desktop/src/components/McpApps/useSandboxBridge.ts React hook managing sandbox lifecycle, postMessage bridge, host context updates, and message routing
ui/desktop/src/components/McpApps/McpAppRenderer.tsx Component rendering MCP App UI resources in sandboxed iframes with security boundaries
ui/desktop/src/components/McpApps/mockAppData.ts Demo MCP App HTML with interactive UI testing all protocol methods
ui/desktop/src/components/ToolCallWithResponse.tsx Commented-out integration point for future MCP App rendering (awaiting resource fetching)
ui/desktop/src/components/BaseChat.tsx Passes append callback to message list for MCP App message handling
crates/goose-server/src/routes/mcp_app_proxy.rs Rust endpoint serving sandbox proxy HTML with dynamically constructed CSP based on resource metadata
crates/goose-server/src/routes/templates/mcp_app_proxy.html Outer sandbox proxy HTML template that creates inner guest iframe and relays JSON-RPC messages
crates/goose-server/src/routes/mod.rs Registers mcp_app_proxy route module and passes secret key to both proxy endpoints
crates/goose-server/src/auth.rs Adds mcp-app-proxy to auth bypass list (handles own authentication via query param)
ui/desktop/eslint.config.js Adds HTMLIFrameElement, MessageEvent, and StorageEvent as readonly globals

…sponse functions to return null directly instead of casting. This improves type safety and clarity in the code.
…type instead of a generic Record for improved type safety and clarity.
Copilot AI review requested due to automatic review settings December 17, 2025 21:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Copilot AI review requested due to automatic review settings December 17, 2025 21:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Copy link
Collaborator

@DOsinga DOsinga left a comment

Choose a reason for hiding this comment

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

I'll take this over

_meta?: Record<string, unknown>;
content: unknown[];
isError?: boolean;
structuredContent?: Record<string, unknown>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

no. these types need to come from the server, no saying you copied it

setIframeHeight(newHeight);
return null;
};
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I still don't think this file is adding much. these are simple types, creating constructors for them like this just loses type information

Co-authored-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 22, 2025 21:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 11 comments.

Comment on lines +127 to +131
.postMessage({
jsonrpc: '2.0',
method: 'ui/notifications/sandbox-ready',
params: {}
}, '*');
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The postMessage wildcards in both the proxy template and the mock app HTML use '*' for targetOrigin throughout. This is a security vulnerability that allows any malicious frame to intercept or inject messages into the communication channel. All postMessage calls should specify the expected origin.

Copilot uses AI. Check for mistakes.
if (isGuestInitializedRef.current) {
sendToSandbox({
jsonrpc: '2.0',
id: Date.now(),
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Using Date.now() as a request ID in cleanup can collide with other concurrent requests. If multiple components unmount simultaneously or if the cleanup happens at the same millisecond as another request, the IDs will conflict. Consider using a more robust ID generation strategy like a UUID or incrementing counter.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +47
// allow-same-origin: needed for localStorage, cookies, etc.
// allow-forms: needed if the app has forms
guestIframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The same unsafe sandbox combination allow-scripts allow-same-origin allow-forms exists in the proxy template. This creates two layers of potentially unsafe sandboxing where each could be exploited to escape the sandbox restrictions.

Suggested change
// allow-same-origin: needed for localStorage, cookies, etc.
// allow-forms: needed if the app has forms
guestIframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
// allow-forms: needed if the app has forms
// Note: we intentionally do NOT grant allow-same-origin to keep the iframe strongly isolated.
guestIframe.setAttribute('sandbox', 'allow-scripts allow-forms');

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +64
/// Parse comma-separated domains, filtering out empty strings
fn parse_domains(domains: Option<&String>) -> Vec<String> {
domains
.map(|d| {
d.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The domain strings from user input are directly interpolated into the CSP header without validation or sanitization. Malicious domains containing CSP syntax (like semicolons, quotes, or other directives) could break out of the intended CSP directive and inject their own policies. The domains should be validated to ensure they only contain valid URL characters and don't include CSP metacharacters.

Suggested change
/// Parse comma-separated domains, filtering out empty strings
fn parse_domains(domains: Option<&String>) -> Vec<String> {
domains
.map(|d| {
d.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
/// Validate that a single CSP domain token contains only safe characters.
///
/// This is intentionally conservative: it allows common URL/host punctuation but
/// rejects whitespace, quotes, semicolons, and other characters that could break
/// out of a CSP directive.
fn is_valid_csp_domain_token(token: &str) -> bool {
if token.is_empty() {
return false;
}
token.chars().all(|c| {
c.is_ascii_alphanumeric()
|| matches!(
c,
'-' | '_' | '.' | ':' | '/' | '*' | '?' | '&' | '=' | '%' | '#' | '+' | '@' | '[' | ']'
)
})
}
/// Parse comma-separated domains, filtering out empty strings and invalid tokens.
fn parse_domains(domains: Option<&String>) -> Vec<String> {
domains
.map(|d| {
d.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.filter(|s| is_valid_csp_domain_token(s))
.map(|s| s.to_string())

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +77
if (method === 'ui/notifications/sandbox-resource-ready') {
var params = data.params || {};
var html = params.html || '';

createGuestIframe(html);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The HTML content from params.html is inserted directly into an iframe without any sanitization. While CSP provides some protection, a malicious server could inject arbitrary HTML/JavaScript that executes within the sandbox context. Consider adding HTML sanitization or at minimum validating that the HTML doesn't contain dangerous patterns before creating the iframe.

Copilot uses AI. Check for mistakes.
Comment on lines +260 to +271
useEffect(() => {
return () => {
if (isGuestInitializedRef.current) {
sendToSandbox({
jsonrpc: '2.0',
id: Date.now(),
method: 'ui/resource-teardown',
params: { reason: 'Component unmounting' },
});
}
};
}, [sendToSandbox]);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The cleanup effect attempts to send a message to an iframe that may already be destroyed. When a component unmounts, the iframe is removed from the DOM, but the cleanup function still tries to call sendToSandbox which accesses iframeRef.current?.contentWindow?.postMessage. This will silently fail since the contentWindow is null, but the postMessage call will be made to a destroyed context.

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +65
const sendToSandbox = useCallback((message: JsonRpcMessage) => {
iframeRef.current?.contentWindow?.postMessage(message, '*');
}, []);

Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The postMessage calls use wildcard '*' as targetOrigin, which means messages can be sent to any origin. This creates a security risk where malicious iframes could intercept these messages. The targetOrigin should be restricted to the expected proxy URL origin.

Suggested change
const sendToSandbox = useCallback((message: JsonRpcMessage) => {
iframeRef.current?.contentWindow?.postMessage(message, '*');
}, []);
const sendToSandbox = useCallback(
(message: JsonRpcMessage) => {
if (!proxyUrl) {
return;
}
let origin: string;
try {
origin = new URL(proxyUrl).origin;
} catch {
// If proxyUrl is not a valid URL, do not attempt to send the message.
return;
}
const targetWindow = iframeRef.current?.contentWindow;
if (!targetWindow) {
return;
}
targetWindow.postMessage(message, origin);
},
[proxyUrl]
);

Copilot uses AI. Check for mistakes.
border: 'none',
overflow: 'hidden',
}}
sandbox="allow-scripts allow-same-origin"
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The sandbox="allow-scripts allow-same-origin" combination is considered unsafe because it allows sandboxed content to remove the sandbox attribute from itself or create new unsandboxed windows. According to MDN documentation, when both flags are present, the sandbox becomes significantly less restrictive. Consider whether allow-same-origin is truly necessary or if there's an alternative approach.

Suggested change
sandbox="allow-scripts allow-same-origin"
sandbox="allow-scripts"

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +45
script-src 'self' 'unsafe-inline'{resources}; \
script-src-elem 'self' 'unsafe-inline'{resources}; \
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The CSP allows 'unsafe-inline' for scripts, which defeats much of the purpose of CSP and enables XSS attacks. While this may be necessary for the current implementation, it significantly weakens the security boundary. Consider using nonces or hashes for inline scripts instead.

Suggested change
script-src 'self' 'unsafe-inline'{resources}; \
script-src-elem 'self' 'unsafe-inline'{resources}; \
script-src 'self'{resources}; \
script-src-elem 'self'{resources}; \

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +45
if (params && typeof params === 'object' && 'url' in params) {
const { url } = params as { url: string };
window.electron.openExternal(url).catch(console.error);
return { status: 'success', message: 'Link opened successfully' };
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The URL from the MCP app is passed directly to window.electron.openExternal() without validation. A malicious app could attempt to open dangerous URLs (e.g., file://, javascript:, or other protocol handlers). The URL should be validated to ensure it uses a safe protocol like https:// or http:// before opening.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings December 22, 2025 22:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.

Comment on lines +1 to +5
//! goose Apps module
//!
//! This module contains types and utilities for working with goose Apps,
//! which are UI resources that can be rendered in an MCP server or native
//! goose apps, or something in between.
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The module documentation refers to "goose Apps" and "MCP server" but based on the PR description, this should be "MCP Apps". The text should consistently use "MCP Apps" throughout to match the spec terminology.

Suggested change
//! goose Apps module
//!
//! This module contains types and utilities for working with goose Apps,
//! which are UI resources that can be rendered in an MCP server or native
//! goose apps, or something in between.
//! MCP Apps module
//!
//! This module contains types and utilities for working with MCP Apps,
//! which are UI resources that can be rendered in MCP Apps or native
//! MCP Apps, or something in between.

Copilot uses AI. Check for mistakes.
@DOsinga DOsinga merged commit 91a6b21 into main Dec 22, 2025
34 of 41 checks passed
@DOsinga DOsinga deleted the feat/add-mcp-app-renderer branch December 22, 2025 23:01
wpfleger96 added a commit that referenced this pull request Dec 23, 2025
* main: (155 commits)
  remove Tool Selection Strategy preview (#6250)
  fix(cli): correct bash syntax in terminal integration functions (#6181)
  fix : opening a session to view it modifies session history order in desktop (#6156)
  test: fix recipe and audio tests to avoid side effects (#6231)
  chore: Update gemini versions in test_providers.sh (#6246)
  feat: option to stream json - jsonl really (#6228)
  feat: add mcp app renderer (#6095)
  docs: update skills extension to support .agents/skills directories (#6199)
  Add YouTube short to Chrome DevTools MCP tutorial (#6244)
  docs: Caveats for privacy information in logs documentation (#6218)
  move goose issue solver to opus (#6233)
  feat: improved UX for tool calls via execute_code (#6205)
  Blog: Code Mode Doesn't Replace MCP (#6227)
  fix: prevent keychain requests during cargo test (#6219)
  test: fix test_max_turns_limit slow execution and wrong message type (#6221)
  Skills vs MCP blog (#6220)
  Add blog post: Does Your AI Agent Need a Plan? (#6209)
  fix(ui): enable MCP UI to send a prompt message when an element is clicked (#6207)
  docs: param option for recipe deeplink/open (#6206)
  docs: edit in place or fork session (#6203)
  ...
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.

2 participants