Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/goose-acp/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,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::agents::{Agent, AgentConfig, ExtensionConfig, GoosePlatform, SessionConfig};
use goose::builtin_extension::register_builtin_extensions;
use goose::config::base::CONFIG_YAML_NAME;
use goose::config::extensions::get_enabled_extensions_with_config;
Expand Down Expand Up @@ -325,6 +325,7 @@ impl GooseAcpAgent {
None,
self.goose_mode,
self.disable_session_naming,
GoosePlatform::GooseCli,
));
let agent = Arc::new(agent);

Expand Down
3 changes: 2 additions & 1 deletion crates/goose-cli/src/scenario_tests/scenario_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::scenario_tests::mock_client::weather_client;
use crate::scenario_tests::provider_configs::{get_provider_configs, ProviderConfig};
use crate::session::CliSession;
use anyhow::Result;
use goose::agents::{Agent, AgentConfig};
use goose::agents::{Agent, AgentConfig, GoosePlatform};
use goose::config::permission::PermissionManager;
use goose::config::GooseMode;
use goose::model::ModelConfig;
Expand Down Expand Up @@ -214,6 +214,7 @@ where
None,
GooseMode::Auto,
true,
GoosePlatform::GooseCli,
);
let agent = Agent::with_config(agent_config);
agent
Expand Down
36 changes: 34 additions & 2 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
Expand All @@ -14,7 +15,9 @@ use super::platform_tools;
use super::tool_execution::{ToolCallResult, CHAT_MODE_TOOL_SKIPPED_RESPONSE, DECLINED_RESPONSE};
use crate::action_required_manager::ActionRequiredManager;
use crate::agents::extension::{ExtensionConfig, ExtensionResult, ToolInfo};
use crate::agents::extension_manager::{get_parameter_names, ExtensionManager};
use crate::agents::extension_manager::{
get_parameter_names, ExtensionManager, ExtensionManagerCapabilities,
};
use crate::agents::final_output_tool::{FINAL_OUTPUT_CONTINUATION_MESSAGE, FINAL_OUTPUT_TOOL_NAME};
use crate::agents::platform_extensions::MANAGE_EXTENSIONS_TOOL_NAME_COMPLETE;
use crate::agents::platform_tools::PLATFORM_MANAGE_SCHEDULE_TOOL_NAME;
Expand Down Expand Up @@ -84,13 +87,29 @@ pub struct ExtensionLoadResult {
pub error: Option<String>,
}

#[derive(Clone, Debug)]
pub enum GoosePlatform {
GooseDesktop,
GooseCli,
}

impl fmt::Display for GoosePlatform {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GoosePlatform::GooseCli => write!(f, "goose-cli"),
GoosePlatform::GooseDesktop => write!(f, "goose-desktop"),
}
}
}

#[derive(Clone)]
pub struct AgentConfig {
pub session_manager: Arc<SessionManager>,
pub permission_manager: Arc<PermissionManager>,
pub scheduler_service: Option<Arc<dyn SchedulerTrait>>,
pub goose_mode: GooseMode,
pub disable_session_naming: bool,
pub goose_platform: GoosePlatform,
}

impl AgentConfig {
Expand All @@ -100,13 +119,15 @@ impl AgentConfig {
scheduler_service: Option<Arc<dyn SchedulerTrait>>,
goose_mode: GooseMode,
disable_session_naming: bool,
goose_platform: GoosePlatform,
) -> Self {
Self {
session_manager,
permission_manager,
scheduler_service,
goose_mode,
disable_session_naming,
goose_platform,
}
}
}
Expand Down Expand Up @@ -190,6 +211,7 @@ impl Agent {
Config::global()
.get_goose_disable_session_naming()
.unwrap_or(false),
GoosePlatform::GooseCli,
))
}

Expand All @@ -199,12 +221,22 @@ impl Agent {
let (tool_tx, tool_rx) = mpsc::channel(32);
let provider = Arc::new(Mutex::new(None));

let goose_platform = config.goose_platform.clone();
let capabilities = match config.goose_platform {
GoosePlatform::GooseDesktop => ExtensionManagerCapabilities { mcpui: true },
GoosePlatform::GooseCli => ExtensionManagerCapabilities { mcpui: false },
};
let session_manager = Arc::clone(&config.session_manager);
let permission_manager = Arc::clone(&config.permission_manager);
Self {
provider: provider.clone(),
config,
extension_manager: Arc::new(ExtensionManager::new(provider.clone(), session_manager)),
extension_manager: Arc::new(ExtensionManager::new(
provider.clone(),
session_manager,
goose_platform.to_string(),
capabilities,
)),
final_output_tool: Arc::new(Mutex::new(None)),
frontend_tools: Mutex::new(HashMap::new()),
frontend_instructions: Mutex::new(None),
Expand Down
70 changes: 66 additions & 4 deletions crates/goose/src/agents/extension_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use super::tool_execution::ToolCallResult;
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::agents::mcp_client::{GooseMcpClientCapabilities, McpClient, McpClientTrait};
use crate::builtin_extension::get_builtin_extension;
use crate::config::extensions::name_to_key;
use crate::config::search_path::SearchPaths;
Expand Down Expand Up @@ -99,13 +99,19 @@ impl Extension {
}
}

pub struct ExtensionManagerCapabilities {
pub mcpui: bool,
}

/// Manages goose extensions / MCP clients and their interactions
pub struct ExtensionManager {
extensions: Mutex<HashMap<String, Extension>>,
context: PlatformExtensionContext,
provider: SharedProvider,
tools_cache: Mutex<Option<Arc<Vec<Tool>>>>,
tools_cache_version: AtomicU64,
client_name: String,
capabilities: ExtensionManagerCapabilities,
}

/// A flattened representation of a resource used by the agent to prepare inference
Expand Down Expand Up @@ -221,6 +227,8 @@ async fn child_process_client(
provider: SharedProvider,
working_dir: Option<&PathBuf>,
docker_container: Option<String>,
client_name: String,
capabilities: GooseMcpClientCapabilities,
) -> ExtensionResult<McpClient> {
configure_subprocess(&mut command);

Expand Down Expand Up @@ -258,6 +266,8 @@ async fn child_process_client(
Duration::from_secs(timeout.unwrap_or(crate::config::DEFAULT_EXTENSION_TIMEOUT)),
provider,
docker_container,
client_name,
capabilities,
)
.await;

Expand Down Expand Up @@ -384,6 +394,8 @@ async fn create_streamable_http_client(
headers: &HashMap<String, String>,
name: &str,
provider: SharedProvider,
client_name: String,
capabilities: GooseMcpClientCapabilities,
) -> ExtensionResult<Box<dyn McpClientTrait>> {
let mut default_headers = HeaderMap::new();

Expand Down Expand Up @@ -415,7 +427,14 @@ async fn create_streamable_http_client(
let timeout_duration =
Duration::from_secs(timeout.unwrap_or(crate::config::DEFAULT_EXTENSION_TIMEOUT));

let client_res = McpClient::connect(transport, timeout_duration, provider.clone()).await;
let client_res = McpClient::connect(
transport,
timeout_duration,
provider.clone(),
client_name.clone(),
capabilities.clone(),
)
.await;

if extract_auth_error(&client_res).is_some() {
let auth_manager = oauth_flow(&uri.to_string(), &name.to_string())
Expand All @@ -438,7 +457,14 @@ async fn create_streamable_http_client(
},
);
Ok(Box::new(
McpClient::connect(transport, timeout_duration, provider).await?,
McpClient::connect(
transport,
timeout_duration,
provider,
client_name,
capabilities,
)
.await?,
))
} else {
Ok(Box::new(client_res?))
Expand All @@ -449,6 +475,8 @@ impl ExtensionManager {
pub fn new(
provider: SharedProvider,
session_manager: Arc<crate::session::SessionManager>,
client_name: String,
capabilities: ExtensionManagerCapabilities,
) -> Self {
Self {
extensions: Mutex::new(HashMap::new()),
Expand All @@ -459,13 +487,20 @@ impl ExtensionManager {
provider,
tools_cache: Mutex::new(None),
tools_cache_version: AtomicU64::new(0),
client_name,
capabilities,
}
}

#[cfg(test)]
pub fn new_without_provider(data_dir: std::path::PathBuf) -> Self {
let session_manager = Arc::new(crate::session::SessionManager::new(data_dir));
Self::new(Arc::new(Mutex::new(None)), session_manager)
Self::new(
Arc::new(Mutex::new(None)),
session_manager,
"goose-cli".to_string(),
ExtensionManagerCapabilities { mcpui: false },
)
}

pub fn get_context(&self) -> &PlatformExtensionContext {
Expand Down Expand Up @@ -527,12 +562,18 @@ impl ExtensionManager {
.iter()
.map(|(k, v)| (k.clone(), substitute_env_vars(v, &all_envs)))
.collect();
let capability = GooseMcpClientCapabilities {
mcpui: self.capabilities.mcpui,
};

create_streamable_http_client(
uri,
*timeout,
&resolved_headers,
name,
self.provider.clone(),
self.client_name.clone(),
capability,
)
.await?
}
Expand Down Expand Up @@ -578,12 +619,17 @@ impl ExtensionManager {
})
};

let capabilities = GooseMcpClientCapabilities {
mcpui: self.capabilities.mcpui,
};
let client = child_process_client(
command,
timeout,
self.provider.clone(),
Some(&effective_working_dir),
container.map(|c| c.id().to_string()),
self.client_name.clone(),
capabilities,
)
.await?;
Box::new(client)
Expand Down Expand Up @@ -614,24 +660,34 @@ impl ExtensionManager {
.arg(&normalized_name);
});

let capabilities = GooseMcpClientCapabilities {
mcpui: self.capabilities.mcpui,
};
let client = child_process_client(
command,
timeout,
self.provider.clone(),
Some(&effective_working_dir),
Some(container_id.to_string()),
self.client_name.clone(),
capabilities,
)
.await?;
Box::new(client)
} else {
let (server_read, client_write) = tokio::io::duplex(65536);
let (client_read, server_write) = tokio::io::duplex(65536);
extension_fn(server_read, server_write);
let capabilities = GooseMcpClientCapabilities {
mcpui: self.capabilities.mcpui,
};
Box::new(
McpClient::connect(
(client_read, client_write),
timeout_duration,
self.provider.clone(),
self.client_name.clone(),
capabilities,
)
.await?,
)
Expand Down Expand Up @@ -668,12 +724,18 @@ impl ExtensionManager {
command.arg("python").arg(file_path.to_str().unwrap());
});

let capabilities = GooseMcpClientCapabilities {
mcpui: self.capabilities.mcpui,
};

let client = child_process_client(
command,
timeout,
self.provider.clone(),
Some(&effective_working_dir),
container.map(|c| c.id().to_string()),
self.client_name.clone(),
capabilities,
)
.await?;

Expand Down
Loading