Skip to content
Closed
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion crates/goose-cli/src/commands/mcp.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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,
};
Expand Down
1 change: 1 addition & 0 deletions crates/goose-mcp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ description.workspace = true
workspace = true

[dependencies]
goose = { path = "../goose" }
Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah this doesn't seem ideal, but at least it is in right direction

mcp-core = { path = "../mcp-core" }
mcp-server = { path = "../mcp-server" }
rmcp = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions crates/goose-mcp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
134 changes: 134 additions & 0 deletions crates/goose-mcp/src/todo/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Tool>,
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
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think if we are moving the prompt here we need to make it clear similar to the old system.md one how to do it, including if we want it to use subagents at times (otherwise I found it will not) - https://github.com/block/goose/pull/4421/files#diff-507a51de1f94529d2674a027f5bf6e5eeed83ccfe9cbbe6cea674b75aef65ee8 like that

Copy link
Collaborator Author

@tlongwell-block tlongwell-block Sep 1, 2025

Choose a reason for hiding this comment

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

- [ ] 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<Tool> {
self.tools.clone()
}

fn call_tool(
&self,
_tool_name: &str,
_arguments: Value,
_notifier: mpsc::Sender<JsonRpcMessage>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Content>, 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<Resource> {
Vec::new()
}

fn read_resource(
&self,
_uri: &str,
) -> Pin<Box<dyn Future<Output = Result<String, ResourceError>> + Send + 'static>> {
Box::pin(async move { Ok(String::new()) })
}

fn list_prompts(&self) -> Vec<Prompt> {
Vec::new()
}

fn get_prompt(
&self,
_prompt_name: &str,
) -> Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>> {
Box::pin(async move {
Err(PromptError::NotFound(
"TODO extension has no prompts".to_string(),
))
})
}
}
17 changes: 0 additions & 17 deletions crates/goose/src/prompts/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading