//! MCP (Model Context Protocol) tool registry and execution //! //! Provides tool definition, registration, and execution for RAG, Guidance, //! Settings, and IaC tools. use provisioning_mcp_server::tools::settings::{DeploymentMode, SettingsTools}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use tokio::sync::Mutex; /// Tool execution result #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolExecution { pub tool_name: String, pub result: Value, pub duration_ms: u64, } /// MCP tool registry for provisioning system pub struct ToolRegistry { tools: std::collections::HashMap, settings_tools: Mutex, } /// Tool definition for MCP #[derive(Debug, Clone)] pub struct ToolDefinition { pub name: String, pub description: String, pub category: ToolCategory, pub input_schema: Value, } /// Tool categories #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ToolCategory { Rag, Guidance, Settings, Iac, } impl ToolRegistry { /// Create a new tool registry pub fn new() -> Self { let mut registry = Self { tools: std::collections::HashMap::new(), settings_tools: Mutex::new(SettingsTools::new()), }; registry.register_all_tools(); registry } /// Register all tool categories (RAG, Guidance, Settings, IaC) fn register_all_tools(&mut self) { self.register_rag_tools(); self.register_guidance_tools(); self.register_settings_tools(); self.register_iac_tools(); } /// Register RAG tools fn register_rag_tools(&mut self) { self.tools.insert( "rag_ask_question".to_string(), ToolDefinition { name: "rag_ask_question".to_string(), description: "Ask a question using RAG (Retrieval-Augmented Generation) with knowledge base search".to_string(), category: ToolCategory::Rag, input_schema: json!({ "type": "object", "properties": { "question": {"type": "string", "description": "The question to ask"}, "context": {"type": "string", "description": "Optional context for the question"}, "top_k": {"type": "integer", "description": "Number of top results to return", "default": 3} }, "required": ["question"] }), }, ); self.tools.insert( "rag_semantic_search".to_string(), ToolDefinition { name: "rag_semantic_search".to_string(), description: "Perform semantic search on the knowledge base".to_string(), category: ToolCategory::Rag, input_schema: json!({ "type": "object", "properties": { "query": {"type": "string", "description": "Search query"}, "category": {"type": "string", "description": "Optional category filter"}, "top_k": {"type": "integer", "description": "Number of results", "default": 5} }, "required": ["query"] }), }, ); self.tools.insert( "rag_get_status".to_string(), ToolDefinition { name: "rag_get_status".to_string(), description: "Get the current status of the RAG knowledge base".to_string(), category: ToolCategory::Rag, input_schema: json!({ "type": "object", "properties": {} }), }, ); } /// Register Guidance tools fn register_guidance_tools(&mut self) { self.tools.insert( "guidance_check_system_status".to_string(), ToolDefinition { name: "guidance_check_system_status".to_string(), description: "Check the current system status and configuration".to_string(), category: ToolCategory::Guidance, input_schema: json!({ "type": "object", "properties": {} }), }, ); self.tools.insert( "guidance_suggest_next_action".to_string(), ToolDefinition { name: "guidance_suggest_next_action".to_string(), description: "Get suggestions for the next action based on current system state".to_string(), category: ToolCategory::Guidance, input_schema: json!({ "type": "object", "properties": { "context": {"type": "string", "description": "Optional context for suggestion"} } }), }, ); self.tools.insert( "guidance_find_docs".to_string(), ToolDefinition { name: "guidance_find_docs".to_string(), description: "Find relevant documentation and guides".to_string(), category: ToolCategory::Guidance, input_schema: json!({ "type": "object", "properties": { "query": {"type": "string", "description": "What to search for"}, "context": {"type": "string", "description": "Optional context"} }, "required": ["query"] }), }, ); self.tools.insert( "guidance_troubleshoot".to_string(), ToolDefinition { name: "guidance_troubleshoot".to_string(), description: "Troubleshoot an issue or error".to_string(), category: ToolCategory::Guidance, input_schema: json!({ "type": "object", "properties": { "error": {"type": "string", "description": "Error message or description"}, "context": {"type": "string", "description": "Context about the issue"} }, "required": ["error"] }), }, ); self.tools.insert( "guidance_validate_config".to_string(), ToolDefinition { name: "guidance_validate_config".to_string(), description: "Validate a configuration file or settings".to_string(), category: ToolCategory::Guidance, input_schema: json!({ "type": "object", "properties": { "config_path": {"type": "string", "description": "Path to configuration file"} }, "required": ["config_path"] }), }, ); } /// Register Settings tools fn register_settings_tools(&mut self) { self.tools.insert( "installer_get_settings".to_string(), ToolDefinition { name: "installer_get_settings".to_string(), description: "Get installer settings and configuration".to_string(), category: ToolCategory::Settings, input_schema: json!({ "type": "object", "properties": { "query": {"type": "string", "description": "Optional settings query"} } }), }, ); self.tools.insert( "installer_complete_config".to_string(), ToolDefinition { name: "installer_complete_config".to_string(), description: "Complete partial configuration with defaults".to_string(), category: ToolCategory::Settings, input_schema: json!({ "type": "object", "properties": { "config": {"type": "object", "description": "Partial configuration"} } }), }, ); self.tools.insert( "installer_validate_config".to_string(), ToolDefinition { name: "installer_validate_config".to_string(), description: "Validate configuration against schema".to_string(), category: ToolCategory::Settings, input_schema: json!({ "type": "object", "properties": { "config": {"type": "object", "description": "Configuration to validate"} }, "required": ["config"] }), }, ); self.tools.insert( "installer_get_defaults".to_string(), ToolDefinition { name: "installer_get_defaults".to_string(), description: "Get default settings for a deployment mode".to_string(), category: ToolCategory::Settings, input_schema: json!({ "type": "object", "properties": { "mode": {"type": "string", "description": "Deployment mode"} }, "required": ["mode"] }), }, ); self.tools.insert( "installer_platform_recommendations".to_string(), ToolDefinition { name: "installer_platform_recommendations".to_string(), description: "Get platform-specific recommendations".to_string(), category: ToolCategory::Settings, input_schema: json!({ "type": "object", "properties": {} }), }, ); self.tools.insert( "installer_service_recommendations".to_string(), ToolDefinition { name: "installer_service_recommendations".to_string(), description: "Get service recommendations for a deployment mode".to_string(), category: ToolCategory::Settings, input_schema: json!({ "type": "object", "properties": { "mode": {"type": "string", "description": "Deployment mode"} }, "required": ["mode"] }), }, ); self.tools.insert( "installer_resource_recommendations".to_string(), ToolDefinition { name: "installer_resource_recommendations".to_string(), description: "Get resource recommendations for a deployment mode".to_string(), category: ToolCategory::Settings, input_schema: json!({ "type": "object", "properties": { "mode": {"type": "string", "description": "Deployment mode"} }, "required": ["mode"] }), }, ); } /// Register IaC tools fn register_iac_tools(&mut self) { self.tools.insert( "iac_detect_technologies".to_string(), ToolDefinition { name: "iac_detect_technologies".to_string(), description: "Detect technologies used in infrastructure".to_string(), category: ToolCategory::Iac, input_schema: json!({ "type": "object", "properties": { "path": {"type": "string", "description": "Path to analyze"} } }), }, ); self.tools.insert( "iac_analyze_completeness".to_string(), ToolDefinition { name: "iac_analyze_completeness".to_string(), description: "Analyze completeness of infrastructure configuration".to_string(), category: ToolCategory::Iac, input_schema: json!({ "type": "object", "properties": { "config": {"type": "object", "description": "Configuration to analyze"} } }), }, ); self.tools.insert( "iac_infer_requirements".to_string(), ToolDefinition { name: "iac_infer_requirements".to_string(), description: "Infer infrastructure requirements from description".to_string(), category: ToolCategory::Iac, input_schema: json!({ "type": "object", "properties": { "description": {"type": "string", "description": "Infrastructure description"} }, "required": ["description"] }), }, ); } /// Get all tool definitions pub fn list_tools(&self) -> Vec { self.tools.values().cloned().collect() } /// Get tools by category pub fn tools_by_category(&self, category: ToolCategory) -> Vec { self.tools .values() .filter(|t| t.category == category) .cloned() .collect() } /// Check if tool exists pub fn has_tool(&self, name: &str) -> bool { self.tools.contains_key(name) } /// Execute a tool (async) pub async fn execute(&self, tool_name: &str, args: &Value) -> Result { match tool_name { // RAG tools "rag_ask_question" => self.rag_ask_question(args).await, "rag_semantic_search" => self.rag_semantic_search(args).await, "rag_get_status" => self.rag_get_status(args).await, // Guidance tools "guidance_check_system_status" => self.guidance_check_system_status(args).await, "guidance_suggest_next_action" => self.guidance_suggest_next_action(args).await, "guidance_find_docs" => self.guidance_find_docs(args).await, "guidance_troubleshoot" => self.guidance_troubleshoot(args).await, "guidance_validate_config" => self.guidance_validate_config(args).await, // Settings tools "installer_get_settings" => self.installer_get_settings(args).await, "installer_complete_config" => self.installer_complete_config(args).await, "installer_validate_config" => self.installer_validate_config(args).await, "installer_get_defaults" => self.installer_get_defaults(args).await, "installer_platform_recommendations" => { self.installer_platform_recommendations(args).await } "installer_service_recommendations" => { self.installer_service_recommendations(args).await } "installer_resource_recommendations" => { self.installer_resource_recommendations(args).await } // IaC tools "iac_detect_technologies" => self.iac_detect_technologies(args).await, "iac_analyze_completeness" => self.iac_analyze_completeness(args).await, "iac_infer_requirements" => self.iac_infer_requirements(args).await, _ => Err(format!("Unknown tool: {}", tool_name)), } } // ========== RAG TOOL IMPLEMENTATIONS ========== async fn rag_ask_question(&self, args: &Value) -> Result { let question = args .get("question") .and_then(|v| v.as_str()) .ok_or("question parameter required")?; Ok(json!({ "status": "success", "tool": "rag_ask_question", "message": format!("RAG query would be processed for: {}", question) })) } async fn rag_semantic_search(&self, args: &Value) -> Result { let query = args .get("query") .and_then(|v| v.as_str()) .ok_or("query parameter required")?; Ok(json!({ "status": "success", "tool": "rag_semantic_search", "message": format!("Semantic search would be performed for: {}", query), "results": [] })) } async fn rag_get_status(&self, _args: &Value) -> Result { Ok(json!({ "status": "active", "tool": "rag_get_status", "knowledge_base": { "documents_loaded": true, "total_documents": 76, "categories": ["architecture", "deployment", "security", "reliability"] } })) } // ========== GUIDANCE TOOL IMPLEMENTATIONS ========== /// Execute a Nushell command and parse JSON output async fn execute_nu_command(cmd: &str) -> Result { use tokio::process::Command; let output = Command::new("nu") .arg("-c") .arg(cmd) .output() .await .map_err(|e| format!("Failed to execute Nushell: {}", e))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(format!("Nushell command failed: {}", stderr)); } serde_json::from_slice(&output.stdout) .map_err(|e| format!("Failed to parse JSON output: {}", e)) } async fn guidance_check_system_status(&self, _args: &Value) -> Result { Self::execute_nu_command("provisioning status-json").await } async fn guidance_suggest_next_action(&self, _args: &Value) -> Result { Self::execute_nu_command("provisioning next").await } async fn guidance_find_docs(&self, args: &Value) -> Result { let query = args .get("query") .and_then(|v| v.as_str()) .ok_or("query parameter required")?; let cmd = format!("provisioning guide {}", query); Self::execute_nu_command(&cmd).await } async fn guidance_troubleshoot(&self, _args: &Value) -> Result { Self::execute_nu_command("provisioning health-json").await } async fn guidance_validate_config(&self, args: &Value) -> Result { let config_path = args .get("config_path") .and_then(|v| v.as_str()) .ok_or("config_path parameter required")?; let cmd = format!("validate-system-config {}", config_path); Self::execute_nu_command(&cmd).await } // ========== SETTINGS TOOL IMPLEMENTATIONS ========== async fn installer_get_settings(&self, args: &Value) -> Result { let query = args.get("query").and_then(|v| v.as_str()); let mut settings_tools = self.settings_tools.lock().await; let settings = settings_tools .get_settings(query) .await .map_err(|e| format!("Failed to get settings: {}", e))?; Ok(json!({ "status": "success", "tool": "installer_get_settings", "platforms": settings.get("platforms"), "modes": settings.get("modes"), "default_domain": settings.get("default_domain"), "auto_generate_secrets": settings.get("auto_generate_secrets"), "available_services": settings.get("available_services") })) } async fn installer_complete_config(&self, args: &Value) -> Result { let config = args .get("config") .cloned() .ok_or("config parameter required")?; let mut settings_tools = self.settings_tools.lock().await; settings_tools .complete_config(config) .await .map_err(|e| format!("Failed to complete config: {}", e)) } async fn installer_validate_config(&self, args: &Value) -> Result { let config = args .get("config") .cloned() .ok_or("config parameter required")?; let settings_tools = self.settings_tools.lock().await; settings_tools .validate_config(config) .map_err(|e| format!("Failed to validate config: {}", e)) } async fn installer_get_defaults(&self, args: &Value) -> Result { let mode = args .get("mode") .and_then(|v| v.as_str()) .ok_or("mode parameter required")?; let settings_tools = self.settings_tools.lock().await; let defaults = settings_tools .get_mode_defaults(mode) .map_err(|e| format!("Failed to get defaults: {}", e))?; Ok(json!({ "status": "success", "tool": "installer_get_defaults", "mode": defaults.get("mode"), "description": defaults.get("description"), "service_count": defaults.get("service_count"), "min_cpu_cores": defaults.get("min_cpu_cores"), "min_memory_gb": defaults.get("min_memory_gb"), "recommended_services": defaults.get("recommended_services"), "auto_generate_secrets": defaults.get("auto_generate_secrets"), "domain": defaults.get("domain") })) } async fn installer_platform_recommendations(&self, _args: &Value) -> Result { let mut settings_tools = self.settings_tools.lock().await; let recommendations = settings_tools .get_platform_recommendations() .await .map_err(|e| format!("Failed to get platform recommendations: {}", e))?; Ok(json!({ "status": "success", "tool": "installer_platform_recommendations", "recommendations": recommendations })) } async fn installer_service_recommendations(&self, args: &Value) -> Result { let mode_str = args .get("mode") .and_then(|v| v.as_str()) .ok_or("mode parameter required")?; let mode = DeploymentMode::from_str(mode_str) .ok_or(format!("Invalid deployment mode: {}", mode_str))?; let settings_tools = self.settings_tools.lock().await; let recommendations = settings_tools.get_service_recommendations(&mode); Ok(json!({ "status": "success", "tool": "installer_service_recommendations", "mode": mode_str, "recommendations": recommendations })) } async fn installer_resource_recommendations(&self, args: &Value) -> Result { let mode_str = args .get("mode") .and_then(|v| v.as_str()) .ok_or("mode parameter required")?; let mode = DeploymentMode::from_str(mode_str) .ok_or(format!("Invalid deployment mode: {}", mode_str))?; let settings_tools = self.settings_tools.lock().await; let recommendations = settings_tools.get_resource_recommendations(&mode); Ok(json!({ "status": "success", "tool": "installer_resource_recommendations", "mode": mode_str, "recommendations": recommendations })) } // ========== IAC TOOL IMPLEMENTATIONS ========== async fn iac_detect_technologies(&self, args: &Value) -> Result { let path = args .get("path") .and_then(|v| v.as_str()) .ok_or("path parameter required")?; let path_obj = std::path::Path::new(path); let mut technologies = Vec::new(); // Check for Kubernetes if path_obj.join("kustomization.yaml").exists() || path_obj.join("deployment.yaml").exists() { technologies.push("kubernetes"); } // Check for Docker if path_obj.join("Dockerfile").exists() || path_obj.join("docker-compose.yml").exists() { technologies.push("docker"); } // Check for Terraform if path_obj.join("main.tf").exists() { technologies.push("terraform"); } // Check for KCL (legacy) if path_obj.join("kcl.mod").exists() { technologies.push("kcl"); } // Check for Nickel (current IaC) if path_obj.join("main.ncl").exists() { technologies.push("nickel"); } Ok(json!({ "status": "success", "tool": "iac_detect_technologies", "path": path, "technologies": technologies })) } async fn iac_analyze_completeness(&self, args: &Value) -> Result { let path = args .get("path") .and_then(|v| v.as_str()) .ok_or("path parameter required")?; let path_obj = std::path::Path::new(path); let mut missing = Vec::new(); // Check for essential infrastructure files if !path_obj.join("infrastructure.ncl").exists() { missing.push("infrastructure.ncl"); } if !path_obj.join("config.toml").exists() { missing.push("config.toml"); } if !path_obj.join("README.md").exists() { missing.push("README.md"); } let complete = missing.is_empty(); let completeness_score = if missing.is_empty() { 1.0 } else { 0.7 }; Ok(json!({ "status": "success", "tool": "iac_analyze_completeness", "complete": complete, "completeness_score": completeness_score, "missing_files": missing })) } async fn iac_infer_requirements(&self, args: &Value) -> Result { let _description = args .get("description") .and_then(|v| v.as_str()) .ok_or("description parameter required")?; // Basic requirements inference (can be enhanced with ML later) Ok(json!({ "status": "success", "tool": "iac_infer_requirements", "requirements": { "compute": "2 CPU, 4GB RAM (minimum)", "storage": "20GB", "network": "Private network recommended", "high_availability": false } })) } } impl Default for ToolRegistry { fn default() -> Self { Self::new() } }