diff --git a/Cargo.lock b/Cargo.lock index f77b5dbff72c..814153e90424 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2703,6 +2703,7 @@ dependencies = [ "docx-rs", "etcetera", "glob", + "goose", "http-body-util", "hyper 1.6.0", "ignore", diff --git a/crates/goose-cli/src/commands/mcp.rs b/crates/goose-cli/src/commands/mcp.rs index 0db83f3efc0d..038e4e0a5215 100644 --- a/crates/goose-cli/src/commands/mcp.rs +++ b/crates/goose-cli/src/commands/mcp.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Result}; use goose_mcp::{ - AutoVisualiserRouter, ComputerControllerRouter, DeveloperRouter, MemoryRouter, TutorialRouter, + AutoVisualiserRouter, ComputerControllerRouter, DeveloperRouter, MemoryRouter, TodoRouter, + TutorialRouter, }; use mcp_server::router::RouterService; use mcp_server::{BoundedService, ByteTransport, Server}; @@ -32,6 +33,7 @@ pub async fn run_server(name: &str) -> Result<()> { "computercontroller" => Some(Box::new(RouterService(ComputerControllerRouter::new()))), "autovisualiser" => Some(Box::new(RouterService(AutoVisualiserRouter::new()))), "memory" => Some(Box::new(RouterService(MemoryRouter::new()))), + "todo" => Some(Box::new(RouterService(TodoRouter::new()))), "tutorial" => Some(Box::new(RouterService(TutorialRouter::new()))), _ => None, }; diff --git a/crates/goose-mcp/Cargo.toml b/crates/goose-mcp/Cargo.toml index feb195ded449..1da16e8be565 100644 --- a/crates/goose-mcp/Cargo.toml +++ b/crates/goose-mcp/Cargo.toml @@ -11,6 +11,7 @@ description.workspace = true workspace = true [dependencies] +goose = { path = "../goose" } mcp-core = { path = "../mcp-core" } mcp-server = { path = "../mcp-server" } rmcp = { workspace = true } diff --git a/crates/goose-mcp/src/lib.rs b/crates/goose-mcp/src/lib.rs index a57fe810caa5..57b9c205216c 100644 --- a/crates/goose-mcp/src/lib.rs +++ b/crates/goose-mcp/src/lib.rs @@ -11,10 +11,12 @@ pub mod autovisualiser; pub mod computercontroller; mod developer; mod memory; +mod todo; mod tutorial; pub use autovisualiser::AutoVisualiserRouter; pub use computercontroller::ComputerControllerRouter; pub use developer::DeveloperRouter; pub use memory::MemoryRouter; +pub use todo::TodoRouter; pub use tutorial::TutorialRouter; diff --git a/crates/goose-mcp/src/todo/mod.rs b/crates/goose-mcp/src/todo/mod.rs new file mode 100644 index 000000000000..ef9729704b96 --- /dev/null +++ b/crates/goose-mcp/src/todo/mod.rs @@ -0,0 +1,134 @@ +use std::future::Future; +use std::pin::Pin; + +use mcp_core::handler::{PromptError, ResourceError}; +use mcp_core::protocol::ServerCapabilities; +use mcp_server::router::CapabilitiesBuilder; +use mcp_server::Router; +use rmcp::model::{Content, ErrorCode, ErrorData, JsonRpcMessage, Prompt, Resource, Tool}; +use serde_json::Value; +use tokio::sync::mpsc; + +// Import the TODO tools +use goose::agents::todo_tools::{todo_read_tool, todo_write_tool}; + +/// A lightweight router that provides TODO task management capabilities. +/// +/// This extension acts as a metadata provider - it exposes the TODO tools +/// and provides instructions, but the actual execution remains in the agent +/// for session storage access. +#[derive(Clone)] +pub struct TodoRouter { + tools: Vec, + instructions: String, +} + +impl Default for TodoRouter { + fn default() -> Self { + Self::new() + } +} + +impl TodoRouter { + pub fn new() -> Self { + let instructions = r#"The todo extension provides persistent task management throughout your session. + +These tools help you track multi-step work, maintain context between interactions, and ensure systematic task completion. + +## Task Management Guidelines + +**Required Usage:** +- Use `todo__read` and `todo__write` for any task with 2+ steps, multiple files/components, or uncertain scope +- Skipping these tools when needed is considered an error + +**Workflow:** +1. Start: Always `todo__read` first, then `todo__write` a brief checklist using Markdown checkboxes +2. During: After each major action, reread via `todo__read`, then update via `todo__write` - mark completed items, add new discoveries, note blockers +3. Finish: Ensure every item is checked, or clearly list what remains + +**Critical:** `todo__write` replaces the entire list. Always read before writing - not doing so is an error. + +**Best Practices:** +- Keep items short, specific, and action-oriented +- Use nested checkboxes for subtasks +- Include context about blockers or dependencies + +Example format: +```markdown +- [x] Analyze request fully +- [ ] Create implementation plan + - [x] General guidelines + - [ ] Sample work +- [ ] Begin on implementation plan +```"#; + + Self { + tools: vec![todo_read_tool(), todo_write_tool()], + instructions: instructions.to_string(), + } + } +} + +impl Router for TodoRouter { + fn name(&self) -> String { + "todo".to_string() + } + + fn instructions(&self) -> String { + self.instructions.clone() + } + + fn capabilities(&self) -> ServerCapabilities { + CapabilitiesBuilder::new() + .with_tools(false) + .with_prompts(false) + .build() + } + + fn list_tools(&self) -> Vec { + self.tools.clone() + } + + fn call_tool( + &self, + _tool_name: &str, + _arguments: Value, + _notifier: mpsc::Sender, + ) -> Pin, ErrorData>> + Send + 'static>> { + // The agent handles TODO tool execution directly for session access. + // This router only provides metadata and tool definitions. + Box::pin(async move { + Err(ErrorData::new( + ErrorCode::METHOD_NOT_FOUND, + "TODO tools are executed directly by the agent".to_string(), + None, + )) + }) + } + + fn list_resources(&self) -> Vec { + Vec::new() + } + + fn read_resource( + &self, + _uri: &str, + ) -> Pin> + Send + 'static>> { + Box::pin(async move { Ok(String::new()) }) + } + + fn list_prompts(&self) -> Vec { + Vec::new() + } + + fn get_prompt( + &self, + _prompt_name: &str, + ) -> Pin> + Send + 'static>> { + Box::pin(async move { + Err(PromptError::NotFound( + "TODO extension has no prompts".to_string(), + )) + }) + } +} diff --git a/crates/goose/src/prompts/system.md b/crates/goose/src/prompts/system.md index 1bcf22993c72..2681696a0dbc 100644 --- a/crates/goose/src/prompts/system.md +++ b/crates/goose/src/prompts/system.md @@ -38,23 +38,6 @@ No extensions are defined. You should let the user know that they should add ext {{tool_selection_strategy}} -# Task Management - -- Required — use `todo__read` and `todo__write` for any task with 2+ steps, multiple files/components, or uncertain scope. Skipping them is an error. -- Start — `todo__read`, then `todo__write` a brief checklist (Markdown checkboxes). -- During — after each major action, update via `todo__write`: mark done, add/edit items, note blockers/dependencies. -- Finish — ensure every item is checked, or clearly list what remains. -- Overwrite warning — `todo__write` replaces the entire list; always read before writing. It is an error to not read before writing. -- Quality — keep items short, specific, and action‑oriented. - -Template: -```markdown -- [ ] Implement feature X - - [ ] Update API - - [ ] Write tests -- [ ] Blocked: waiting on credentials -``` - # Response Guidelines - Use Markdown formatting for all responses.