diff --git a/crates/goose-cli/src/commands/configure.rs b/crates/goose-cli/src/commands/configure.rs index 99298f433..b97c08ab7 100644 --- a/crates/goose-cli/src/commands/configure.rs +++ b/crates/goose-cli/src/commands/configure.rs @@ -103,20 +103,9 @@ pub async fn handle_configure( .interact()? }; - // Forward any existing systems from the profile if present - let additional_systems = - existing_profile.map_or(Vec::new(), |profile| profile.additional_systems.clone()); - - if !additional_systems.is_empty() { - let _ = cliclack::log::info( - format!("We kept the existing systems from your {} profile. You can edit this with `goose system`", profile_name) - ); - } - let profile = Profile { provider: provider_name.to_string(), model: model.clone(), - additional_systems, temperature: None, context_limit: None, max_tokens: None, diff --git a/crates/goose-cli/src/main.rs b/crates/goose-cli/src/main.rs index 833217527..daa6e4e4f 100644 --- a/crates/goose-cli/src/main.rs +++ b/crates/goose-cli/src/main.rs @@ -8,7 +8,6 @@ mod logging; mod profile; mod prompt; mod session; -mod systems; use commands::agent_version::AgentCommand; use commands::configure::handle_configure; @@ -22,8 +21,6 @@ use std::io::{self, Read}; #[cfg(test)] mod test_helpers; -use crate::systems::system_handler::{add_system, remove_system}; - #[derive(Parser)] #[command(author, about, long_about = None)] struct Cli { @@ -67,13 +64,6 @@ enum Command { model: Option, }, - /// Manage system prompts and behaviors - #[command(about = "Manage the systems that goose can operate")] - System { - #[command(subcommand)] - action: SystemCommands, - }, - /// Manage system prompts and behaviors #[command(about = "Run one of the mcp servers bundled with goose")] Mcp { name: String }, @@ -187,29 +177,6 @@ enum Command { Agents(AgentCommand), } -#[derive(Subcommand)] -enum SystemCommands { - /// Add a new system prompt - #[command(about = "Add a new system prompt from URL")] - Add { - #[arg( - help = "URL of the system prompt to add", - long_help = "URL pointing to a file containing the system prompt to be added." - )] - url: String, - }, - - /// Remove an existing system prompt - #[command(about = "Remove an existing system prompt")] - Remove { - #[arg( - help = "URL of the system prompt to remove", - long_help = "URL of the system prompt that should be removed from the configuration." - )] - url: String, - }, -} - #[derive(clap::ValueEnum, Clone, Debug)] enum CliProviderVariant { OpenAi, @@ -235,16 +202,6 @@ async fn main() -> Result<()> { let _ = handle_configure(profile_name, provider, model).await; return Ok(()); } - Some(Command::System { action }) => match action { - SystemCommands::Add { url } => { - add_system(url).await.unwrap(); - return Ok(()); - } - SystemCommands::Remove { url } => { - remove_system(url).await.unwrap(); - return Ok(()); - } - }, Some(Command::Mcp { name }) => { let _ = run_server(&name).await; } diff --git a/crates/goose-cli/src/profile.rs b/crates/goose-cli/src/profile.rs index d62dfc5b8..78494b4b1 100644 --- a/crates/goose-cli/src/profile.rs +++ b/crates/goose-cli/src/profile.rs @@ -14,8 +14,6 @@ use std::path::PathBuf; pub struct Profile { pub provider: String, pub model: String, - #[serde(default)] - pub additional_systems: Vec, pub temperature: Option, pub context_limit: Option, pub max_tokens: Option, @@ -27,12 +25,6 @@ pub struct Profiles { pub profile_items: HashMap, } -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct AdditionalSystem { - pub name: String, - pub location: String, -} - pub fn profile_path() -> Result { let home_dir = dirs::home_dir().ok_or(anyhow::anyhow!("Could not determine home directory"))?; let config_dir = home_dir.join(".config").join("goose"); diff --git a/crates/goose-cli/src/systems/mod.rs b/crates/goose-cli/src/systems/mod.rs deleted file mode 100644 index 0d60bb123..000000000 --- a/crates/goose-cli/src/systems/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod system_handler; diff --git a/crates/goose-cli/src/systems/system_handler.rs b/crates/goose-cli/src/systems/system_handler.rs deleted file mode 100644 index 3fd9eac5b..000000000 --- a/crates/goose-cli/src/systems/system_handler.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::profile::{load_profiles, save_profile, AdditionalSystem}; -use serde_json::Value; -use std::error::Error; - -pub async fn fetch_system(url: &str) -> Result, Box> { - let full_url = format!("{}/fetch_name", url); - match reqwest::get(full_url).await { - Ok(response) => match response.json::().await { - Ok(json) => { - if let Some(name) = json.get("name").and_then(|n| n.as_str()) { - return Ok(Some(name.to_string())); - } else { - println!("No 'name' field in the JSON response."); - } - } - Err(err) => { - println!("Failed to parse JSON: {}", err); - } - }, - Err(err) => { - println!("Failed to fetch URL: {}", err); - } - } - Ok(None) -} - -pub async fn add_system(url: String) -> Result<(), Box> { - let system_name = fetch_system(url.as_str()).await?; - if system_name.is_none() { - println!("System not found. Please enter a valid system location."); - return Ok(()); - } - let system_name = system_name.unwrap(); - match load_profiles() { - Ok(mut profiles) => { - if profiles.is_empty() { - println!("No profiles found. Please create a profile first via goose configure"); - return Ok(()); - } - for (profile_name, profile) in profiles.iter_mut() { - if profile.additional_systems.iter().any(|s| s.location == url) { - continue; - } - profile.additional_systems.push(AdditionalSystem { - name: system_name.to_string(), - location: url.to_string(), - }); - save_profile(profile_name, profile.clone())? - } - println!( - "System '{}' at '{}' added to all profiles", - system_name, url - ); - Ok(()) - } - Err(err) => { - println!("Failed to load profiles: {}", err); - Ok(()) - } - } -} - -pub async fn remove_system(url: String) -> Result<(), Box> { - let mut removed = false; - match load_profiles() { - Ok(mut profiles) => { - if profiles.is_empty() { - return Ok(()); - } - for (profile_name, profile) in profiles.iter_mut() { - if let Some(pos) = profile - .additional_systems - .iter() - .position(|s| s.location == url) - { - profile.additional_systems.remove(pos); - save_profile(profile_name, profile.clone())?; - removed = true; - } - } - if removed { - println!("System at '{}' has been removed from all profiles", url); - } else { - println!("System at '{}' not found in any profiles", url); - } - Ok(()) - } - Err(err) => { - println!("Failed to load profiles: {}", err); - Ok(()) - } - } -} diff --git a/crates/goose/src/agents/capabilities.rs b/crates/goose/src/agents/capabilities.rs index 4007896ac..5a2f70b1a 100644 --- a/crates/goose/src/agents/capabilities.rs +++ b/crates/goose/src/agents/capabilities.rs @@ -92,8 +92,8 @@ impl Capabilities { let transport = SseTransport::new(uri); McpClient::new(transport.start().await?) } - SystemConfig::Stdio { ref cmd, ref args } => { - let transport = StdioTransport::new(cmd, args.to_vec()); + SystemConfig::Stdio { ref cmd, ref args, ref env } => { + let transport = StdioTransport::new(cmd, args.to_vec()).with_env(env.clone()); McpClient::new(transport.start().await?) } }; diff --git a/crates/goose/src/agents/system.rs b/crates/goose/src/agents/system.rs index 022064a6a..9e69c88c3 100644 --- a/crates/goose/src/agents/system.rs +++ b/crates/goose/src/agents/system.rs @@ -1,5 +1,6 @@ use mcp_client::client::Error as ClientError; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use thiserror::Error; /// Errors from System operation @@ -22,7 +23,11 @@ pub enum SystemConfig { /// Server-sent events client with a URI endpoint Sse { uri: String }, /// Standard I/O client with command and arguments - Stdio { cmd: String, args: Vec }, + Stdio { + cmd: String, + args: Vec, + env: Option>, + }, } impl SystemConfig { @@ -34,6 +39,7 @@ impl SystemConfig { Self::Stdio { cmd: cmd.into(), args: vec![], + env: None, } } @@ -43,9 +49,26 @@ impl SystemConfig { S: Into, { match self { - Self::Stdio { cmd, .. } => Self::Stdio { + Self::Stdio { cmd, env, .. } => Self::Stdio { cmd, args: args.into_iter().map(Into::into).collect(), + env, + }, + other => other, + } + } + + pub fn with_env(self, env_vars: I) -> Self + where + I: IntoIterator, + K: Into, + V: Into, + { + match self { + Self::Stdio { cmd, args, .. } => Self::Stdio { + cmd, + args, + env: Some(env_vars.into_iter().map(|(k, v)| (k.into(), v.into())).collect()), }, other => other, } @@ -56,7 +79,12 @@ impl std::fmt::Display for SystemConfig { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SystemConfig::Sse { uri } => write!(f, "SSE({})", uri), - SystemConfig::Stdio { cmd, args } => write!(f, "Stdio({} {})", cmd, args.join(" ")), + SystemConfig::Stdio { cmd, args, env } => { + let env_str = env.as_ref().map_or(String::new(), |e| { + format!(" with env: {}", e.iter().map(|(k,v)| format!("{}={}", k, v)).collect::>().join(",")) + }); + write!(f, "Stdio({} {}{})", cmd, args.join(" "), env_str) + }, } } } diff --git a/crates/mcp-client/src/transport/stdio.rs b/crates/mcp-client/src/transport/stdio.rs index c983342bb..bb27b96e5 100644 --- a/crates/mcp-client/src/transport/stdio.rs +++ b/crates/mcp-client/src/transport/stdio.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::sync::Arc; use tokio::process::{Child, ChildStdin, ChildStdout, Command}; @@ -103,6 +104,7 @@ impl StdioActor { pub struct StdioTransport { command: String, args: Vec, + env: Option>, } impl StdioTransport { @@ -110,18 +112,31 @@ impl StdioTransport { Self { command: command.into(), args, + env: None, } } + pub fn with_env(mut self, env: Option>) -> Self { + self.env = env; + self + } + async fn spawn_process(&self) -> Result<(Child, ChildStdin, ChildStdout), Error> { - let mut process = Command::new(&self.command) + let mut command = Command::new(&self.command); + command .args(&self.args) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::inherit()) .kill_on_drop(true) // 0 sets the process group ID equal to the process ID - .process_group(0) // don't inherit signal handling from parent process + .process_group(0); // don't inherit signal handling from parent process + + if let Some(env) = &self.env { + command.envs(env); + } + + let mut process = command .spawn() .map_err(|e| Error::Other(e.to_string()))?;