From a9f4b2d63775975f5f798f197268576078416b9d Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Mon, 2 Feb 2026 13:21:35 -0500 Subject: [PATCH] Remove dependency on goose-mcp from goose crate (#6637) --- Cargo.lock | 2 +- crates/goose-acp/Cargo.toml | 1 + crates/goose-acp/src/server.rs | 2 ++ crates/goose-acp/tests/fixtures/mod.rs | 3 +++ crates/goose-cli/src/cli.rs | 3 +++ .../src/scenario_tests/scenario_runner.rs | 2 +- crates/goose-mcp/src/lib.rs | 16 +++---------- crates/goose-server/src/state.rs | 3 +++ crates/goose/Cargo.toml | 1 - crates/goose/src/agents/extension_manager.rs | 19 ++++++--------- crates/goose/src/builtin_extension.rs | 24 +++++++++++++++++++ crates/goose/src/lib.rs | 1 + 12 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 crates/goose/src/builtin_extension.rs diff --git a/Cargo.lock b/Cargo.lock index 46dbd549..97092d68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2872,7 +2872,6 @@ dependencies = [ "etcetera 0.11.0", "fs2", "futures", - "goose-mcp", "ignore", "include_dir", "indexmap 2.13.0", @@ -2946,6 +2945,7 @@ dependencies = [ "fs-err", "futures", "goose", + "goose-mcp", "http-body-util", "regex", "rmcp 0.14.0", diff --git a/crates/goose-acp/Cargo.toml b/crates/goose-acp/Cargo.toml index e1a50961..871dc71f 100644 --- a/crates/goose-acp/Cargo.toml +++ b/crates/goose-acp/Cargo.toml @@ -16,6 +16,7 @@ workspace = true [dependencies] goose = { path = "../goose" } +goose-mcp = { path = "../goose-mcp" } rmcp = { workspace = true } sacp = "10.1.0" anyhow = { workspace = true } diff --git a/crates/goose-acp/src/server.rs b/crates/goose-acp/src/server.rs index c2049d66..2f37872f 100644 --- a/crates/goose-acp/src/server.rs +++ b/crates/goose-acp/src/server.rs @@ -2,6 +2,7 @@ use anyhow::Result; use fs_err as fs; use goose::agents::extension::{Envs, PLATFORM_EXTENSIONS}; use goose::agents::{Agent, AgentConfig, ExtensionConfig, SessionConfig}; +use goose::builtin_extension::register_builtin_extensions; use goose::config::base::CONFIG_YAML_NAME; use goose::config::extensions::get_enabled_extensions_with_config; use goose::config::paths::Paths; @@ -1035,6 +1036,7 @@ where } pub async fn run(builtins: Vec) -> Result<()> { + register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone()); info!("listening on stdio"); let outgoing = tokio::io::stdout().compat_write(); diff --git a/crates/goose-acp/tests/fixtures/mod.rs b/crates/goose-acp/tests/fixtures/mod.rs index f79cbf8d..110d2385 100644 --- a/crates/goose-acp/tests/fixtures/mod.rs +++ b/crates/goose-acp/tests/fixtures/mod.rs @@ -1,6 +1,7 @@ use assert_json_diff::{assert_json_matches_no_panic, CompareMode, Config}; use async_trait::async_trait; use fs_err as fs; +use goose::builtin_extension::register_builtin_extensions; use goose::config::{GooseMode, PermissionManager}; use goose::model::ModelConfig; use goose::providers::api_client::{ApiClient, AuthMethod}; @@ -445,6 +446,8 @@ pub fn run_test(fut: F) where F: Future + Send + 'static, { + register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone()); + let handle = std::thread::Builder::new() .name("acp-test".to_string()) .stack_size(8 * 1024 * 1024) diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index ba38ce92..babe728c 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -1,6 +1,7 @@ use anyhow::Result; use clap::{Args, CommandFactory, Parser, Subcommand}; use clap_complete::{generate, Shell as ClapShell}; +use goose::builtin_extension::register_builtin_extensions; use goose::config::Config; use goose::posthog::get_telemetry_choice; use goose::recipe::Recipe; @@ -1513,6 +1514,8 @@ async fn handle_default_session() -> Result<()> { } pub async fn cli() -> anyhow::Result<()> { + register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone()); + let cli = Cli::parse(); if let Err(e) = crate::project_tracker::update_project_tracker(None, None) { diff --git a/crates/goose-cli/src/scenario_tests/scenario_runner.rs b/crates/goose-cli/src/scenario_tests/scenario_runner.rs index 6815c1dd..46fee597 100644 --- a/crates/goose-cli/src/scenario_tests/scenario_runner.rs +++ b/crates/goose-cli/src/scenario_tests/scenario_runner.rs @@ -204,7 +204,7 @@ where let temp_dir = TempDir::new()?; let session_manager = Arc::new(SessionManager::new(temp_dir.path().to_path_buf())); let permission_manager = Arc::new(PermissionManager::new(temp_dir.path().to_path_buf())); - let agent_config = AgentConfig::new(session_manager, permission_manager, None, GooseMode::Auto); // no scheduler needed for scenario tests + let agent_config = AgentConfig::new(session_manager, permission_manager, None, GooseMode::Auto); let agent = Agent::with_config(agent_config); agent .extension_manager diff --git a/crates/goose-mcp/src/lib.rs b/crates/goose-mcp/src/lib.rs index b940ec4a..4b4a5406 100644 --- a/crates/goose-mcp/src/lib.rs +++ b/crates/goose-mcp/src/lib.rs @@ -22,13 +22,9 @@ pub use developer::rmcp_developer::DeveloperServer; pub use memory::MemoryServer; pub use tutorial::TutorialServer; +/// Type definition for a function that spawns and serves a builtin extension server pub type SpawnServerFn = fn(tokio::io::DuplexStream, tokio::io::DuplexStream); -pub struct BuiltinDef { - pub name: &'static str, - pub spawn_server: SpawnServerFn, -} - fn spawn_and_serve( name: &'static str, server: S, @@ -51,17 +47,11 @@ macro_rules! builtin { fn spawn(r: tokio::io::DuplexStream, w: tokio::io::DuplexStream) { spawn_and_serve(stringify!($name), <$server_ty>::new(), (r, w)); } - ( - stringify!($name), - BuiltinDef { - name: stringify!($name), - spawn_server: spawn, - }, - ) + (stringify!($name), spawn as SpawnServerFn) }}; } -pub static BUILTIN_EXTENSIONS: Lazy> = Lazy::new(|| { +pub static BUILTIN_EXTENSIONS: Lazy> = Lazy::new(|| { HashMap::from([ builtin!(developer, DeveloperServer), builtin!(autovisualiser, AutoVisualiserRouter), diff --git a/crates/goose-server/src/state.rs b/crates/goose-server/src/state.rs index 06620aea..85f4ab76 100644 --- a/crates/goose-server/src/state.rs +++ b/crates/goose-server/src/state.rs @@ -1,4 +1,5 @@ use axum::http::StatusCode; +use goose::builtin_extension::register_builtin_extensions; use goose::execution::manager::AgentManager; use goose::scheduler_trait::SchedulerTrait; use goose::session::SessionManager; @@ -26,6 +27,8 @@ pub struct AppState { impl AppState { pub async fn new() -> anyhow::Result> { + register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone()); + let agent_manager = AgentManager::instance().await?; let tunnel_manager = Arc::new(TunnelManager::new()); diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index 3aadb82a..5f912e84 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -87,7 +87,6 @@ dashmap = "6.1" ahash = "0.8" tokio-util = { version = "0.7.15", features = ["compat"] } unicode-normalization = "0.1" -goose-mcp = { path = "../goose-mcp" } zip = "0.6" sys-info = "0.9" diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index 104cdf2a..e54e5bb4 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -35,6 +35,7 @@ use super::types::SharedProvider; use crate::agents::extension::{Envs, ProcessExit}; use crate::agents::extension_malware_check; use crate::agents::mcp_client::{McpClient, McpClientTrait}; +use crate::builtin_extension::get_builtin_extension; use crate::config::extensions::name_to_key; use crate::config::search_path::SearchPaths; use crate::config::{get_all_extensions, Config}; @@ -565,13 +566,10 @@ impl ExtensionManager { ExtensionConfig::Builtin { name, timeout, .. } => { let timeout_duration = Duration::from_secs(timeout.unwrap_or(300)); let normalized_name = name_to_key(name); - - if !goose_mcp::BUILTIN_EXTENSIONS.contains_key(normalized_name.as_str()) { - return Err(ExtensionError::ConfigError(format!( - "Unknown builtin extension: {}", - name - ))); - } + let extension_fn = + get_builtin_extension(normalized_name.as_str()).ok_or_else(|| { + ExtensionError::ConfigError(format!("Unknown builtin extension: {}", name)) + })?; if let Some(container) = container { let container_id = container.id(); @@ -580,6 +578,7 @@ impl ExtensionManager { builtin = %name, "Starting builtin extension inside Docker container" ); + let normalized_name = name_to_key(name); let command = Command::new("docker").configure(|command| { command .arg("exec") @@ -600,10 +599,6 @@ impl ExtensionManager { .await?; Box::new(client) } else { - let def = goose_mcp::BUILTIN_EXTENSIONS - .get(normalized_name.as_str()) - .unwrap(); - // Set GOOSE_WORKING_DIR in the current process for builtin extensions // since they run in-process and read from std::env::var if effective_working_dir.exists() && effective_working_dir.is_dir() { @@ -616,7 +611,7 @@ impl ExtensionManager { let (server_read, client_write) = tokio::io::duplex(65536); let (client_read, server_write) = tokio::io::duplex(65536); - (def.spawn_server)(server_read, server_write); + extension_fn(server_read, server_write); Box::new( McpClient::connect( (client_read, client_write), diff --git a/crates/goose/src/builtin_extension.rs b/crates/goose/src/builtin_extension.rs new file mode 100644 index 00000000..c69a44cf --- /dev/null +++ b/crates/goose/src/builtin_extension.rs @@ -0,0 +1,24 @@ +use once_cell::sync::Lazy; +use std::collections::HashMap; +use std::sync::RwLock; + +pub type SpawnServerFn = fn(tokio::io::DuplexStream, tokio::io::DuplexStream); + +static BUILTIN_REGISTRY: Lazy>> = + Lazy::new(|| RwLock::new(HashMap::new())); + +/// Register a builtin extension into the global registry +pub fn register_builtin_extension(name: &'static str, spawn_fn: SpawnServerFn) { + BUILTIN_REGISTRY.write().unwrap().insert(name, spawn_fn); +} + +/// Register multiple builtin extensions from a HashMap +pub fn register_builtin_extensions(extensions: HashMap<&'static str, SpawnServerFn>) { + let mut registry = BUILTIN_REGISTRY.write().unwrap(); + registry.extend(extensions); +} + +/// Get a copy of all registered builtin extensions +pub fn get_builtin_extension(name: &str) -> Option { + BUILTIN_REGISTRY.read().unwrap().get(name).cloned() +} diff --git a/crates/goose/src/lib.rs b/crates/goose/src/lib.rs index 4b834c53..d5b0e028 100644 --- a/crates/goose/src/lib.rs +++ b/crates/goose/src/lib.rs @@ -1,5 +1,6 @@ pub mod action_required_manager; pub mod agents; +pub mod builtin_extension; pub mod config; pub mod context_mgmt; pub mod conversation;