Skip to content

Commit

Permalink
Combine named agents and MCP support
Browse files Browse the repository at this point in the history
Co-authored-by: Zaki Ali <[email protected]>
Co-authored-by: Salman Mohammed <[email protected]>
Co-authored-by: Kalvin Chau <[email protected]>
  • Loading branch information
3 people authored and baxen committed Jan 6, 2025
1 parent a7a8aee commit eb82d32
Show file tree
Hide file tree
Showing 51 changed files with 1,189 additions and 3,940 deletions.
990 changes: 477 additions & 513 deletions crates/goose/src/developer.rs → crates/developer/src/lib.rs

Large diffs are not rendered by default.

1,193 changes: 4 additions & 1,189 deletions crates/developer/src/main.rs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions crates/goose-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ path = "src/main.rs"

[dependencies]
goose = { path = "../goose" }
developer = { path = "../developer" }
mcp-client = { path = "../mcp-client" }
mcp-server = { path = "../mcp-server" }
mcp-core = { path = "../mcp-core" }
clap = { version = "4.4", features = ["derive"] }
cliclack = "0.3.5"
Expand Down Expand Up @@ -47,6 +50,7 @@ async-trait = "0.1"
rustyline = "15.0.0"
rust_decimal = "1.36.0"
rust_decimal_macros = "1.36.0"
tracing = "0.1.41"

[dev-dependencies]
tempfile = "3"
Expand Down
63 changes: 0 additions & 63 deletions crates/goose-cli/src/agents/mock_agent.rs

This file was deleted.

2 changes: 0 additions & 2 deletions crates/goose-cli/src/agents/mod.rs

This file was deleted.

1 change: 1 addition & 0 deletions crates/goose-cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod agent_version;
pub mod configure;
pub mod server;
pub mod session;
pub mod version;
21 changes: 21 additions & 0 deletions crates/goose-cli/src/commands/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use anyhow::Result;
use developer::DeveloperRouter;
use mcp_server::router::RouterService;
use mcp_server::{ByteTransport, Server};
use tokio::io::{stdin, stdout};

pub async fn run_server(name: &str) -> Result<()> {
tracing::info!("Starting MCP server");

let router = match name {
"developer" => Some(RouterService(DeveloperRouter::new())),
_ => None,
};

// Create and run the server
let server = Server::new(router.unwrap_or_else(|| panic!("Unknown server requested {}", name)));
let transport = ByteTransport::new(stdin(), stdout());

tracing::info!("Server initialized and ready to handle requests");
Ok(server.run(transport).await?)
}
65 changes: 17 additions & 48 deletions crates/goose-cli/src/commands/session.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use console::style;
use goose::agents::AgentFactory;
use goose::agents::SystemConfig;
use goose::providers::factory;
use rand::{distributions::Alphanumeric, Rng};
use std::path::{Path, PathBuf};
Expand All @@ -10,7 +11,7 @@ use crate::prompt::rustyline::RustylinePrompt;
use crate::prompt::Prompt;
use crate::session::{ensure_session_dir, get_most_recent_session, Session};

pub fn build_session<'a>(
pub async fn build_session<'a>(
session: Option<String>,
profile: Option<String>,
agent_version: Option<String>,
Expand Down Expand Up @@ -44,9 +45,22 @@ pub fn build_session<'a>(

let provider_config = get_provider_config(&loaded_profile.provider, (*loaded_profile).clone());

// TODO: Odd to be prepping the provider rather than having that done in the agent?
let provider = factory::get_provider(provider_config).unwrap();
let agent = AgentFactory::create(agent_version.as_deref().unwrap_or("default"), provider).unwrap();

let mut agent =
AgentFactory::create(agent_version.as_deref().unwrap_or("default"), provider).unwrap();

// We now add systems to the session based on configuration
// TODO update the profile system tracking
// TODO use systems from the profile
// TODO once the client/server for MCP has stabilized, we should probably add InProcess transport to each
// and avoid spawning here. But it is at least included in the CLI for portability
let config = SystemConfig::stdio("goose").with_args(vec!["server", "--name", "developer"]);
agent
.add_system(config)
.await
.expect("should start developer server");

let prompt = match std::env::var("GOOSE_INPUT") {
Ok(val) => match val.as_str() {
"rustyline" => Box::new(RustylinePrompt::new()) as Box<dyn Prompt>,
Expand Down Expand Up @@ -161,48 +175,3 @@ fn display_session_info(resume: bool, provider: String, model: String, session_f
style(session_file.display()).dim().cyan(),
);
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::run_with_tmp_dir;
use std::fs;
use std::thread;
use std::time::Duration;

#[test]
#[should_panic(expected = "Cannot resume session: file")]
fn test_resume_nonexistent_session_panics() {
run_with_tmp_dir(|| {
build_session(Some("nonexistent-session".to_string()), None, None, true);
})
}

#[test]
fn test_resume_most_recent_session() -> anyhow::Result<()> {
run_with_tmp_dir(|| {
let session_dir = ensure_session_dir()?;
// Create test session files with different timestamps
let file1_path = session_dir.join("session1.jsonl");
let file2_path = session_dir.join("session2.jsonl");

fs::write(&file1_path, "{}")?;
thread::sleep(Duration::from_millis(1));
fs::write(&file2_path, "{}")?;

// Test resuming without a session name
let session = build_session(None, None, None, true);
assert_eq!(session.session_file().as_path(), file2_path.as_path());

Ok(())
})
}

#[test]
#[should_panic(expected = "No session files found")]
fn test_resume_most_recent_session_no_files() {
run_with_tmp_dir(|| {
build_session(None, None, None, true);
});
}
}
22 changes: 19 additions & 3 deletions crates/goose-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use anyhow::Result;
use clap::{Parser, Subcommand};
use goose::agents::AgentFactory;

mod agents;
mod commands;
mod log_usage;
mod profile;
Expand All @@ -12,6 +11,7 @@ mod systems;

use commands::agent_version::AgentCommand;
use commands::configure::handle_configure;
use commands::server::run_server;
use commands::session::build_session;
use commands::version::print_version;
use profile::has_no_profiles;
Expand Down Expand Up @@ -72,6 +72,19 @@ enum Command {
action: SystemCommands,
},

/// Manage system prompts and behaviors
#[command(about = "Manage the systems that goose can operate")]
Server {
#[arg(
short,
long,
value_name = "NAME",
help = "The stdio server to run",
long_help = "Runs one of the goose builtin MCP servers with stdio transport."
)]
name: String,
},

/// Start or resume interactive chat sessions
#[command(about = "Start or resume interactive chat sessions", alias = "s")]
Session {
Expand Down Expand Up @@ -239,6 +252,9 @@ async fn main() -> Result<()> {
return Ok(());
}
},
Some(Command::Server { name }) => {
let _ = run_server(&name).await;
}
Some(Command::Session {
name,
profile,
Expand All @@ -260,7 +276,7 @@ async fn main() -> Result<()> {
}
}

let mut session = build_session(name, profile, agent, resume);
let mut session = build_session(name, profile, agent, resume).await;
let _ = session.start().await;
return Ok(());
}
Expand Down Expand Up @@ -299,7 +315,7 @@ async fn main() -> Result<()> {
.expect("Failed to read from stdin");
stdin
};
let mut session = build_session(name, profile, agent, resume);
let mut session = build_session(name, profile, agent, resume).await;
let _ = session.headless_start(contents.clone()).await;
return Ok(());
}
Expand Down
3 changes: 0 additions & 3 deletions crates/goose-cli/src/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ pub trait Prompt {
println!("Goose is running! Enter your instructions, or try asking what goose can do.");
println!("\n");
}
// Used for testing. Allows us to downcast to any type.
#[cfg(test)]
fn as_any(&self) -> &dyn std::any::Any;
}

pub struct Input {
Expand Down
5 changes: 0 additions & 5 deletions crates/goose-cli/src/prompt/rustyline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,4 @@ impl Prompt for RustylinePrompt {
fn close(&self) {
// No cleanup required
}

#[cfg(test)]
fn as_any(&self) -> &dyn std::any::Any {
panic!("Not implemented");
}
}
Loading

0 comments on commit eb82d32

Please sign in to comment.