From 23dbed73fa7b41038aff2d1334d55927192d56fc Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Wed, 25 Feb 2026 11:50:37 -0500 Subject: [PATCH] Use capabilities --- crates/goose-acp/src/bin/server.rs | 1 - crates/goose-acp/src/server.rs | 44 +++++++++++++++----- crates/goose-acp/src/server_factory.rs | 2 - crates/goose-acp/src/tools.rs | 48 ++++++++++++++++++++-- crates/goose-acp/tests/common_tests/mod.rs | 1 - crates/goose-acp/tests/fixtures/mod.rs | 1 - crates/goose-cli/src/cli.rs | 9 +--- 7 files changed, 78 insertions(+), 28 deletions(-) diff --git a/crates/goose-acp/src/bin/server.rs b/crates/goose-acp/src/bin/server.rs index ef7d5d5125..f59f634a2a 100644 --- a/crates/goose-acp/src/bin/server.rs +++ b/crates/goose-acp/src/bin/server.rs @@ -44,7 +44,6 @@ async fn main() -> Result<()> { builtins, data_dir: Paths::data_dir(), config_dir: Paths::config_dir(), - acp_editor_tools: false, })); let router = goose_acp::transport::create_router(server); diff --git a/crates/goose-acp/src/server.rs b/crates/goose-acp/src/server.rs index e3c4869a5a..45d0937887 100644 --- a/crates/goose-acp/src/server.rs +++ b/crates/goose-acp/src/server.rs @@ -1,5 +1,6 @@ use crate::custom_requests::*; use crate::tools::AcpTools; +use agent_client_protocol_schema::ClientCapabilities; use anyhow::Result; use fs_err as fs; use goose::agents::extension::{Envs, PLATFORM_EXTENSIONS}; @@ -60,7 +61,7 @@ pub struct GooseAcpAgent { goose_mode: goose::config::GooseMode, disable_session_naming: bool, builtins: Vec, - acp_editor_tools: bool, + client_capabilities: Mutex, } fn mcp_server_to_extension_config(mcp_server: McpServer) -> Result { @@ -306,7 +307,6 @@ impl GooseAcpAgent { config_dir: std::path::PathBuf, goose_mode: goose::config::GooseMode, disable_session_naming: bool, - acp_editor_tools: bool, ) -> Result { let session_manager = Arc::new(SessionManager::new(data_dir)); let permission_manager = Arc::new(PermissionManager::new(config_dir.clone())); @@ -320,7 +320,7 @@ impl GooseAcpAgent { goose_mode, disable_session_naming, builtins, - acp_editor_tools, + client_capabilities: Mutex::new(ClientCapabilities::default()), }) } @@ -335,12 +335,19 @@ impl GooseAcpAgent { )); let agent = Arc::new(agent); - if !self.acp_editor_tools { - let config_path = self.config_dir.join(CONFIG_YAML_NAME); - if let Ok(config_file) = Config::new(&config_path, "goose") { - let extensions = get_enabled_extensions_with_config(&config_file); - add_extensions(&agent, extensions).await; + let exclude_developer = { + let capabilities = self.client_capabilities.lock().await; + capabilities.terminal + || capabilities.fs.read_text_file + || capabilities.fs.write_text_file + }; + let config_path = self.config_dir.join(CONFIG_YAML_NAME); + if let Ok(config_file) = Config::new(&config_path, "goose") { + let mut extensions = get_enabled_extensions_with_config(&config_file); + if exclude_developer { + extensions.retain(|ext| ext.name() != "developer"); } + add_extensions(&agent, extensions).await; } add_builtins(&agent, self.builtins.clone()).await; @@ -676,6 +683,11 @@ impl GooseAcpAgent { ) -> Result { debug!(?args, "initialize request"); + { + let mut client_capabilities = self.client_capabilities.lock().await; + *client_capabilities = args.client_capabilities; + } + let capabilities = AgentCapabilities::new() .load_session(true) .prompt_capabilities( @@ -737,11 +749,22 @@ impl GooseAcpAgent { } } - if self.acp_editor_tools { + let (fs_read, fs_write, terminal) = { + let capabilities = self.client_capabilities.lock().await; + ( + capabilities.fs.read_text_file, + capabilities.fs.write_text_file, + capabilities.terminal, + ) + }; + if fs_read || fs_write || terminal { let acp_tools = Arc::new(AcpTools::new( cx, SessionId::new(goose_session.id.clone()), args.cwd.clone(), + fs_read, + fs_write, + terminal, )); agent .extension_manager @@ -1365,7 +1388,7 @@ where }) } -pub async fn run(builtins: Vec, acp_editor_tools: bool) -> Result<()> { +pub async fn run(builtins: Vec) -> Result<()> { register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone()); info!("listening on stdio"); @@ -1377,7 +1400,6 @@ pub async fn run(builtins: Vec, acp_editor_tools: bool) -> Result<()> { builtins, data_dir: Paths::data_dir(), config_dir: Paths::config_dir(), - acp_editor_tools, }); let agent = server.create_agent().await?; serve(agent, incoming, outgoing).await diff --git a/crates/goose-acp/src/server_factory.rs b/crates/goose-acp/src/server_factory.rs index df779d771a..b2810d8564 100644 --- a/crates/goose-acp/src/server_factory.rs +++ b/crates/goose-acp/src/server_factory.rs @@ -9,7 +9,6 @@ pub struct AcpServerFactoryConfig { pub builtins: Vec, pub data_dir: std::path::PathBuf, pub config_dir: std::path::PathBuf, - pub acp_editor_tools: bool, } pub struct AcpServer { @@ -51,7 +50,6 @@ impl AcpServer { self.config.config_dir.clone(), goose_mode, disable_session_naming, - self.config.acp_editor_tools, ) .await?; info!("Created new ACP agent"); diff --git a/crates/goose-acp/src/tools.rs b/crates/goose-acp/src/tools.rs index dc92088bb1..31d838742a 100644 --- a/crates/goose-acp/src/tools.rs +++ b/crates/goose-acp/src/tools.rs @@ -18,10 +18,17 @@ use schemars::{schema_for, JsonSchema}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tokio_util::sync::CancellationToken; +struct Capabilities { + filesystem_read: bool, + filesystem_write: bool, + terminal: bool, +} + pub struct AcpTools { cx: JrConnectionCx, session_id: sacp::schema::SessionId, working_dir: PathBuf, + capabilities: Capabilities, } #[derive(Debug, Serialize, Deserialize, JsonSchema)] @@ -74,9 +81,11 @@ macro_rules! schema { }; } -static TOOL_DEFS: LazyLock> = LazyLock::new(|| { +static FILESYSTEM_READ_TOOL_DEFS: LazyLock> = + LazyLock::new(|| vec![Tool::new("read", "Read files", schema!(ReadParams))]); + +static FILESYSTEM_WRITE_TOOL_DEFS: LazyLock> = LazyLock::new(|| { vec![ - Tool::new("read", "Read files", schema!(ReadParams)), Tool::new("write", "Write files", schema!(WriteParams)), Tool::new( "str_replace", @@ -84,10 +93,17 @@ static TOOL_DEFS: LazyLock> = LazyLock::new(|| { schema!(StrReplaceParams), ), Tool::new("insert", "Insert strings into files", schema!(InsertParams)), - Tool::new("shell", "Execute shell commands", schema!(ShellParams)), ] }); +static TERMINAL_TOOL_DEFS: LazyLock> = LazyLock::new(|| { + vec![Tool::new( + "shell", + "Execute shell commands", + schema!(ShellParams), + )] +}); + async fn handle_tool_call( mut func: impl FnMut(Req) -> Fut, request: JsonObject, @@ -149,11 +165,19 @@ impl AcpTools { cx: JrConnectionCx, session_id: sacp::schema::SessionId, working_dir: PathBuf, + filesystem_read: bool, + filesystem_write: bool, + terminal: bool, ) -> Self { AcpTools { cx, session_id, working_dir, + capabilities: Capabilities { + filesystem_read, + filesystem_write, + terminal, + }, } } @@ -369,7 +393,23 @@ impl McpClientTrait for AcpTools { _next_cursor: Option, _cancel_token: CancellationToken, ) -> Result { - Ok(ListToolsResult::with_all_items(TOOL_DEFS.clone())) + let tools = [ + ( + self.capabilities.filesystem_read, + &*FILESYSTEM_READ_TOOL_DEFS, + ), + ( + self.capabilities.filesystem_write, + &*FILESYSTEM_WRITE_TOOL_DEFS, + ), + (self.capabilities.terminal, &*TERMINAL_TOOL_DEFS), + ] + .into_iter() + .filter(|(enabled, _)| *enabled) + .flat_map(|(_, defs)| defs.clone()) + .collect(); + + Ok(ListToolsResult::with_all_items(tools)) } async fn call_tool( diff --git a/crates/goose-acp/tests/common_tests/mod.rs b/crates/goose-acp/tests/common_tests/mod.rs index f0d539ff4e..e6a4fd26ee 100644 --- a/crates/goose-acp/tests/common_tests/mod.rs +++ b/crates/goose-acp/tests/common_tests/mod.rs @@ -73,7 +73,6 @@ pub async fn run_initialize_without_provider() { temp_dir.path().to_path_buf(), GooseMode::Auto, false, - false, ) .await .unwrap(), diff --git a/crates/goose-acp/tests/fixtures/mod.rs b/crates/goose-acp/tests/fixtures/mod.rs index 072503b8d1..ff9c298311 100644 --- a/crates/goose-acp/tests/fixtures/mod.rs +++ b/crates/goose-acp/tests/fixtures/mod.rs @@ -228,7 +228,6 @@ pub async fn spawn_acp_server_in_process( data_root.to_path_buf(), goose_mode, true, - false, ) .await .unwrap(), diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index 3822a363a7..080e8975e6 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -691,10 +691,6 @@ enum Command { value_delimiter = ',' )] builtins: Vec, - - /// Enable ACP editor tools - #[arg(long)] - acp_editor_tools: bool, }, /// Start or resume interactive chat sessions @@ -1667,10 +1663,7 @@ pub async fn cli() -> anyhow::Result<()> { Some(Command::Configure {}) => handle_configure().await, Some(Command::Info { verbose }) => handle_info(verbose), Some(Command::Mcp { server }) => handle_mcp_command(server).await, - Some(Command::Acp { - builtins, - acp_editor_tools, - }) => goose_acp::server::run(builtins, acp_editor_tools).await, + Some(Command::Acp { builtins }) => goose_acp::server::run(builtins).await, Some(Command::Session { command: Some(cmd), .. }) => handle_session_subcommand(cmd).await,