From 7c62f50c415018aa01ad01465a518609fb6ff3b7 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Fri, 16 Jan 2026 08:59:04 -0500 Subject: [PATCH] Standalone mcp apps (#6458) Co-authored-by: Douwe Osinga --- crates/goose-server/src/openapi.rs | 7 + crates/goose-server/src/routes/agent.rs | 85 +++++++- crates/goose/src/goose_apps/mod.rs | 206 +++++++++++++++++- ui/desktop/openapi.json | 177 +++++++++++++++ ui/desktop/src/App.tsx | 4 + ui/desktop/src/api/index.ts | 4 +- ui/desktop/src/api/sdk.gen.ts | 13 +- ui/desktop/src/api/types.gen.ts | 87 ++++++++ .../components/GooseSidebar/AppSidebar.tsx | 41 +++- .../src/components/McpApps/McpAppRenderer.tsx | 69 +++++- ui/desktop/src/components/apps/AppsView.tsx | 184 ++++++++++++++++ .../src/components/apps/StandaloneAppView.tsx | 172 +++++++++++++++ ui/desktop/src/hooks/useChatStream.ts | 9 + ui/desktop/src/main.ts | 54 +++++ ui/desktop/src/preload.ts | 3 + 15 files changed, 1086 insertions(+), 29 deletions(-) create mode 100644 ui/desktop/src/components/apps/AppsView.tsx create mode 100644 ui/desktop/src/components/apps/StandaloneAppView.tsx diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index 1caa9ced49..7935a1acf4 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -355,11 +355,13 @@ derive_utoipa!(Icon as IconSchema); super::routes::config_management::get_pricing, super::routes::agent::start_agent, super::routes::agent::resume_agent, + super::routes::agent::stop_agent, super::routes::agent::restart_agent, super::routes::agent::update_working_dir, super::routes::agent::get_tools, super::routes::agent::read_resource, super::routes::agent::call_tool, + super::routes::agent::list_apps, super::routes::agent::update_from_session, super::routes::agent::agent_add_extension, super::routes::agent::agent_remove_extension, @@ -533,8 +535,11 @@ derive_utoipa!(Icon as IconSchema); super::routes::agent::ReadResourceResponse, super::routes::agent::CallToolRequest, super::routes::agent::CallToolResponse, + super::routes::agent::ListAppsRequest, + super::routes::agent::ListAppsResponse, super::routes::agent::StartAgentRequest, super::routes::agent::ResumeAgentRequest, + super::routes::agent::StopAgentRequest, super::routes::agent::RestartAgentRequest, super::routes::agent::UpdateWorkingDirRequest, super::routes::agent::UpdateFromSessionRequest, @@ -547,6 +552,8 @@ derive_utoipa!(Icon as IconSchema); super::tunnel::TunnelInfo, super::tunnel::TunnelState, super::routes::telemetry::TelemetryEventRequest, + goose::goose_apps::GooseApp, + goose::goose_apps::WindowProps, goose::goose_apps::McpAppResource, goose::goose_apps::CspMetadata, goose::goose_apps::UiMetadata, diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index 93d01319b3..9bb75ed5d9 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -11,6 +11,7 @@ use axum::{ Json, Router, }; use goose::agents::ExtensionLoadResult; +use goose::goose_apps::{fetch_mcp_apps, GooseApp, McpAppCache}; use base64::Engine; use goose::agents::ExtensionConfig; @@ -35,7 +36,7 @@ use std::path::PathBuf; use std::sync::atomic::Ordering; use std::sync::Arc; use tokio_util::sync::CancellationToken; -use tracing::error; +use tracing::{error, warn}; #[derive(Deserialize, utoipa::ToSchema)] pub struct UpdateFromSessionRequest { @@ -933,6 +934,87 @@ async fn call_tool( })) } +#[derive(Deserialize, utoipa::IntoParams, utoipa::ToSchema)] +pub struct ListAppsRequest { + session_id: Option, +} + +#[derive(Serialize, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ListAppsResponse { + pub apps: Vec, +} + +#[utoipa::path( + get, + path = "/agent/list_apps", + params( + ListAppsRequest + ), + responses( + (status = 200, description = "List of apps retrieved successfully", body = ListAppsResponse), + (status = 401, description = "Unauthorized - Invalid or missing API key", body = ErrorResponse), + (status = 500, description = "Internal server error", body = ErrorResponse), + ), + security( + ("api_key" = []) + ), + tag = "Agent" +)] +async fn list_apps( + State(state): State>, + Query(params): Query, +) -> Result, ErrorResponse> { + let cache = McpAppCache::new().ok(); + + let Some(session_id) = params.session_id else { + let apps = cache + .as_ref() + .and_then(|c| c.list_apps().ok()) + .unwrap_or_default(); + return Ok(Json(ListAppsResponse { apps })); + }; + + let agent = state + .get_agent_for_route(session_id) + .await + .map_err(|status| ErrorResponse { + message: "Failed to get agent".to_string(), + status, + })?; + + let apps = fetch_mcp_apps(&agent.extension_manager) + .await + .map_err(|e| ErrorResponse { + message: format!("Failed to list apps: {}", e.message), + status: StatusCode::INTERNAL_SERVER_ERROR, + })?; + + if let Some(cache) = cache.as_ref() { + let active_extensions: std::collections::HashSet = apps + .iter() + .filter_map(|app| app.mcp_server.clone()) + .collect(); + + for extension_name in active_extensions { + if let Err(e) = cache.delete_extension_apps(&extension_name) { + warn!( + "Failed to clean cache for extension {}: {}", + extension_name, e + ); + } + } + + for app in &apps { + if let Err(e) = cache.store_app(app) { + warn!("Failed to cache app {}: {}", app.resource.name, e); + } + } + } + + Ok(Json(ListAppsResponse { apps })) +} + pub fn routes(state: Arc) -> Router { Router::new() .route("/agent/start", post(start_agent)) @@ -942,6 +1024,7 @@ pub fn routes(state: Arc) -> Router { .route("/agent/tools", get(get_tools)) .route("/agent/read_resource", post(read_resource)) .route("/agent/call_tool", post(call_tool)) + .route("/agent/list_apps", get(list_apps)) .route("/agent/update_provider", post(update_agent_provider)) .route("/agent/update_from_session", post(update_from_session)) .route("/agent/add_extension", post(agent_add_extension)) diff --git a/crates/goose/src/goose_apps/mod.rs b/crates/goose/src/goose_apps/mod.rs index 4fdeb773f2..b93bf27aee 100644 --- a/crates/goose/src/goose_apps/mod.rs +++ b/crates/goose/src/goose_apps/mod.rs @@ -1,9 +1,203 @@ -//! 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. - pub mod resource; +use crate::agents::ExtensionManager; +use crate::config::paths::Paths; +use rmcp::model::ErrorData; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::fs; +use std::path::PathBuf; +use tokio_util::sync::CancellationToken; +use tracing::warn; +use utoipa::ToSchema; + pub use resource::{CspMetadata, McpAppResource, ResourceMetadata, UiMetadata}; + +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct WindowProps { + pub width: u32, + pub height: u32, + pub resizable: bool, +} + +/// A Goose App combining MCP resource data with Goose-specific metadata +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct GooseApp { + #[serde(flatten)] + pub resource: McpAppResource, + #[serde(skip_serializing_if = "Option::is_none")] + pub mcp_server: Option, + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub window_props: Option, +} + +pub struct McpAppCache { + cache_dir: PathBuf, +} + +impl McpAppCache { + pub fn new() -> Result { + let config_dir = Paths::config_dir(); + let cache_dir = config_dir.join("mcp-apps-cache"); + Ok(Self { cache_dir }) + } + + fn cache_key(extension_name: &str, resource_uri: &str) -> String { + let input = format!("{}::{}", extension_name, resource_uri); + let hash = Sha256::digest(input.as_bytes()); + format!("{}_{:x}", extension_name, hash) + } + + pub fn list_apps(&self) -> Result, std::io::Error> { + let mut apps = Vec::new(); + + if !self.cache_dir.exists() { + return Ok(apps); + } + + for entry in fs::read_dir(&self.cache_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.extension().and_then(|s| s.to_str()) == Some("json") { + match fs::read_to_string(&path) { + Ok(content) => match serde_json::from_str::(&content) { + Ok(app) => apps.push(app), + Err(e) => warn!("Failed to parse cached app from {:?}: {}", path, e), + }, + Err(e) => warn!("Failed to read cached app from {:?}: {}", path, e), + } + } + } + + Ok(apps) + } + + pub fn store_app(&self, app: &GooseApp) -> Result<(), std::io::Error> { + fs::create_dir_all(&self.cache_dir)?; + + if let Some(ref extension_name) = app.mcp_server { + let cache_key = Self::cache_key(extension_name, &app.resource.uri); + let app_path = self.cache_dir.join(format!("{}.json", cache_key)); + let json = serde_json::to_string_pretty(app).map_err(std::io::Error::other)?; + fs::write(app_path, json)?; + } + + Ok(()) + } + + pub fn get_app(&self, extension_name: &str, resource_uri: &str) -> Option { + let cache_key = Self::cache_key(extension_name, resource_uri); + let app_path = self.cache_dir.join(format!("{}.json", cache_key)); + + if !app_path.exists() { + return None; + } + + fs::read_to_string(&app_path) + .ok() + .and_then(|content| serde_json::from_str::(&content).ok()) + } + + pub fn delete_extension_apps(&self, extension_name: &str) -> Result { + let mut deleted_count = 0; + + if !self.cache_dir.exists() { + return Ok(0); + } + + for entry in fs::read_dir(&self.cache_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.extension().and_then(|s| s.to_str()) == Some("json") { + if let Ok(content) = fs::read_to_string(&path) { + if let Ok(app) = serde_json::from_str::(&content) { + if app.mcp_server.as_deref() == Some(extension_name) + && fs::remove_file(&path).is_ok() + { + deleted_count += 1; + } + } + } + } + } + + Ok(deleted_count) + } +} + +pub async fn fetch_mcp_apps( + extension_manager: &ExtensionManager, +) -> Result, ErrorData> { + let mut apps = Vec::new(); + + let ui_resources = extension_manager.get_ui_resources().await?; + + for (extension_name, resource) in ui_resources { + match extension_manager + .read_resource(&resource.uri, &extension_name, CancellationToken::default()) + .await + { + Ok(read_result) => { + let mut html = String::new(); + for content in read_result.contents { + if let rmcp::model::ResourceContents::TextResourceContents { text, .. } = + content + { + html = text; + break; + } + } + + if !html.is_empty() { + let mcp_resource = McpAppResource { + uri: resource.uri.clone(), + name: format_resource_name(resource.name.clone()), + description: resource.description.clone(), + mime_type: "text/html;profile=mcp-app".to_string(), + text: Some(html), + blob: None, + meta: None, + }; + + let app = GooseApp { + resource: mcp_resource, + mcp_server: Some(extension_name), + window_props: Some(WindowProps { + width: 800, + height: 600, + resizable: true, + }), + }; + + apps.push(app); + } + } + Err(e) => { + warn!( + "Failed to read resource {} from {}: {}", + resource.uri, extension_name, e + ); + } + } + } + + Ok(apps) +} + +fn format_resource_name(name: String) -> String { + name.replace('_', " ") + .split_whitespace() + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().chain(chars).collect(), + } + }) + .collect::>() + .join(" ") +} diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 8e0d67ce9f..d27bdaabd3 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -128,6 +128,62 @@ } } }, + "/agent/list_apps": { + "get": { + "tags": [ + "Agent" + ], + "operationId": "list_apps", + "parameters": [ + { + "name": "session_id", + "in": "query", + "required": false, + "schema": { + "type": "string", + "nullable": true + } + } + ], + "responses": { + "200": { + "description": "List of apps retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListAppsResponse" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, "/agent/read_resource": { "post": { "tags": [ @@ -340,6 +396,45 @@ } } }, + "/agent/stop": { + "post": { + "tags": [ + "super::routes::agent" + ], + "operationId": "stop_agent", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StopAgentRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Agent stopped successfully", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "description": "Unauthorized - invalid secret key" + }, + "404": { + "description": "Session not found" + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/agent/tools": { "get": { "tags": [ @@ -3762,6 +3857,31 @@ } } }, + "GooseApp": { + "allOf": [ + { + "$ref": "#/components/schemas/McpAppResource" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/WindowProps" + } + ], + "nullable": true + }, + { + "type": "object", + "properties": { + "mcpServer": { + "type": "string", + "nullable": true + } + } + } + ], + "description": "A Goose App combining MCP resource data with Goose-specific metadata" + }, "Icon": { "type": "object", "required": [ @@ -3855,6 +3975,29 @@ } } }, + "ListAppsRequest": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "nullable": true + } + } + }, + "ListAppsResponse": { + "type": "object", + "required": [ + "apps" + ], + "properties": { + "apps": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GooseApp" + } + } + } + }, "ListRecipeResponse": { "type": "object", "required": [ @@ -5674,6 +5817,17 @@ } } }, + "StopAgentRequest": { + "type": "object", + "required": [ + "session_id" + ], + "properties": { + "session_id": { + "type": "string" + } + } + }, "SubRecipe": { "type": "object", "required": [ @@ -6279,6 +6433,29 @@ } } } + }, + "WindowProps": { + "type": "object", + "required": [ + "width", + "height", + "resizable" + ], + "properties": { + "height": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "resizable": { + "type": "boolean" + }, + "width": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } } } } diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index 5c1a575dec..2c2cfba8a1 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -38,6 +38,8 @@ import PermissionSettingsView from './components/settings/permission/PermissionS import ExtensionsView, { ExtensionsViewOptions } from './components/extensions/ExtensionsView'; import RecipesView from './components/recipes/RecipesView'; +import AppsView from './components/apps/AppsView'; +import StandaloneAppView from './components/apps/StandaloneAppView'; import { View, ViewOptions } from './utils/navigationUtils'; import { useNavigation } from './hooks/useNavigation'; @@ -583,6 +585,7 @@ export function AppInner() { element={ setDidSelectProvider(true)} />} /> } /> + } /> } /> + } /> } /> } /> } /> diff --git a/ui/desktop/src/api/index.ts b/ui/desktop/src/api/index.ts index 6e3f95d530..afff8c37ec 100644 --- a/ui/desktop/src/api/index.ts +++ b/ui/desktop/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { addExtension, agentAddExtension, agentRemoveExtension, backupConfig, callTool, checkProvider, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteRecipe, deleteSchedule, deleteSession, detectProvider, diagnostics, editMessage, encodeRecipe, exportSession, getCustomProvider, getExtensions, getPricing, getProviderModels, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importSession, initConfig, inspectRunningJob, killRunningJob, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, recoverConfig, removeConfig, removeCustomProvider, removeExtension, reply, restartAgent, resumeAgent, runNowHandler, saveRecipe, scanRecipe, scheduleRecipe, sendTelemetryEvent, sessionsHandler, setConfigProvider, setRecipeSlashCommand, startAgent, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopTunnel, systemInfo, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateSchedule, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; -export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, BackupConfigData, BackupConfigErrors, BackupConfigResponse, BackupConfigResponses, CallToolData, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, ChatRequest, CheckProviderData, CheckProviderRequest, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderRequest, DetectProviderResponse, DetectProviderResponse2, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageRequest, EditMessageResponse, EditMessageResponse2, EditMessageResponses, EditType, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, ErrorResponse, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FrontendToolRequest, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetPricingData, GetPricingResponse, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, Icon, ImageContent, ImportSessionData, ImportSessionErrors, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponse, InitConfigResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelConfig, ModelInfo, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, PermissionLevel, PricingData, PricingQuery, PricingResponse, PrincipalType, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponse, RecoverConfigResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionDisplayInfo, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TelemetryEventRequest, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses } from './types.gen'; +export { addExtension, agentAddExtension, agentRemoveExtension, backupConfig, callTool, checkProvider, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteRecipe, deleteSchedule, deleteSession, detectProvider, diagnostics, editMessage, encodeRecipe, exportSession, getCustomProvider, getExtensions, getPricing, getProviderModels, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importSession, initConfig, inspectRunningJob, killRunningJob, listApps, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, recoverConfig, removeConfig, removeCustomProvider, removeExtension, reply, restartAgent, resumeAgent, runNowHandler, saveRecipe, scanRecipe, scheduleRecipe, sendTelemetryEvent, sessionsHandler, setConfigProvider, setRecipeSlashCommand, startAgent, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, systemInfo, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateSchedule, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; +export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, BackupConfigData, BackupConfigErrors, BackupConfigResponse, BackupConfigResponses, CallToolData, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, ChatRequest, CheckProviderData, CheckProviderRequest, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderRequest, DetectProviderResponse, DetectProviderResponse2, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageRequest, EditMessageResponse, EditMessageResponse2, EditMessageResponses, EditType, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, ErrorResponse, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FrontendToolRequest, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetPricingData, GetPricingResponse, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, Icon, ImageContent, ImportSessionData, ImportSessionErrors, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponse, InitConfigResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelConfig, ModelInfo, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, PermissionLevel, PricingData, PricingQuery, PricingResponse, PrincipalType, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponse, RecoverConfigResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionDisplayInfo, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TelemetryEventRequest, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WindowProps } from './types.gen'; diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index 05c10fec0e..c420e84bd6 100644 --- a/ui/desktop/src/api/sdk.gen.ts +++ b/ui/desktop/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client'; import { client } from './client.gen'; -import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; +import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; export type Options = Options2 & { /** @@ -45,6 +45,8 @@ export const callTool = (options: Options< } }); +export const listApps = (options?: Options) => (options?.client ?? client).get({ url: '/agent/list_apps', ...options }); + export const readResource = (options: Options) => (options.client ?? client).post({ url: '/agent/read_resource', ...options, @@ -90,6 +92,15 @@ export const startAgent = (options: Option } }); +export const stopAgent = (options: Options) => (options.client ?? client).post({ + url: '/agent/stop', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + export const getTools = (options: Options) => (options.client ?? client).get({ url: '/agent/tools', ...options }); export const updateFromSession = (options: Options) => (options.client ?? client).post({ diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index e100560eae..6a2e2e9fc6 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -364,6 +364,13 @@ export type GetToolsQuery = { session_id: string; }; +/** + * A Goose App combining MCP resource data with Goose-specific metadata + */ +export type GooseApp = McpAppResource & (WindowProps | null) & { + mcpServer?: string | null; +}; + export type Icon = { mimeType?: string; sizes?: Array; @@ -399,6 +406,14 @@ export type KillJobResponse = { message: string; }; +export type ListAppsRequest = { + session_id?: string | null; +}; + +export type ListAppsResponse = { + apps: Array; +}; + export type ListRecipeResponse = { manifests: Array; }; @@ -967,6 +982,10 @@ export type StartAgentRequest = { working_dir: string; }; +export type StopAgentRequest = { + session_id: string; +}; + export type SubRecipe = { description?: string | null; name: string; @@ -1193,6 +1212,12 @@ export type UpsertPermissionsQuery = { tool_permissions: Array; }; +export type WindowProps = { + height: number; + resizable: boolean; + width: number; +}; + export type ConfirmToolActionData = { body: ConfirmToolActionRequest; path?: never; @@ -1284,6 +1309,37 @@ export type CallToolResponses = { export type CallToolResponse2 = CallToolResponses[keyof CallToolResponses]; +export type ListAppsData = { + body?: never; + path?: never; + query?: { + session_id?: string | null; + }; + url: '/agent/list_apps'; +}; + +export type ListAppsErrors = { + /** + * Unauthorized - Invalid or missing API key + */ + 401: ErrorResponse; + /** + * Internal server error + */ + 500: ErrorResponse; +}; + +export type ListAppsError = ListAppsErrors[keyof ListAppsErrors]; + +export type ListAppsResponses = { + /** + * List of apps retrieved successfully + */ + 200: ListAppsResponse; +}; + +export type ListAppsResponse2 = ListAppsResponses[keyof ListAppsResponses]; + export type ReadResourceData = { body: ReadResourceRequest; path?: never; @@ -1445,6 +1501,37 @@ export type StartAgentResponses = { export type StartAgentResponse = StartAgentResponses[keyof StartAgentResponses]; +export type StopAgentData = { + body: StopAgentRequest; + path?: never; + query?: never; + url: '/agent/stop'; +}; + +export type StopAgentErrors = { + /** + * Unauthorized - invalid secret key + */ + 401: unknown; + /** + * Session not found + */ + 404: unknown; + /** + * Internal server error + */ + 500: unknown; +}; + +export type StopAgentResponses = { + /** + * Agent stopped successfully + */ + 200: string; +}; + +export type StopAgentResponse = StopAgentResponses[keyof StopAgentResponses]; + export type GetToolsData = { body?: never; path?: never; diff --git a/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx b/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx index b91b21adf0..02787717ff 100644 --- a/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx +++ b/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useRef } from 'react'; -import { FileText, Clock, Home, Puzzle, History } from 'lucide-react'; +import React, { useEffect, useRef, useState } from 'react'; +import { FileText, Clock, Home, Puzzle, History, AppWindow } from 'lucide-react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { SidebarContent, @@ -16,6 +16,7 @@ import { ViewOptions, View } from '../../utils/navigationUtils'; import { useChatContext } from '../../contexts/ChatContext'; import { DEFAULT_CHAT_TITLE } from '../../contexts/ChatContext'; import EnvironmentBadge from './EnvironmentBadge'; +import { listApps } from '../../api'; interface SidebarProps { onSelectSession: (sessionId: string) => void; @@ -84,6 +85,13 @@ const menuItems: NavigationEntry[] = [ icon: Puzzle, tooltip: 'Manage your extensions', }, + { + type: 'item', + path: '/apps', + label: 'Apps', + icon: AppWindow, + tooltip: 'Browse and launch MCP apps', + }, { type: 'separator' }, { type: 'item', @@ -100,6 +108,7 @@ const AppSidebar: React.FC = ({ currentPath }) => { const chatContext = useChatContext(); const lastSessionIdRef = useRef(null); const currentSessionId = currentPath === '/pair' ? searchParams.get('resumeSessionId') : null; + const [hasApps, setHasApps] = useState(false); useEffect(() => { if (currentSessionId) { @@ -108,12 +117,19 @@ const AppSidebar: React.FC = ({ currentPath }) => { }, [currentSessionId]); useEffect(() => { - const timer = setTimeout(() => { - // setIsVisible(true); - }, 100); + const checkApps = async () => { + try { + const response = await listApps({ + throwOnError: true, + }); + setHasApps((response.data?.apps || []).length > 0); + } catch (err) { + console.warn('Failed to check for apps:', err); + } + }; - return () => clearTimeout(timer); - }, []); + checkApps(); + }, [currentPath]); useEffect(() => { const currentItem = menuItems.find( @@ -179,10 +195,19 @@ const AppSidebar: React.FC = ({ currentPath }) => { ); }; + const visibleMenuItems = menuItems.filter((entry) => { + if (entry.type === 'item' && entry.path === '/apps') { + return hasApps; + } + return true; + }); + return ( <> - {menuItems.map((entry, index) => renderMenuItem(entry, index))} + + {visibleMenuItems.map((entry, index) => renderMenuItem(entry, index))} + diff --git a/ui/desktop/src/components/McpApps/McpAppRenderer.tsx b/ui/desktop/src/components/McpApps/McpAppRenderer.tsx index 9931425751..e3df201c11 100644 --- a/ui/desktop/src/components/McpApps/McpAppRenderer.tsx +++ b/ui/desktop/src/components/McpApps/McpAppRenderer.tsx @@ -24,12 +24,14 @@ import { readResource, callTool } from '../../api'; interface McpAppRendererProps { resourceUri: string; extensionName: string; - sessionId: string; + sessionId?: string | null; toolInput?: ToolInput; toolInputPartial?: ToolInputPartial; toolResult?: ToolResult; toolCancelled?: ToolCancelled; append?: (text: string) => void; + fullscreen?: boolean; + cachedHtml?: string; } interface ResourceData { @@ -47,9 +49,11 @@ export default function McpAppRenderer({ toolResult, toolCancelled, append, + fullscreen = false, + cachedHtml, }: McpAppRendererProps) { const [resource, setResource] = useState({ - html: null, + html: cachedHtml || null, csp: null, prefersBorder: true, }); @@ -57,6 +61,10 @@ export default function McpAppRenderer({ const [iframeHeight, setIframeHeight] = useState(DEFAULT_IFRAME_HEIGHT); useEffect(() => { + if (!sessionId) { + return; + } + const fetchResource = async () => { try { const response = await readResource({ @@ -73,19 +81,25 @@ export default function McpAppRenderer({ | { ui?: { csp?: CspMetadata; prefersBorder?: boolean } } | undefined; - setResource({ - html: content.text, - csp: meta?.ui?.csp || null, - prefersBorder: meta?.ui?.prefersBorder ?? true, - }); + if (content.text !== cachedHtml) { + setResource({ + html: content.text, + csp: meta?.ui?.csp || null, + prefersBorder: meta?.ui?.prefersBorder ?? true, + }); + } } } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to load resource'); + if (!cachedHtml) { + setError(err instanceof Error ? err.message : 'Failed to load resource'); + } else { + console.warn('Failed to fetch fresh resource, using cached version:', err); + } } }; fetchResource(); - }, [resourceUri, extensionName, sessionId]); + }, [resourceUri, extensionName, sessionId, cachedHtml]); const handleMcpRequest = useCallback( async ( @@ -93,6 +107,12 @@ export default function McpAppRenderer({ params: Record = {}, _id?: string | number ): Promise => { + // Methods that require a session + const requiresSession = ['tools/call', 'resources/read']; + if (requiresSession.includes(method) && !sessionId) { + throw new Error('Session not initialized for MCP request'); + } + switch (method) { case 'ui/open-link': { const { url } = params as McpMethodParams['ui/open-link']; @@ -121,7 +141,7 @@ export default function McpAppRenderer({ const fullToolName = `${extensionName}__${name}`; const response = await callTool({ body: { - session_id: sessionId, + session_id: sessionId!, name: fullToolName, arguments: args || {}, }, @@ -139,7 +159,7 @@ export default function McpAppRenderer({ const { uri } = params as McpMethodParams['resources/read']; const response = await readResource({ body: { - session_id: sessionId, + session_id: sessionId!, uri, extension_name: extensionName, }, @@ -193,6 +213,33 @@ export default function McpAppRenderer({ ); } + if (fullscreen) { + return proxyUrl ? ( +