spec_kit_mcp/tools/
mod.rs1use anyhow::Result;
6use async_trait::async_trait;
7use serde_json::Value;
8use std::collections::HashMap;
9use std::sync::Arc;
10
11use crate::mcp::types::{ToolDefinition, ToolResult};
12use crate::speckit::SpecKitCli;
13
14pub mod analyze;
15pub mod check;
16pub mod checklist;
17pub mod clarify;
18pub mod constitution;
19pub mod implement;
20pub mod init;
21pub mod plan;
22pub mod specify;
23pub mod tasks;
24
25pub use analyze::AnalyzeTool;
26pub use check::CheckTool;
27pub use checklist::ChecklistTool;
28pub use clarify::ClarifyTool;
29pub use constitution::ConstitutionTool;
30pub use implement::ImplementTool;
31pub use init::InitTool;
32pub use plan::PlanTool;
33pub use specify::SpecifyTool;
34pub use tasks::TasksTool;
35
36#[async_trait]
38pub trait Tool: Send + Sync {
39 fn definition(&self) -> ToolDefinition;
41
42 async fn execute(&self, params: Value) -> Result<ToolResult>;
44
45 fn name(&self) -> String {
47 self.definition().name.clone()
48 }
49}
50
51pub struct ToolRegistry {
53 tools: HashMap<String, Arc<dyn Tool>>,
54}
55
56impl ToolRegistry {
57 pub fn new() -> Self {
59 Self {
60 tools: HashMap::new(),
61 }
62 }
63
64 pub fn register(&mut self, tool: Arc<dyn Tool>) {
66 let name = tool.name();
67 self.tools.insert(name, tool);
68 }
69
70 pub fn get(&self, name: &str) -> Option<Arc<dyn Tool>> {
72 self.tools.get(name).cloned()
73 }
74
75 pub fn list_tools(&self) -> Vec<ToolDefinition> {
77 self.tools.values().map(|tool| tool.definition()).collect()
78 }
79
80 pub fn has_tool(&self, name: &str) -> bool {
82 self.tools.contains_key(name)
83 }
84
85 pub fn len(&self) -> usize {
87 self.tools.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.tools.is_empty()
93 }
94}
95
96impl Default for ToolRegistry {
97 fn default() -> Self {
98 Self::new()
99 }
100}
101
102pub fn create_registry(cli: SpecKitCli) -> ToolRegistry {
104 let mut registry = ToolRegistry::new();
105
106 registry.register(Arc::new(InitTool::new(cli.clone())));
108 registry.register(Arc::new(CheckTool::new(cli.clone())));
109 registry.register(Arc::new(ConstitutionTool::new(cli.clone())));
110 registry.register(Arc::new(SpecifyTool::new(cli.clone())));
111 registry.register(Arc::new(PlanTool::new(cli.clone())));
112 registry.register(Arc::new(TasksTool::new(cli.clone())));
113 registry.register(Arc::new(ImplementTool::new(cli.clone())));
114 registry.register(Arc::new(ClarifyTool::new(cli.clone())));
115 registry.register(Arc::new(AnalyzeTool::new(cli.clone())));
116 registry.register(Arc::new(ChecklistTool::new(cli)));
117
118 tracing::info!(tool_count = registry.len(), "Tool registry created");
119
120 registry
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_registry_creation() {
129 let registry = ToolRegistry::new();
130 assert_eq!(registry.len(), 0);
131 assert!(registry.is_empty());
132 }
133
134 #[test]
135 fn test_registry_with_tools() {
136 let cli = SpecKitCli::new();
137 let registry = create_registry(cli);
138
139 assert!(!registry.is_empty());
140 assert!(registry.has_tool("speckit_init"));
141 assert!(registry.has_tool("speckit_constitution"));
142 assert!(registry.has_tool("speckit_specify"));
143 }
144
145 #[test]
146 fn test_list_tools() {
147 let cli = SpecKitCli::new();
148 let registry = create_registry(cli);
149
150 let tools = registry.list_tools();
151 assert!(!tools.is_empty());
152
153 for tool in tools {
155 assert!(!tool.name.is_empty());
156 assert!(!tool.description.is_empty());
157 }
158 }
159}