use rmcp::handler::server::wrapper::Parameters;
use rmcp::model::ErrorData;
use rmcp::{tool, tool_router};
use crate::commands;
use super::params::*;
use super::server::{SaraServer, mcp_err, ok_json};
#[tool_router(router = read_router, vis = "pub(crate)")]
impl SaraServer {
#[tool(description = "List pending tasks for a project (or all projects).")]
fn list(&self, Parameters(p): Parameters<ListParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp list", |conn, cfg| {
commands::list::list_value(conn, cfg, p.all.unwrap_or(false), p.project.as_deref())
})
.map_err(mcp_err)?;
ok_json(v)
}
#[tool(
description = "Full task guide as JSON: description, steps, acceptance, notes, links, freshness, open feedback."
)]
fn info(&self, Parameters(p): Parameters<IdParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp info", |conn, _cfg| {
commands::info::guide_value(conn, &p.id)
})
.map_err(mcp_err)?;
ok_json(v)
}
#[tool(description = "The execution cursor: the first not-done step of a task.")]
fn next(&self, Parameters(p): Parameters<IdParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp next", |conn, _cfg| {
commands::guide::next_value(conn, &p.id)
})
.map_err(mcp_err)?;
ok_json(v)
}
#[tool(description = "Ordered steps of a task (optionally only up to step `until`).")]
fn steps(&self, Parameters(p): Parameters<StepsParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp steps", |conn, _cfg| {
commands::guide::steps_value(conn, &p.id, p.until)
})
.map_err(mcp_err)?;
ok_json(v)
}
#[tool(
description = "Read-only: the verification commands + acceptance criteria for a task (does NOT run them)."
)]
fn verify(&self, Parameters(p): Parameters<VerifyParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp verify", |conn, _cfg| {
commands::guide::verify_value(conn, &p.id, p.step)
})
.map_err(mcp_err)?;
ok_json(v)
}
#[tool(description = "Cross-task keyword search over descriptions, notes, and code anchors.")]
fn recall(&self, Parameters(p): Parameters<RecallParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp recall", |conn, cfg| {
commands::recall::recall_value(conn, cfg, &p.query, p.limit.unwrap_or(20))
})
.map_err(mcp_err)?;
ok_json(v)
}
#[tool(description = "List a task's open human feedback (items awaiting a response).")]
fn feedback(&self, Parameters(p): Parameters<IdParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp feedback", |conn, _cfg| {
commands::guide::feedback_value(conn, &p.id)
})
.map_err(mcp_err)?;
ok_json(v)
}
#[tool(
description = "Dependency-ordered briefing for a task: each task's full guide in dependency order (the task plus everything it is blocked by)."
)]
fn plan_show(&self, Parameters(p): Parameters<IdParams>) -> Result<String, ErrorData> {
let v = self
.with_project(p.project_path.as_deref(), "mcp plan_show", |conn, _cfg| {
commands::plan::show_value(conn, &p.id)
})
.map_err(mcp_err)?;
ok_json(v)
}
}