fix: feat(acp): Honor MCP servers passed from ACP clients
Closes #6111
Summary
Changes made for #6111 (feat/acp: Honor MCP servers passed from ACP clients)
Implementation
- Updated crates/goose-cli/src/commands/acp.rs
- Imports
- Now import Envs, ExtensionConfig, ExtensionEntry, and set_extension to construct MCP extensions from ACP input:
- use goose::agents::extension::Envs;
- use goose::agents::{Agent, ExtensionConfig, SessionConfig};
- use goose::config::{get_all_extensions, set_extension, Config, ExtensionEntry};
- Now import Envs, ExtensionConfig, ExtensionEntry, and set_extension to construct MCP extensions from ACP input:
- new_session implementation
- Before creating the Goose session, iterate over args.mcp_servers:
- For each acp::McpServer::Stdio { name, command, args, env }:
- Convert env (Vec of name/value pairs from ACP) into HashMap<String, String>.
- Construct an ExtensionEntry with:
- enabled: true
- config: ExtensionConfig::Stdio { name, description: String::new(), cmd: command.display().to_string(), args, envs: Envs::new(envs), env_keys: Vec::new(), timeout: None, bundled: None, available_tools: Vec::new(), }
- Call set_extension(entry) to persist/update the MCP extension configuration.
- For each acp::McpServer::Stdio { name, command, args, env }:
- Non-stdio MCP server variants are ignored (no-op), keeping behavior minimal and aligned with the requirement to support stdio MCP servers.
- Session creation remains the same except for removal of the prior inline comment about maybe_update_name.
- Before creating the Goose session, iterate over args.mcp_servers:
- Imports
Behavior
- ACP-provided stdio MCP servers from NewSessionRequest.mcp_servers are now merged into Goose's configured extensions:
- They are stored via set_extension, using ExtensionConfig::key()/name_to_key, so name conflicts are resolved by last-writer-wins.
- This matches the requested “union with ACP wins on conflict” behavior: an ACP-provided server with the same logical name will override an existing configuration entry.
- Existing configured extensions remain unchanged and are still loaded in GooseAcpAgent::new via get_all_extensions.
- MCP capabilities advertised in initialize remain unchanged:
- http: false, sse: false.
- Only stdio transport from ACP is honored; other transports are skipped.
Verification
- source bin/activate-hermit
- cargo check
- Succeeded.
- cargo test -p goose-cli
- Fails due to missing system dependency libxcb when linking test binaries; this is an environment limitation, not a code issue.
- cargo fmt
- Run and completed.
- ./scripts/clippy-lint.sh
- Run via redirected log; no code changes required after linting.
Notable considerations / potential issues
- Environment variables from ACP mcp_servers.env are passed through Envs::new, which filters out disallowed/sensitive keys as defined in Envs; this may silently drop some ACP-provided env vars (by design for security).
- set_extension mutates the global extensions configuration:
- ACP-provided MCP servers will persist for subsequent runs that reuse the same configuration store, consistent with other extension configuration flows.
- If a client repeatedly sends different command/arg/env combinations for the same server name, later sessions will overwrite earlier ones.
- Non-stdio transports in mcp_servers are ignored rather than rejected:
- This is intentional to provide graceful degradation while still meeting the “MUST support stdio MCP servers” requirement.
Generated by goose Issue Solver
Ps the sketch i had in the linked issue was based on the current sdk not the old 0.4. I would recommend work after this to avoid drift https://github.com/block/goose/pull/6109
/goose my sketch in #6111 was imprecise as it subtly affects global state. The goal for ACP is to be like --with-extension which scopes an MCP server addition, not persisting it forever. You should verify something like self.agent.add_extension(config).await.map_err is the correct way to accomplish this goal, and change your code accordingly, and in the same style as other code in this file.
This is not a great PR. Going to close it.