Skip to content
1 change: 1 addition & 0 deletions crates/goose-acp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ futures = { workspace = true }
regex = { workspace = true }
fs-err = "3"
url = { workspace = true }
async-trait = { workspace = true }

# HTTP server dependencies
axum = { workspace = true, features = ["ws"] }
Expand Down
1 change: 1 addition & 0 deletions crates/goose-acp/src/bin/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ 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);

Expand Down
1 change: 1 addition & 0 deletions crates/goose-acp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ mod adapters;
pub mod custom_requests;
pub mod server;
pub mod server_factory;
pub mod tools;
pub mod transport;
68 changes: 57 additions & 11 deletions crates/goose-acp/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::custom_requests::*;
use crate::tools::AcpTools;
use anyhow::Result;
use fs_err as fs;
use goose::agents::extension::{Envs, PLATFORM_EXTENSIONS};
Expand Down Expand Up @@ -59,6 +60,7 @@ pub struct GooseAcpAgent {
goose_mode: goose::config::GooseMode,
disable_session_naming: bool,
builtins: Vec<String>,
acp_editor_tools: bool,
}

fn mcp_server_to_extension_config(mcp_server: McpServer) -> Result<ExtensionConfig, String> {
Expand Down Expand Up @@ -304,6 +306,7 @@ impl GooseAcpAgent {
config_dir: std::path::PathBuf,
goose_mode: goose::config::GooseMode,
disable_session_naming: bool,
acp_editor_tools: bool,
) -> Result<Self> {
let session_manager = Arc::new(SessionManager::new(data_dir));
let permission_manager = Arc::new(PermissionManager::new(config_dir.clone()));
Expand All @@ -317,6 +320,7 @@ impl GooseAcpAgent {
goose_mode,
disable_session_naming,
builtins,
acp_editor_tools,
})
}

Expand All @@ -331,10 +335,12 @@ impl GooseAcpAgent {
));
let agent = Arc::new(agent);

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;
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;
}
}
add_builtins(&agent, self.builtins.clone()).await;

Expand Down Expand Up @@ -476,16 +482,29 @@ impl GooseAcpAgent {
Err(_) => ToolCallStatus::Failed,
};

let content = build_tool_call_content(&tool_response.tool_result);

let locations = if let Some(tool_request) = session.tool_requests.get(&tool_response.id) {
let tool_request = session.tool_requests.get(&tool_response.id);
let locations = if let Some(tool_request) = tool_request {
extract_tool_locations(tool_request, tool_response)
} else {
Vec::new()
};

let mut fields = ToolCallUpdateFields::new().status(status).content(content);
if !locations.is_empty() {
let mut fields = ToolCallUpdateFields::new().status(status);
let is_acp_tool = tool_request
.and_then(|req| req.tool_call.as_ref().ok())
// TODO: something more robust here
.is_some_and(|req| {
req.name == "read"
|| req.name == "write"
|| req.name == "str_replace"
|| req.name == "insert"
|| req.name == "shell"
});

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard-coding tool names for detection is fragile and creates a maintenance burden. Consider adding a method to AcpTools or a marker to identify these tools programmatically, or store the extension name in the tool metadata and check against "acp-tools" instead.

Copilot uses AI. Check for mistakes.
if !is_acp_tool {
let content = build_tool_call_content(&tool_response.tool_result);
fields = fields.content(content);
}
if !is_acp_tool && !locations.is_empty() {
fields = fields.locations(locations);
}
cx.send_notification(SessionNotification::new(
Expand Down Expand Up @@ -674,6 +693,7 @@ impl GooseAcpAgent {
async fn on_new_session(
&self,
args: NewSessionRequest,
cx: JrConnectionCx<AgentToClient>,
) -> Result<NewSessionResponse, sacp::Error> {
debug!(?args, "new session request");

Expand Down Expand Up @@ -711,6 +731,31 @@ impl GooseAcpAgent {
}
}

if self.acp_editor_tools {
let acp_tools = Arc::new(AcpTools::new(
cx,
SessionId::new(goose_session.id.clone()),
args.cwd.clone(),
));
agent
.extension_manager
.add_client(
"acp-tools".to_string(),
ExtensionConfig::Builtin {
name: "acp-tools".to_string(),
description: "".to_string(),
display_name: None,
timeout: None,
bundled: Some(true),
available_tools: vec![],
},
acp_tools,
None,
None,
)
.await;
}

let session = GooseAcpSession {
agent,
messages: Conversation::new_unvalidated(Vec::new()),
Expand Down Expand Up @@ -1221,7 +1266,7 @@ impl JrMessageHandler for GooseAcpHandler {
.await
.if_request(
|req: NewSessionRequest, req_cx: JrRequestCx<NewSessionResponse>| async {
req_cx.respond(agent.on_new_session(req).await?)
req_cx.respond(agent.on_new_session(req, cx.clone()).await?)
},
)
.await
Expand Down Expand Up @@ -1314,7 +1359,7 @@ where
})
}

pub async fn run(builtins: Vec<String>) -> Result<()> {
pub async fn run(builtins: Vec<String>, acp_editor_tools: bool) -> Result<()> {
register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone());
info!("listening on stdio");

Expand All @@ -1326,6 +1371,7 @@ pub async fn run(builtins: Vec<String>) -> 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
Expand Down
2 changes: 2 additions & 0 deletions crates/goose-acp/src/server_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct AcpServerFactoryConfig {
pub builtins: Vec<String>,
pub data_dir: std::path::PathBuf,
pub config_dir: std::path::PathBuf,
pub acp_editor_tools: bool,
}

pub struct AcpServer {
Expand Down Expand Up @@ -50,6 +51,7 @@ impl AcpServer {
self.config.config_dir.clone(),
goose_mode,
disable_session_naming,
self.config.acp_editor_tools,
)
.await?;
info!("Created new ACP agent");
Expand Down
Loading
Loading