Files
huskies/server/src/http/mcp/tools_list/mod.rs
T

105 lines
4.5 KiB
Rust
Raw Normal View History

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"));
assert_eq!(tools.len(), 67);
}
#[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");
}
}
}