2026-04-27 23:26:40 +00:00
|
|
|
//! `tools/list` MCP method — returns the static schema for every tool the server exposes.
|
|
|
|
|
|
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
|
|
|
|
|
|
use super::JsonRpcResponse;
|
|
|
|
|
|
|
|
|
|
mod agent_tools;
|
|
|
|
|
mod story_tools;
|
|
|
|
|
mod system_tools;
|
|
|
|
|
|
|
|
|
|
pub(super) fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
|
|
|
|
let mut tools = Vec::new();
|
|
|
|
|
tools.extend(story_tools::story_tools());
|
|
|
|
|
tools.extend(agent_tools::agent_tools());
|
|
|
|
|
tools.extend(system_tools::system_tools());
|
|
|
|
|
JsonRpcResponse::success(id, json!({ "tools": tools }))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn tools_list_returns_all_tools() {
|
|
|
|
|
let resp = handle_tools_list(Some(json!(2)));
|
|
|
|
|
let result = resp.result.unwrap();
|
|
|
|
|
let tools = result["tools"].as_array().unwrap();
|
|
|
|
|
let names: Vec<&str> = tools.iter().map(|t| t["name"].as_str().unwrap()).collect();
|
|
|
|
|
assert!(names.contains(&"create_story"));
|
|
|
|
|
assert!(names.contains(&"validate_stories"));
|
|
|
|
|
assert!(names.contains(&"list_upcoming"));
|
|
|
|
|
assert!(names.contains(&"get_story_todos"));
|
|
|
|
|
assert!(names.contains(&"record_tests"));
|
|
|
|
|
assert!(names.contains(&"ensure_acceptance"));
|
|
|
|
|
assert!(names.contains(&"start_agent"));
|
|
|
|
|
assert!(names.contains(&"stop_agent"));
|
|
|
|
|
assert!(names.contains(&"list_agents"));
|
|
|
|
|
assert!(names.contains(&"get_agent_config"));
|
|
|
|
|
assert!(names.contains(&"reload_agent_config"));
|
|
|
|
|
assert!(names.contains(&"get_agent_output"));
|
|
|
|
|
assert!(names.contains(&"wait_for_agent"));
|
|
|
|
|
assert!(names.contains(&"get_agent_remaining_turns_and_budget"));
|
|
|
|
|
assert!(names.contains(&"create_worktree"));
|
|
|
|
|
assert!(names.contains(&"list_worktrees"));
|
|
|
|
|
assert!(names.contains(&"remove_worktree"));
|
|
|
|
|
assert!(names.contains(&"get_editor_command"));
|
|
|
|
|
assert!(!names.contains(&"report_completion"));
|
|
|
|
|
assert!(names.contains(&"accept_story"));
|
|
|
|
|
assert!(names.contains(&"check_criterion"));
|
|
|
|
|
assert!(names.contains(&"add_criterion"));
|
|
|
|
|
assert!(names.contains(&"update_story"));
|
|
|
|
|
assert!(names.contains(&"create_spike"));
|
|
|
|
|
assert!(names.contains(&"create_bug"));
|
|
|
|
|
assert!(names.contains(&"list_bugs"));
|
|
|
|
|
assert!(names.contains(&"close_bug"));
|
|
|
|
|
assert!(names.contains(&"create_refactor"));
|
|
|
|
|
assert!(names.contains(&"list_refactors"));
|
|
|
|
|
assert!(names.contains(&"merge_agent_work"));
|
|
|
|
|
assert!(names.contains(&"get_merge_status"));
|
|
|
|
|
assert!(names.contains(&"move_story_to_merge"));
|
|
|
|
|
assert!(names.contains(&"report_merge_failure"));
|
|
|
|
|
assert!(names.contains(&"request_qa"));
|
|
|
|
|
assert!(names.contains(&"approve_qa"));
|
|
|
|
|
assert!(names.contains(&"reject_qa"));
|
|
|
|
|
assert!(names.contains(&"launch_qa_app"));
|
|
|
|
|
assert!(names.contains(&"get_server_logs"));
|
|
|
|
|
assert!(names.contains(&"prompt_permission"));
|
|
|
|
|
assert!(names.contains(&"get_pipeline_status"));
|
|
|
|
|
assert!(names.contains(&"rebuild_and_restart"));
|
|
|
|
|
assert!(names.contains(&"get_token_usage"));
|
|
|
|
|
assert!(names.contains(&"move_story"));
|
|
|
|
|
assert!(names.contains(&"unblock_story"));
|
|
|
|
|
assert!(names.contains(&"delete_story"));
|
|
|
|
|
assert!(names.contains(&"run_command"));
|
|
|
|
|
assert!(names.contains(&"run_tests"));
|
|
|
|
|
assert!(names.contains(&"get_test_result"));
|
|
|
|
|
assert!(names.contains(&"run_build"));
|
|
|
|
|
assert!(names.contains(&"run_lint"));
|
|
|
|
|
assert!(names.contains(&"git_status"));
|
|
|
|
|
assert!(names.contains(&"git_diff"));
|
|
|
|
|
assert!(names.contains(&"git_add"));
|
|
|
|
|
assert!(names.contains(&"git_commit"));
|
|
|
|
|
assert!(names.contains(&"git_log"));
|
|
|
|
|
assert!(names.contains(&"status"));
|
|
|
|
|
assert!(names.contains(&"loc_file"));
|
|
|
|
|
assert!(names.contains(&"dump_crdt"));
|
|
|
|
|
assert!(names.contains(&"get_version"));
|
|
|
|
|
assert!(names.contains(&"remove_criterion"));
|
|
|
|
|
assert!(names.contains(&"mesh_status"));
|
2026-04-29 13:24:10 +00:00
|
|
|
assert!(names.contains(&"run_check"));
|
|
|
|
|
assert_eq!(tools.len(), 68);
|
2026-04-27 23:26:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn tools_list_schemas_have_required_fields() {
|
|
|
|
|
let resp = handle_tools_list(Some(json!(1)));
|
|
|
|
|
let tools = resp.result.unwrap()["tools"].as_array().unwrap().clone();
|
|
|
|
|
for tool in &tools {
|
|
|
|
|
assert!(tool["name"].is_string(), "tool missing name");
|
|
|
|
|
assert!(tool["description"].is_string(), "tool missing description");
|
|
|
|
|
assert!(tool["inputSchema"].is_object(), "tool missing inputSchema");
|
|
|
|
|
assert_eq!(tool["inputSchema"]["type"], "object");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|