diff --git a/crates/goose-server/src/routes/mod.rs b/crates/goose-server/src/routes/mod.rs index 83cb42d90d..57e641f812 100644 --- a/crates/goose-server/src/routes/mod.rs +++ b/crates/goose-server/src/routes/mod.rs @@ -8,7 +8,6 @@ pub mod prompts; pub mod recipe; pub mod recipe_utils; pub mod reply; -pub mod sampling; pub mod schedule; pub mod session; pub mod session_events; @@ -35,6 +34,5 @@ pub fn configure(state: Arc, secret_key: String) -> Rout .merge(telemetry::routes(state.clone())) .merge(mcp_app_proxy::routes(secret_key)) .merge(session_events::routes(state.clone())) - .merge(sampling::routes(state.clone())) .merge(dictation::routes(state.clone())) } diff --git a/crates/goose-server/src/routes/sampling.rs b/crates/goose-server/src/routes/sampling.rs deleted file mode 100644 index 7057d61a28..0000000000 --- a/crates/goose-server/src/routes/sampling.rs +++ /dev/null @@ -1,97 +0,0 @@ -use axum::{ - extract::{Path, State}, - http::StatusCode, - routing::post, - Json, Router, -}; -use goose::conversation::message::Message; -use rmcp::model::{ - CreateMessageRequestParams, CreateMessageResult, Role, SamplingContent, SamplingMessage, - SamplingMessageContent, -}; -use std::sync::Arc; - -use crate::state::AppState; - -pub fn routes(state: Arc) -> Router { - Router::new() - .route( - "/sessions/{session_id}/sampling/message", - post(create_message), - ) - .with_state(state) -} - -async fn create_message( - State(state): State>, - Path(session_id): Path, - Json(request): Json, -) -> Result, StatusCode> { - let agent = state.get_agent_for_route(session_id.clone()).await?; - - let provider = agent.provider().await.map_err(|e| { - tracing::error!("Failed to get provider: {}", e); - StatusCode::INTERNAL_SERVER_ERROR - })?; - - let messages: Vec = request - .messages - .iter() - .map(|msg| { - let base = match msg.role { - Role::User => Message::user(), - Role::Assistant => Message::assistant(), - }; - content_to_message(base, &msg.content) - }) - .collect(); - - let system = request - .system_prompt - .as_deref() - .unwrap_or("You are a helpful AI assistant."); - - let model_config = agent - .model_config_for_session(&session_id) - .await - .map_err(|e| { - tracing::error!("Failed to resolve model config: {}", e); - StatusCode::INTERNAL_SERVER_ERROR - })?; - let (response, usage) = goose::session_context::with_session_id( - Some(session_id.clone()), - provider.complete(&model_config, system, &messages, &[]), - ) - .await - .map_err(|e| { - tracing::error!("Sampling completion failed: {}", e); - StatusCode::INTERNAL_SERVER_ERROR - })?; - - let text = response.as_concat_text(); - - Ok(Json( - CreateMessageResult::new( - SamplingMessage::new(Role::Assistant, SamplingMessageContent::text(&text)), - usage.model, - ) - .with_stop_reason(CreateMessageResult::STOP_REASON_END_TURN), - )) -} - -fn content_to_message(base: Message, content: &SamplingContent) -> Message { - let items = match content { - SamplingContent::Single(item) => vec![item], - SamplingContent::Multiple(items) => items.iter().collect(), - }; - - let mut msg = base; - for item in items { - msg = match item { - SamplingMessageContent::Text(text) => msg.with_text(&text.text), - SamplingMessageContent::Image(image) => msg.with_image(&image.data, &image.mime_type), - _ => msg, - }; - } - msg -} diff --git a/crates/goose/src/goose_apps/cache.rs b/crates/goose/src/goose_apps/cache.rs index 36582829fe..3cb0d58380 100644 --- a/crates/goose/src/goose_apps/cache.rs +++ b/crates/goose/src/goose_apps/cache.rs @@ -8,7 +8,6 @@ use tracing::warn; use super::app::GooseApp; static CLOCK_HTML: &str = include_str!("../goose_apps/clock.html"); -static CHAT_HTML: &str = include_str!("../goose_apps/chat.html"); const APPS_EXTENSION_NAME: &str = "apps"; pub struct McpAppCache { @@ -25,12 +24,10 @@ impl McpAppCache { } fn ensure_default_apps(&self) { - for (uri, html) in [("apps://clock", CLOCK_HTML), ("apps://chat", CHAT_HTML)] { - if self.get_app(APPS_EXTENSION_NAME, uri).is_none() { - if let Ok(mut app) = GooseApp::from_html(html) { - app.mcp_servers = vec![APPS_EXTENSION_NAME.to_string()]; - let _ = self.store_app(&app); - } + if self.get_app(APPS_EXTENSION_NAME, "apps://clock").is_none() { + if let Ok(mut app) = GooseApp::from_html(CLOCK_HTML) { + app.mcp_servers = vec![APPS_EXTENSION_NAME.to_string()]; + let _ = self.store_app(&app); } } } diff --git a/crates/goose/src/goose_apps/chat.html b/crates/goose/src/goose_apps/chat.html deleted file mode 100644 index 906d6a8500..0000000000 --- a/crates/goose/src/goose_apps/chat.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - Chat - - - - -
-
- - -
- - - - diff --git a/ui/desktop/src/components/McpApps/McpAppRenderer.tsx b/ui/desktop/src/components/McpApps/McpAppRenderer.tsx index c4681f1a89..cb8daba8a2 100644 --- a/ui/desktop/src/components/McpApps/McpAppRenderer.tsx +++ b/ui/desktop/src/components/McpApps/McpAppRenderer.tsx @@ -51,8 +51,6 @@ import { McpAppToolInputPartial, DimensionLayout, OnDisplayModeChange, - SamplingCreateMessageParams, - SamplingCreateMessageResponse, } from './types'; import { useDisplayMode, @@ -676,13 +674,6 @@ export default function McpAppRenderer({ const [containerWidth, setContainerWidth] = useState(0); const [containerHeight, setContainerHeight] = useState(0); - const [apiHost, setApiHost] = useState(null); - const [secretKey, setSecretKey] = useState(null); - - useEffect(() => { - window.electron.getGoosedHostPort().then(setApiHost); - window.electron.getSecretKey().then(setSecretKey); - }, []); // Fetch the resource from the extension to get HTML and metadata (CSP, permissions, etc.). // If cachedHtml is provided we show it immediately; the fetch updates metadata and @@ -933,38 +924,12 @@ export default function McpAppRenderer({ const handleFallbackRequest = useCallback( async (request: JSONRPCRequest, _extra: RequestHandlerExtra) => { - if (request.method === 'sampling/createMessage') { - if (!sessionId || !apiHost || !secretKey) { - throw new Error('Session not initialized for sampling request'); - } - const { messages, systemPrompt, maxTokens } = - request.params as unknown as SamplingCreateMessageParams; - const response = await fetch(`${apiHost}/sessions/${sessionId}/sampling/message`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Secret-Key': secretKey, - }, - body: JSON.stringify({ - messages: messages.map((m) => ({ - role: m.role, - content: m.content, - })), - systemPrompt, - maxTokens, - }), - }); - if (!response.ok) { - throw new Error(`Sampling request failed: ${response.statusText}`); - } - return (await response.json()) as SamplingCreateMessageResponse; - } return { status: 'error' as const, message: `Unhandled JSON-RPC method: ${request.method ?? ''}`, }; }, - [sessionId, apiHost, secretKey] + [] ); const handleError = useCallback((err: Error) => { diff --git a/ui/desktop/src/components/McpApps/types.ts b/ui/desktop/src/components/McpApps/types.ts index 44fdd42a72..d61d00fbe3 100644 --- a/ui/desktop/src/components/McpApps/types.ts +++ b/ui/desktop/src/components/McpApps/types.ts @@ -41,21 +41,3 @@ export type McpAppToolCancelled = McpUiToolCancelledNotification['params']; * host-side controls or app-initiated `ui/request-display-mode` changes. */ export type OnDisplayModeChange = (mode: GooseDisplayMode) => void; - -export type SamplingMessage = { - role: 'user' | 'assistant'; - content: { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }; -}; - -export type SamplingCreateMessageParams = { - messages: SamplingMessage[]; - systemPrompt?: string; - maxTokens?: number; -}; - -export type SamplingCreateMessageResponse = { - model: string; - stopReason: string; - role: 'assistant'; - content: { type: 'text'; text: string }; -}; diff --git a/ui/desktop/src/components/apps/AppsView.tsx b/ui/desktop/src/components/apps/AppsView.tsx index c3cc353fda..df0a8138b7 100644 --- a/ui/desktop/src/components/apps/AppsView.tsx +++ b/ui/desktop/src/components/apps/AppsView.tsx @@ -1,12 +1,13 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { MainPanelLayout } from '../Layout/MainPanelLayout'; import { Button } from '../ui/button'; -import { Download, Play, Upload } from 'lucide-react'; +import { AlertTriangle, Download, Play, Upload } from 'lucide-react'; import type { GooseApp } from '../../types/apps'; import { exportMcpApp, importMcpApp, listMcpApps } from '../../acp/mcp-apps'; import { useChatContext } from '../../contexts/ChatContext'; import { formatAppName } from '../../utils/conversionUtils'; import { errorMessage } from '../../utils/conversionUtils'; +import { isRetiredGooseChatApp } from '../../utils/retiredApps'; import { defineMessages, useIntl } from '../../i18n'; const i18n = defineMessages({ @@ -52,6 +53,14 @@ const i18n = defineMessages({ id: 'appsView.launch', defaultMessage: 'Launch', }, + retiredChatApp: { + id: 'appsView.retiredChatApp', + defaultMessage: 'Chat app retired', + }, + retiredChatAppDetail: { + id: 'appsView.retiredChatAppDetail', + defaultMessage: 'We removed this feature because MCP sampling is no longer supported.', + }, }); const GridLayout = ({ children }: { children: React.ReactNode }) => { @@ -272,11 +281,25 @@ export default function AppsView() { {apps.map((app) => { const isCustomApp = app.mcpServers?.includes('apps') ?? false; + const retiredChatApp = isRetiredGooseChatApp(app); return (
+ {retiredChatApp && ( +
+ +
+
+ {intl.formatMessage(i18n.retiredChatApp)} +
+

+ {intl.formatMessage(i18n.retiredChatAppDetail)} +

+
+
+ )}

{formatAppName(app.name)} @@ -297,6 +320,7 @@ export default function AppsView() { variant="default" size="sm" onClick={() => handleLaunchApp(app)} + disabled={retiredChatApp} className="flex items-center gap-2 flex-1" > diff --git a/ui/desktop/src/i18n/messages/de.json b/ui/desktop/src/i18n/messages/de.json index 52640c837e..19dc36913b 100644 --- a/ui/desktop/src/i18n/messages/de.json +++ b/ui/desktop/src/i18n/messages/de.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Erneut versuchen" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Apps" }, diff --git a/ui/desktop/src/i18n/messages/en.json b/ui/desktop/src/i18n/messages/en.json index af7e0e216c..85085508b2 100644 --- a/ui/desktop/src/i18n/messages/en.json +++ b/ui/desktop/src/i18n/messages/en.json @@ -41,6 +41,12 @@ "appsView.noAppsTitle": { "defaultMessage": "No apps available" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.retry": { "defaultMessage": "Retry" }, diff --git a/ui/desktop/src/i18n/messages/es.json b/ui/desktop/src/i18n/messages/es.json index 25e7a307fa..8aba39ef76 100644 --- a/ui/desktop/src/i18n/messages/es.json +++ b/ui/desktop/src/i18n/messages/es.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Reintentar" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Apps" }, diff --git a/ui/desktop/src/i18n/messages/fr.json b/ui/desktop/src/i18n/messages/fr.json index d73f192eb5..41fc303d15 100644 --- a/ui/desktop/src/i18n/messages/fr.json +++ b/ui/desktop/src/i18n/messages/fr.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Réessayer" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Applications" }, diff --git a/ui/desktop/src/i18n/messages/hi.json b/ui/desktop/src/i18n/messages/hi.json index 61db2c6d63..906fcb129b 100644 --- a/ui/desktop/src/i18n/messages/hi.json +++ b/ui/desktop/src/i18n/messages/hi.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "पुनः प्रयास करें" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "ऐप्स" }, diff --git a/ui/desktop/src/i18n/messages/id.json b/ui/desktop/src/i18n/messages/id.json index 37090e4852..bdd7de7128 100644 --- a/ui/desktop/src/i18n/messages/id.json +++ b/ui/desktop/src/i18n/messages/id.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Coba lagi" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Aplikasi" }, diff --git a/ui/desktop/src/i18n/messages/it.json b/ui/desktop/src/i18n/messages/it.json index c25c659f0f..c19fa48e16 100644 --- a/ui/desktop/src/i18n/messages/it.json +++ b/ui/desktop/src/i18n/messages/it.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Riprova" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "App" }, diff --git a/ui/desktop/src/i18n/messages/ja.json b/ui/desktop/src/i18n/messages/ja.json index 79e3e4f8cd..75863c6d3b 100644 --- a/ui/desktop/src/i18n/messages/ja.json +++ b/ui/desktop/src/i18n/messages/ja.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "再試行" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "アプリ" }, diff --git a/ui/desktop/src/i18n/messages/ko.json b/ui/desktop/src/i18n/messages/ko.json index 45c8220bea..09aa513621 100644 --- a/ui/desktop/src/i18n/messages/ko.json +++ b/ui/desktop/src/i18n/messages/ko.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "다시 시도" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "앱" }, diff --git a/ui/desktop/src/i18n/messages/ms.json b/ui/desktop/src/i18n/messages/ms.json index ca44ad01ee..68fedd6c24 100644 --- a/ui/desktop/src/i18n/messages/ms.json +++ b/ui/desktop/src/i18n/messages/ms.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Cuba semula" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Apl" }, diff --git a/ui/desktop/src/i18n/messages/pt.json b/ui/desktop/src/i18n/messages/pt.json index a1e7110520..b715b37b65 100644 --- a/ui/desktop/src/i18n/messages/pt.json +++ b/ui/desktop/src/i18n/messages/pt.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Tentar novamente" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Aplicações" }, diff --git a/ui/desktop/src/i18n/messages/ru.json b/ui/desktop/src/i18n/messages/ru.json index fba6151910..fb890d3153 100644 --- a/ui/desktop/src/i18n/messages/ru.json +++ b/ui/desktop/src/i18n/messages/ru.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Повторить" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Приложения" }, diff --git a/ui/desktop/src/i18n/messages/tr.json b/ui/desktop/src/i18n/messages/tr.json index 398b902dc1..c69394cd8d 100644 --- a/ui/desktop/src/i18n/messages/tr.json +++ b/ui/desktop/src/i18n/messages/tr.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Yeniden dene" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Uygulamalar" }, diff --git a/ui/desktop/src/i18n/messages/vi.json b/ui/desktop/src/i18n/messages/vi.json index cc28a0b44b..8f94847441 100644 --- a/ui/desktop/src/i18n/messages/vi.json +++ b/ui/desktop/src/i18n/messages/vi.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "Thử lại" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "Ứng dụng" }, diff --git a/ui/desktop/src/i18n/messages/zh-CN.json b/ui/desktop/src/i18n/messages/zh-CN.json index e45d274c9e..ca094060af 100644 --- a/ui/desktop/src/i18n/messages/zh-CN.json +++ b/ui/desktop/src/i18n/messages/zh-CN.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "重试" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "应用" }, diff --git a/ui/desktop/src/i18n/messages/zh-TW.json b/ui/desktop/src/i18n/messages/zh-TW.json index f8f1acb731..002bae4570 100644 --- a/ui/desktop/src/i18n/messages/zh-TW.json +++ b/ui/desktop/src/i18n/messages/zh-TW.json @@ -44,6 +44,12 @@ "appsView.retry": { "defaultMessage": "重試" }, + "appsView.retiredChatApp": { + "defaultMessage": "Chat app retired" + }, + "appsView.retiredChatAppDetail": { + "defaultMessage": "We removed this feature because MCP sampling is no longer supported." + }, "appsView.title": { "defaultMessage": "應用程式" }, diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index 95d326c559..f444410f17 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -33,6 +33,7 @@ import log from './utils/logger'; import { ensureWinShims } from './utils/winShims'; import { addRecentDir, loadRecentDirs } from './utils/recentDirs'; import { formatAppName, errorMessage, formatErrorForLogging } from './utils/conversionUtils'; +import { isRetiredGooseChatApp } from './utils/retiredApps'; import type { Settings, SettingKey } from './utils/settings'; import { defaultSettings, getKeyboardShortcuts } from './utils/settings'; import * as crypto from 'crypto'; @@ -2877,6 +2878,10 @@ async function appMain() { ipcMain.handle('launch-app', async (event, gooseApp: GooseApp) => { try { + if (isRetiredGooseChatApp(gooseApp)) { + throw new Error('This built-in Chat app is no longer supported.'); + } + const launchingWindow = BrowserWindow.fromWebContents(event.sender); if (!launchingWindow) { throw new Error('Could not find launching window'); diff --git a/ui/desktop/src/utils/retiredApps.ts b/ui/desktop/src/utils/retiredApps.ts new file mode 100644 index 0000000000..14fbc72e17 --- /dev/null +++ b/ui/desktop/src/utils/retiredApps.ts @@ -0,0 +1,13 @@ +import type { GooseApp } from '../types/apps'; + +export function isRetiredGooseChatApp(app: GooseApp) { + return ( + app.mcpServers?.includes('apps') && + app.uri === 'ui://apps/chat' && + app.name === 'chat' && + app.description === 'Simple Chat UI' && + app.width === 400 && + app.height === 500 && + app.resizable === true + ); +}