spec_kit_mcp/tools/
mod.rs

1//! MCP Tools Implementation
2//!
3//! This module provides all the spec-kit tools exposed via MCP.
4
5use 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/// Trait for all MCP tools
37#[async_trait]
38pub trait Tool: Send + Sync {
39    /// Get the tool definition
40    fn definition(&self) -> ToolDefinition;
41
42    /// Execute the tool
43    async fn execute(&self, params: Value) -> Result<ToolResult>;
44
45    /// Get the tool name
46    fn name(&self) -> String {
47        self.definition().name.clone()
48    }
49}
50
51/// Tool registry
52pub struct ToolRegistry {
53    tools: HashMap<String, Arc<dyn Tool>>,
54}
55
56impl ToolRegistry {
57    /// Create a new tool registry
58    pub fn new() -> Self {
59        Self {
60            tools: HashMap::new(),
61        }
62    }
63
64    /// Register a tool
65    pub fn register(&mut self, tool: Arc<dyn Tool>) {
66        let name = tool.name();
67        self.tools.insert(name, tool);
68    }
69
70    /// Get a tool by name
71    pub fn get(&self, name: &str) -> Option<Arc<dyn Tool>> {
72        self.tools.get(name).cloned()
73    }
74
75    /// List all tool definitions
76    pub fn list_tools(&self) -> Vec<ToolDefinition> {
77        self.tools.values().map(|tool| tool.definition()).collect()
78    }
79
80    /// Check if a tool exists
81    pub fn has_tool(&self, name: &str) -> bool {
82        self.tools.contains_key(name)
83    }
84
85    /// Get the number of registered tools
86    pub fn len(&self) -> usize {
87        self.tools.len()
88    }
89
90    /// Check if the registry is empty
91    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
102/// Create and populate the default tool registry
103pub fn create_registry(cli: SpecKitCli) -> ToolRegistry {
104    let mut registry = ToolRegistry::new();
105
106    // Register all tools
107    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        // Verify all tools have proper definitions
154        for tool in tools {
155            assert!(!tool.name.is_empty());
156            assert!(!tool.description.is_empty());
157        }
158    }
159}