diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index 45e8521dd7..4ee020bdcc 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -1158,6 +1158,19 @@ async fn list_apps( .flat_map(|app| app.mcp_servers.iter().cloned()) .collect(); + // Before wiping the cache, save imported apps (cache-only) so they survive the refresh. + // These are apps with mcp_servers=["apps"] whose URIs don't appear in the live MCP response. + let live_uris: HashSet<&str> = apps.iter().map(|a| a.resource.uri.as_str()).collect(); + let imported_apps: Vec = cache + .list_apps() + .unwrap_or_default() + .into_iter() + .filter(|a| { + a.mcp_servers.contains(&"apps".to_string()) + && !live_uris.contains(a.resource.uri.as_str()) + }) + .collect(); + for extension_name in active_extensions { if let Err(e) = cache.delete_extension_apps(&extension_name) { warn!( @@ -1172,9 +1185,32 @@ async fn list_apps( warn!("Failed to cache app {}: {}", app.resource.name, e); } } + + // Restore imported apps back to cache + for app in &imported_apps { + if let Err(e) = cache.store_app(app) { + warn!( + "Failed to restore imported app {}: {}", + app.resource.name, e + ); + } + } } let mut apps = apps; + // Merge imported apps into the response + if let Some(cache) = cache.as_ref() { + let live_uris: HashSet = apps.iter().map(|a| a.resource.uri.clone()).collect(); + let imported: Vec = cache + .list_apps() + .unwrap_or_default() + .into_iter() + .filter(|a| { + a.mcp_servers.contains(&"apps".to_string()) && !live_uris.contains(&a.resource.uri) + }) + .collect(); + apps.extend(imported); + } mark_deletable_apps(&mut apps); Ok(Json(ListAppsResponse { apps }))