Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: secrets configuration for mcp server #565

Open
wants to merge 3 commits into
base: v1.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 9 additions & 2 deletions crates/goose/src/agents/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,19 @@ impl Capabilities {
/// Add a new MCP system based on the provided client type
// TODO IMPORTANT need to ensure this times out if the system command is broken!
pub async fn add_system(&mut self, config: SystemConfig) -> SystemResult<()> {
// If secrets exist, set them as environment variables
if let Some(secrets) = config.secrets() {
secrets.set_environment_vars();
};

let mut client: McpClient = match config {
SystemConfig::Sse { ref uri } => {
SystemConfig::Sse { ref uri, .. } => {
let transport = SseTransport::new(uri);
McpClient::new(transport.start().await?)
}
SystemConfig::Stdio { ref cmd, ref args } => {
SystemConfig::Stdio {
ref cmd, ref args, ..
} => {
let transport = StdioTransport::new(cmd, args.to_vec());
McpClient::new(transport.start().await?)
}
Expand Down
54 changes: 48 additions & 6 deletions crates/goose/src/agents/system.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use mcp_client::client::Error as ClientError;
use serde::{Deserialize, Serialize};
use thiserror::Error;
Expand All @@ -15,25 +17,56 @@ pub enum SystemError {

pub type SystemResult<T> = Result<T, SystemError>;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Secrets {
/// A map of environment variables to set, e.g. API_KEY -> some_secret
Copy link
Collaborator Author

@salman1993 salman1993 Jan 9, 2025

Choose a reason for hiding this comment

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

this right now stores the secret in plaintext. this can also be a map of env_var_name -> key_manager_secret_name and then we pull in the secret value and set it as env var

pub fn get_keyring_secret(

#[serde(default)]
#[serde(flatten)]
map: HashMap<String, String>,
}

impl Secrets {
pub fn new(map: HashMap<String, String>) -> Self {
Self { map }
}

pub fn set_environment_vars(&self) {
for (key, value) in &self.map {
std::env::set_var(key, value);
Copy link
Collaborator

Choose a reason for hiding this comment

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

this looks like it sets the env vars for the main goose process, should we only be setting the secrets in for the subprocess when doing stdio mcps servers?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is a good question. there is a TODO on adding in-process transport. for now, it makes sense to set it in only in subprocess -

// TODO once the client/server for MCP has stabilized, we should probably add InProcess transport to each

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated this to be set envs in the spawned process

}
}
}

/// Represents the different types of MCP systems that can be added to the manager
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type")]
pub enum SystemConfig {
/// Server-sent events client with a URI endpoint
Sse { uri: String },
Sse {
uri: String,
secrets: Option<Secrets>,
},
/// Standard I/O client with command and arguments
Stdio { cmd: String, args: Vec<String> },
Stdio {
cmd: String,
args: Vec<String>,
secrets: Option<Secrets>,
},
}

impl SystemConfig {
pub fn sse<S: Into<String>>(uri: S) -> Self {
Self::Sse { uri: uri.into() }
Self::Sse {
uri: uri.into(),
secrets: None,
}
}

pub fn stdio<S: Into<String>>(cmd: S) -> Self {
Self::Stdio {
cmd: cmd.into(),
args: vec![],
secrets: None,
}
}

Expand All @@ -43,20 +76,29 @@ impl SystemConfig {
S: Into<String>,
{
match self {
Self::Stdio { cmd, .. } => Self::Stdio {
Self::Stdio { cmd, secrets, .. } => Self::Stdio {
cmd,
secrets,
args: args.into_iter().map(Into::into).collect(),
},
other => other,
}
}

/// Returns a reference to the secrets in this config, if any
pub fn secrets(&self) -> Option<&Secrets> {
match self {
Self::Sse { secrets, .. } => secrets.as_ref(),
Self::Stdio { secrets, .. } => secrets.as_ref(),
}
}
}

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::Sse { uri, .. } => write!(f, "SSE({})", uri),
SystemConfig::Stdio { cmd, args, .. } => write!(f, "Stdio({} {})", cmd, args.join(" ")),
}
}
}
Expand Down
Loading