1425 lines
61 KiB
Rust
1425 lines
61 KiB
Rust
#!/usr/bin/env -S cargo run --
|
|
#![allow(dead_code, unused_imports, unused_variables, unused_assignments, unused)]
|
|
#![allow(
|
|
clippy::excessive_nesting,
|
|
clippy::upper_case_acronyms,
|
|
clippy::collapsible_if,
|
|
clippy::enum_variant_names,
|
|
clippy::unnecessary_map_or
|
|
)]
|
|
|
|
//! Cloud Native Provisioning MCP Server
|
|
//!
|
|
//! A Rust-native implementation of Model Context Protocol server
|
|
//! for infrastructure automation and AI-assisted DevOps operations.
|
|
|
|
use anyhow::{Context, Result};
|
|
use clap::Parser;
|
|
use rust_mcp_sdk::Server;
|
|
use rust_mcp_sdk::schema::{
|
|
CallToolRequest, CallToolResult, ServerCapabilities, TextContent, Tool
|
|
};
|
|
use serde_json::json;
|
|
use std::path::PathBuf;
|
|
use tracing::{info, debug};
|
|
|
|
mod config;
|
|
mod provisioning;
|
|
mod tools;
|
|
mod errors;
|
|
|
|
use config::Config;
|
|
use errors::ProvisioningError;
|
|
use provisioning::ProvisioningEngine;
|
|
use tools::{
|
|
ProvisioningTools, SettingsTools, SystemStatusTool, NextActionTool,
|
|
DocFinderTool, TroubleshooterTool, ConfigValidatorTool,
|
|
};
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(name = "provisioning-mcp-server")]
|
|
#[command(about = "Rust-native MCP server for Cloud Native Provisioning")]
|
|
#[command(version)]
|
|
struct Args {
|
|
/// Configuration file path
|
|
#[arg(short, long, env = "PROVISIONING_MCP_CONFIG")]
|
|
config: Option<PathBuf>,
|
|
|
|
/// Provisioning system path
|
|
#[arg(short, long, env = "PROVISIONING_PATH")]
|
|
provisioning_path: Option<PathBuf>,
|
|
|
|
/// Debug mode
|
|
#[arg(short, long, env = "PROVISIONING_DEBUG")]
|
|
debug: bool,
|
|
|
|
/// Log level
|
|
#[arg(short, long, env = "PROVISIONING_LOG_LEVEL", default_value = "info")]
|
|
log_level: String,
|
|
}
|
|
|
|
/// Enhanced MCP Server for Systems Provisioning with AI Integration
|
|
pub struct ProvisioningMCPServer {
|
|
config: Config,
|
|
engine: ProvisioningEngine,
|
|
tools: ProvisioningTools,
|
|
api_tools: tools::provisioning_tools::ProvisioningTools,
|
|
settings_tools: std::sync::Arc<tokio::sync::Mutex<SettingsTools>>,
|
|
system_status_tool: SystemStatusTool,
|
|
next_action_tool: NextActionTool,
|
|
doc_finder_tool: std::sync::Arc<tokio::sync::Mutex<DocFinderTool>>,
|
|
troubleshooter_tool: TroubleshooterTool,
|
|
config_validator_tool: ConfigValidatorTool,
|
|
}
|
|
|
|
impl ProvisioningMCPServer {
|
|
/// Create new MCP server instance
|
|
pub fn new(config: Config) -> Result<Self> {
|
|
let engine = ProvisioningEngine::new(&config)?;
|
|
let tools = ProvisioningTools::new(&config);
|
|
let api_tools = tools::provisioning_tools::ProvisioningTools::new(
|
|
Some("http://localhost:3000".to_string())
|
|
);
|
|
let settings_tools = std::sync::Arc::new(tokio::sync::Mutex::new(SettingsTools::new()));
|
|
|
|
// Initialize guidance tools
|
|
let provisioning_root = config.provisioning_path.clone();
|
|
let system_status_tool = SystemStatusTool::new(provisioning_root.clone());
|
|
let next_action_tool = NextActionTool::new(provisioning_root.clone());
|
|
let doc_finder_tool = std::sync::Arc::new(tokio::sync::Mutex::new(
|
|
DocFinderTool::new(provisioning_root.clone())
|
|
));
|
|
let troubleshooter_tool = TroubleshooterTool::new(provisioning_root.clone());
|
|
let config_validator_tool = ConfigValidatorTool::new(provisioning_root);
|
|
|
|
Ok(Self {
|
|
config,
|
|
engine,
|
|
tools,
|
|
api_tools,
|
|
settings_tools,
|
|
system_status_tool,
|
|
next_action_tool,
|
|
doc_finder_tool,
|
|
troubleshooter_tool,
|
|
config_validator_tool,
|
|
})
|
|
}
|
|
|
|
/// Initialize and run the enhanced MCP server
|
|
pub async fn run(&self) -> Result<()> {
|
|
info!("Starting Enhanced Systems Provisioning MCP Server v2.0");
|
|
info!("New features: API integration, AI agents, dashboards, advanced analytics");
|
|
|
|
let mut server = Server::new("enhanced-provisioning-server".to_string())
|
|
.with_capabilities(ServerCapabilities {
|
|
tools: Some(json!({
|
|
"listChanged": true
|
|
})),
|
|
resources: Some(json!({
|
|
"subscribe": true,
|
|
"listChanged": true
|
|
})),
|
|
..Default::default()
|
|
});
|
|
|
|
// Register legacy tools
|
|
server = server
|
|
.tool("provision_create_server", self.handle_create_server())
|
|
.tool("provision_ai_template", self.handle_ai_template())
|
|
.tool("provision_query", self.handle_query())
|
|
.tool("provision_deploy_taskserv", self.handle_deploy_taskserv())
|
|
.tool("provision_cluster_create", self.handle_cluster_create())
|
|
.tool("provision_status", self.handle_status())
|
|
.tool("provision_ai_config", self.handle_ai_config());
|
|
|
|
// Register enhanced API tools
|
|
server = server
|
|
.tool("ai_query", self.handle_enhanced_ai_query())
|
|
.tool("get_infrastructure_status", self.handle_get_infrastructure_status())
|
|
.tool("get_system_metrics", self.handle_get_system_metrics())
|
|
.tool("get_logs", self.handle_get_logs())
|
|
.tool("start_api_server", self.handle_start_api_server())
|
|
.tool("create_dashboard", self.handle_create_dashboard())
|
|
.tool("start_dashboard", self.handle_start_dashboard())
|
|
.tool("start_ai_agents", self.handle_start_ai_agents())
|
|
.tool("get_agents_status", self.handle_get_agents_status())
|
|
.tool("create_servers", self.handle_create_servers())
|
|
.tool("list_servers", self.handle_list_servers())
|
|
.tool("delete_servers", self.handle_delete_servers())
|
|
.tool("create_cluster", self.handle_create_cluster_enhanced())
|
|
.tool("generate_infrastructure", self.handle_generate_infrastructure())
|
|
.tool("get_cost_optimization", self.handle_get_cost_optimization())
|
|
.tool("get_security_analysis", self.handle_get_security_analysis())
|
|
.tool("get_performance_analysis", self.handle_get_performance_analysis())
|
|
.tool("predict_issues", self.handle_predict_issues());
|
|
|
|
// Register installer settings tools
|
|
server = server
|
|
.tool("installer_get_settings", self.handle_get_settings())
|
|
.tool("installer_complete_config", self.handle_complete_config())
|
|
.tool("installer_validate_config", self.handle_validate_config())
|
|
.tool("installer_get_defaults", self.handle_get_defaults())
|
|
.tool("installer_platform_recommendations", self.handle_platform_recommendations())
|
|
.tool("installer_service_recommendations", self.handle_service_recommendations())
|
|
.tool("installer_resource_recommendations", self.handle_resource_recommendations());
|
|
|
|
// Register guidance tools
|
|
server = server
|
|
.tool("guidance_check_system_status", self.handle_check_system_status())
|
|
.tool("guidance_suggest_next_action", self.handle_suggest_next_action())
|
|
.tool("guidance_find_docs", self.handle_find_docs())
|
|
.tool("guidance_diagnose_issue", self.handle_diagnose_issue())
|
|
.tool("guidance_validate_config_file", self.handle_validate_config_file());
|
|
|
|
server
|
|
.run()
|
|
.await
|
|
.context("Failed to run enhanced MCP server")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get list of available tools
|
|
async fn get_available_tools(&self) -> Result<Vec<Tool>> {
|
|
Ok(vec![
|
|
Tool {
|
|
name: "provision_create_server".to_string(),
|
|
title: Some("provision_create_server".to_string()),
|
|
description: "Create infrastructure servers using natural language or specific configuration".to_string(),
|
|
input_schema: json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"description": {
|
|
"type": "string",
|
|
"description": "Natural language description of the infrastructure needed"
|
|
},
|
|
"infra_name": {
|
|
"type": "string",
|
|
"description": "Infrastructure name/project identifier"
|
|
},
|
|
"provider": {
|
|
"type": "string",
|
|
"enum": ["aws", "upcloud", "local"],
|
|
"description": "Cloud provider to use"
|
|
},
|
|
"check_mode": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Run in check mode (no actual resources created)"
|
|
}
|
|
},
|
|
"required": ["description"]
|
|
}),
|
|
annotations: None,
|
|
meta: None,
|
|
output_schema: None, },
|
|
Tool {
|
|
name: "provision_ai_template".to_string(),
|
|
title: Some("provision_ai_template".to_string()),
|
|
description: "Generate infrastructure templates using AI assistance".to_string(),
|
|
input_schema: json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"description": {
|
|
"type": "string",
|
|
"description": "Detailed description of the infrastructure template needed"
|
|
},
|
|
"template_type": {
|
|
"type": "string",
|
|
"enum": ["server", "taskserv", "cluster", "full-stack"],
|
|
"description": "Type of template to generate"
|
|
},
|
|
"complexity": {
|
|
"type": "string",
|
|
"enum": ["simple", "medium", "complex"],
|
|
"default": "medium",
|
|
"description": "Complexity level of the generated template"
|
|
}
|
|
},
|
|
"required": ["description", "template_type"]
|
|
}),
|
|
annotations: None,
|
|
meta: None,
|
|
output_schema: None, },
|
|
Tool {
|
|
name: "provision_query".to_string(),
|
|
title: Some("provision_query".to_string()),
|
|
description: "Query infrastructure state and configuration using natural language".to_string(),
|
|
input_schema: json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {
|
|
"type": "string",
|
|
"description": "Natural language query about infrastructure state"
|
|
},
|
|
"infra_name": {
|
|
"type": "string",
|
|
"description": "Specific infrastructure to query (optional)"
|
|
},
|
|
"output_format": {
|
|
"type": "string",
|
|
"enum": ["human", "json", "yaml"],
|
|
"default": "human",
|
|
"description": "Output format preference"
|
|
}
|
|
},
|
|
"required": ["query"]
|
|
}),
|
|
annotations: None,
|
|
meta: None,
|
|
output_schema: None, },
|
|
Tool {
|
|
name: "provision_deploy_taskserv".to_string(),
|
|
title: Some("provision_deploy_taskserv".to_string()),
|
|
description: "Deploy specific infrastructure services (databases, networking, etc.)".to_string(),
|
|
input_schema: json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"service_name": {
|
|
"type": "string",
|
|
"description": "Name of the service to deploy (e.g., 'postgresql', 'kubernetes', 'youki')"
|
|
},
|
|
"infra_name": {
|
|
"type": "string",
|
|
"description": "Target infrastructure name"
|
|
},
|
|
"configuration": {
|
|
"type": "object",
|
|
"description": "Service-specific configuration options"
|
|
},
|
|
"check_mode": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Run in check mode first"
|
|
}
|
|
},
|
|
"required": ["service_name", "infra_name"]
|
|
}),
|
|
annotations: None,
|
|
meta: None,
|
|
output_schema: None, },
|
|
Tool {
|
|
name: "provision_cluster_create".to_string(),
|
|
title: Some("provision_cluster_create".to_string()),
|
|
description: "Create complete clusters with integrated services".to_string(),
|
|
input_schema: json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"cluster_description": {
|
|
"type": "string",
|
|
"description": "Natural language description of the cluster needed"
|
|
},
|
|
"cluster_type": {
|
|
"type": "string",
|
|
"enum": ["development", "staging", "production", "demo"],
|
|
"description": "Type/purpose of the cluster"
|
|
},
|
|
"services": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
"description": "List of services to include (e.g., ['postgresql', 'redis', 'monitoring'])"
|
|
},
|
|
"infra_name": {
|
|
"type": "string",
|
|
"description": "Infrastructure name for the cluster"
|
|
}
|
|
},
|
|
"required": ["cluster_description", "cluster_type"]
|
|
}),
|
|
annotations: None,
|
|
meta: None,
|
|
output_schema: None, },
|
|
Tool {
|
|
name: "provision_status".to_string(),
|
|
title: Some("provision_status".to_string()),
|
|
description: "Get comprehensive status of infrastructure and services".to_string(),
|
|
input_schema: json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"infra_name": {
|
|
"type": "string",
|
|
"description": "Specific infrastructure to check (optional, checks all if not provided)"
|
|
},
|
|
"detailed": {
|
|
"type": "boolean",
|
|
"default": false,
|
|
"description": "Include detailed metrics and logs"
|
|
}
|
|
}
|
|
}),
|
|
annotations: None,
|
|
meta: None,
|
|
output_schema: None, },
|
|
Tool {
|
|
name: "provision_ai_config".to_string(),
|
|
title: Some("provision_ai_config".to_string()),
|
|
description: "Configure and verify AI capabilities for the provisioning system".to_string(),
|
|
input_schema: json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"action": {
|
|
"type": "string",
|
|
"enum": ["status", "configure", "test"],
|
|
"description": "Action to perform with AI configuration"
|
|
},
|
|
"provider": {
|
|
"type": "string",
|
|
"enum": ["openai", "claude", "generic"],
|
|
"description": "AI provider to configure (for configure action)"
|
|
}
|
|
},
|
|
"required": ["action"]
|
|
}),
|
|
annotations: None,
|
|
meta: None,
|
|
output_schema: None, },
|
|
])
|
|
}
|
|
|
|
/// Handle server creation tool
|
|
fn handle_create_server(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let description = arguments.get("description")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("description is required".to_string()))?;
|
|
|
|
let infra_name = arguments.get("infra_name")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("ai-generated");
|
|
|
|
let provider = arguments.get("provider")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("aws");
|
|
|
|
let check_mode = arguments.get("check_mode")
|
|
.and_then(|v| v.as_bool())
|
|
.unwrap_or(true);
|
|
|
|
info!("Creating server with description: {}", description);
|
|
debug!("Parameters: infra={}, provider={}, check={}", infra_name, provider, check_mode);
|
|
|
|
// Use AI to parse the natural language description
|
|
let parsed_json = self.tools.parse_server_description(description)?;
|
|
|
|
// Convert JSON to ServerConfig
|
|
let parsed_config = provisioning::ServerConfig {
|
|
hostname: parsed_json.get("hostname")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or(infra_name)
|
|
.to_string(),
|
|
instance_type: parsed_json.get("instance_type")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("t3.medium")
|
|
.to_string(),
|
|
count: parsed_json.get("count")
|
|
.and_then(|v| v.as_u64())
|
|
.unwrap_or(1) as u32,
|
|
provider: provider.to_string(),
|
|
region: parsed_json.get("region")
|
|
.and_then(|v| v.as_str())
|
|
.map(|s| s.to_string()),
|
|
purpose: parsed_json.get("purpose")
|
|
.and_then(|v| v.as_str())
|
|
.map(|s| s.to_string()),
|
|
};
|
|
|
|
// Execute provisioning command
|
|
let result = self.engine.create_server(&parsed_config, check_mode)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("✅ Server creation completed:\n\n{}", result)))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Handle AI template generation
|
|
fn handle_ai_template(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let description = arguments.get("description")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("description is required".to_string()))?;
|
|
|
|
let template_type = arguments.get("template_type")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("template_type is required".to_string()))?;
|
|
|
|
let complexity = arguments.get("complexity")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("medium");
|
|
|
|
info!("Generating {} template: {}", template_type, description);
|
|
|
|
let template = self.tools.generate_ai_template(description, template_type, complexity)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🤖 Generated {} template:\n\n```kcl\n{}\n```", template_type, template)))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Handle infrastructure queries
|
|
fn handle_query(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let query = arguments.get("query")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("query is required".to_string()))?;
|
|
|
|
let infra_name = arguments.get("infra_name")
|
|
.and_then(|v| v.as_str());
|
|
|
|
let output_format = arguments.get("output_format")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("human");
|
|
|
|
info!("Processing query: {}", query);
|
|
|
|
let result = self.engine.process_query(query, infra_name, output_format)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🔍 Query result:\n\n{}", result)))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Handle task service deployment
|
|
fn handle_deploy_taskserv(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let service_name = arguments.get("service_name")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("service_name is required".to_string()))?;
|
|
|
|
let infra_name = arguments.get("infra_name")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("infra_name is required".to_string()))?;
|
|
|
|
let configuration = arguments.get("configuration")
|
|
.cloned()
|
|
.unwrap_or_default();
|
|
|
|
let check_mode = arguments.get("check_mode")
|
|
.and_then(|v| v.as_bool())
|
|
.unwrap_or(true);
|
|
|
|
info!("Deploying service {} to infrastructure {}", service_name, infra_name);
|
|
|
|
let result = self.engine.deploy_taskserv(service_name, infra_name, &configuration, check_mode)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("⚙️ Service deployment result:\n\n{}", result)))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Handle cluster creation
|
|
fn handle_cluster_create(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let cluster_description = arguments.get("cluster_description")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("cluster_description is required".to_string()))?;
|
|
|
|
let cluster_type = arguments.get("cluster_type")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("cluster_type is required".to_string()))?;
|
|
|
|
let services = arguments.get("services")
|
|
.and_then(|v| v.as_array())
|
|
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect::<Vec<_>>())
|
|
.unwrap_or_default();
|
|
|
|
let infra_name = arguments.get("infra_name")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("ai-cluster");
|
|
|
|
info!("Creating {} cluster: {}", cluster_type, cluster_description);
|
|
|
|
let result = self.engine.create_cluster(cluster_description, cluster_type, &services, infra_name)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🚢 Cluster creation result:\n\n{}", result)))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Handle status checks
|
|
fn handle_status(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let infra_name = arguments.get("infra_name")
|
|
.and_then(|v| v.as_str());
|
|
|
|
let detailed = arguments.get("detailed")
|
|
.and_then(|v| v.as_bool())
|
|
.unwrap_or(false);
|
|
|
|
info!("Getting status for infrastructure: {:?}", infra_name);
|
|
|
|
let result = self.engine.get_status(infra_name, detailed)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("📊 Infrastructure status:\n\n{}", result)))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Handle AI configuration
|
|
fn handle_ai_config(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let action = arguments.get("action")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("action is required".to_string()))?;
|
|
|
|
let provider = arguments.get("provider")
|
|
.and_then(|v| v.as_str());
|
|
|
|
info!("AI config action: {}", action);
|
|
|
|
let result = match action {
|
|
"status" => self.tools.get_ai_status()?,
|
|
"configure" => {
|
|
let provider = provider.ok_or_else(||
|
|
ProvisioningError::InvalidInput("provider is required for configure action".to_string())
|
|
)?;
|
|
self.tools.configure_ai(provider)?
|
|
},
|
|
"test" => self.tools.test_ai_connection()?,
|
|
_ => return Err(ProvisioningError::InvalidInput(format!("Unknown action: {}", action)).into()),
|
|
};
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🤖 AI configuration result:\n\n{}", result)))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Enhanced API tool handlers
|
|
fn handle_enhanced_ai_query(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
|
|
let query = arguments.get("query")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("query is required".to_string()))?;
|
|
|
|
let context = arguments.get("context").and_then(|v| v.as_str());
|
|
|
|
info!("Enhanced AI query: {}", query);
|
|
|
|
// This would be async in real implementation
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.ai_query(query, context)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🤖 AI Analysis:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_infrastructure_status(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Getting infrastructure status via API");
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.get_infrastructure_status()
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🏗️ Infrastructure Status:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_system_metrics(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let timerange = arguments.get("timerange").and_then(|v| v.as_str());
|
|
|
|
info!("Getting system metrics for timerange: {:?}", timerange);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.get_system_metrics(timerange)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("📊 System Metrics:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_logs(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let level = arguments.get("level").and_then(|v| v.as_str());
|
|
let since = arguments.get("since").and_then(|v| v.as_str());
|
|
|
|
info!("Getting logs - level: {:?}, since: {:?}", level, since);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.get_logs(level, since)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("📋 System Logs:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_start_api_server(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let port = arguments.get("port").and_then(|v| v.as_u64()).map(|p| p as u16);
|
|
|
|
info!("Starting API server on port: {:?}", port);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.start_api_server(port)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🚀 API Server:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_create_dashboard(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let template = arguments.get("template").and_then(|v| v.as_str());
|
|
let name = arguments.get("name").and_then(|v| v.as_str());
|
|
|
|
info!("Creating dashboard - template: {:?}, name: {:?}", template, name);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.create_dashboard(template, name)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("📊 Dashboard Creation:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_start_dashboard(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let name = arguments.get("name")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("name is required".to_string()))?;
|
|
let port = arguments.get("port").and_then(|v| v.as_u64()).map(|p| p as u16);
|
|
|
|
info!("Starting dashboard: {} on port: {:?}", name, port);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.start_dashboard(name, port)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🌐 Dashboard Started:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_start_ai_agents(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let agents = arguments.get("agents")
|
|
.and_then(|v| v.as_array())
|
|
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect::<Vec<_>>());
|
|
|
|
info!("Starting AI agents: {:?}", agents);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.start_ai_agents(agents)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🤖 AI Agents Started:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_agents_status(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Getting AI agents status");
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.get_agents_status()
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🤖 Agents Status:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_create_servers(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let config = arguments.get("config")
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("config is required".to_string()))?;
|
|
|
|
info!("Creating servers with config: {:?}", config);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.create_servers(config)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🖥️ Servers Created:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_list_servers(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let provider = arguments.get("provider").and_then(|v| v.as_str());
|
|
|
|
info!("Listing servers for provider: {:?}", provider);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.list_servers(provider)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("📋 Server List:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_delete_servers(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let server_names = arguments.get("server_names")
|
|
.and_then(|v| v.as_array())
|
|
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect::<Vec<_>>())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("server_names is required".to_string()))?;
|
|
|
|
info!("Deleting servers: {:?}", server_names);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.delete_servers(&server_names)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🗑️ Servers Deleted:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_create_cluster_enhanced(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let config = arguments.get("config")
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("config is required".to_string()))?;
|
|
|
|
info!("Creating cluster with config: {:?}", config);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.create_cluster(config)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🚢 Cluster Created:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_generate_infrastructure(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let description = arguments.get("description")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("description is required".to_string()))?;
|
|
let output_type = arguments.get("output_type").and_then(|v| v.as_str());
|
|
|
|
info!("Generating infrastructure from description: {}", description);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.generate_infrastructure(description, output_type)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🏗️ Generated Infrastructure:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_cost_optimization(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Getting cost optimization recommendations");
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.get_cost_optimization()
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("💰 Cost Optimization:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_security_analysis(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Getting security analysis");
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.get_security_analysis()
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🛡️ Security Analysis:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_performance_analysis(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Getting performance analysis");
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.get_performance_analysis()
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("⚡ Performance Analysis:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_predict_issues(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let timeframe = arguments.get("timeframe").and_then(|v| v.as_str());
|
|
|
|
info!("Predicting issues for timeframe: {:?}", timeframe);
|
|
|
|
let result = tokio::runtime::Handle::current().block_on(
|
|
self.api_tools.predict_issues(timeframe)
|
|
)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🔮 Issue Predictions:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Installer Settings Tool Handlers
|
|
|
|
fn handle_get_settings(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let query = arguments.get("query").and_then(|v| v.as_str());
|
|
|
|
info!("Getting installer settings - query: {:?}", query);
|
|
|
|
let settings_tools = self.settings_tools.clone();
|
|
let result = tokio::runtime::Handle::current().block_on(async move {
|
|
let mut tools = settings_tools.lock().await;
|
|
tools.get_settings(query).await
|
|
})?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("⚙️ Installer Settings:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_complete_config(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let partial_config = arguments.get("config")
|
|
.cloned()
|
|
.unwrap_or(json!({}));
|
|
|
|
info!("Completing partial configuration");
|
|
|
|
let settings_tools = self.settings_tools.clone();
|
|
let result = tokio::runtime::Handle::current().block_on(async move {
|
|
let mut tools = settings_tools.lock().await;
|
|
tools.complete_config(partial_config).await
|
|
})?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("✅ Completed Configuration:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_validate_config(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let config = arguments.get("config")
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("config is required".to_string()))?
|
|
.clone();
|
|
|
|
info!("Validating installer configuration");
|
|
|
|
let settings_tools = self.settings_tools.clone();
|
|
let result = tokio::runtime::Handle::current().block_on(async move {
|
|
let tools = settings_tools.lock().await;
|
|
tools.validate_config(config)
|
|
})?;
|
|
|
|
let is_valid = result.get("valid").and_then(|v| v.as_bool()).unwrap_or(false);
|
|
let icon = if is_valid { "✅" } else { "❌" };
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("{} Configuration Validation:\n\n{}", icon, serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(!is_valid),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_get_defaults(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let mode = arguments.get("mode")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("mode is required".to_string()))?;
|
|
|
|
info!("Getting defaults for mode: {}", mode);
|
|
|
|
let settings_tools = self.settings_tools.clone();
|
|
let result = tokio::runtime::Handle::current().block_on(async move {
|
|
let tools = settings_tools.lock().await;
|
|
tools.get_mode_defaults(mode)
|
|
})?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("📋 Mode Defaults:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_platform_recommendations(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Getting platform recommendations");
|
|
|
|
let settings_tools = self.settings_tools.clone();
|
|
let result = tokio::runtime::Handle::current().block_on(async move {
|
|
let mut tools = settings_tools.lock().await;
|
|
tools.get_platform_recommendations().await
|
|
})?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🎯 Platform Recommendations:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_service_recommendations(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let mode_str = arguments.get("mode")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("mode is required".to_string()))?;
|
|
|
|
info!("Getting service recommendations for mode: {}", mode_str);
|
|
|
|
let settings_tools = self.settings_tools.clone();
|
|
let result = tokio::runtime::Handle::current().block_on(async move {
|
|
let tools = settings_tools.lock().await;
|
|
let mode = tools::settings::DeploymentMode::from_str(mode_str)
|
|
.ok_or_else(|| ProvisioningError::invalid_input(format!("Invalid mode: {}", mode_str)))?;
|
|
Ok::<_, ProvisioningError>(tools.get_service_recommendations(&mode))
|
|
})?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("🔧 Service Recommendations:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_resource_recommendations(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let mode_str = arguments.get("mode")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("mode is required".to_string()))?;
|
|
|
|
info!("Getting resource recommendations for mode: {}", mode_str);
|
|
|
|
let settings_tools = self.settings_tools.clone();
|
|
let result = tokio::runtime::Handle::current().block_on(async move {
|
|
let tools = settings_tools.lock().await;
|
|
let mode = tools::settings::DeploymentMode::from_str(mode_str)
|
|
.ok_or_else(|| ProvisioningError::invalid_input(format!("Invalid mode: {}", mode_str)))?;
|
|
Ok::<_, ProvisioningError>(tools.get_resource_recommendations(&mode))
|
|
})?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(rust_mcp_sdk::schema::TextContent::from(format!("💾 Resource Recommendations:\n\n{}", serde_json::to_string_pretty(&result)?),
|
|
))],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Guidance Tool Handlers
|
|
// ============================================================================
|
|
|
|
fn handle_check_system_status(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|_request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Checking system status");
|
|
|
|
let status = self.system_status_tool.check_system()?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(
|
|
rust_mcp_sdk::schema::TextContent::from(format!(
|
|
"🔍 System Status:\n\n{}",
|
|
serde_json::to_string_pretty(&status)?
|
|
)),
|
|
)],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_suggest_next_action(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|_request: CallToolRequest| -> Result<CallToolResult> {
|
|
info!("Suggesting next action");
|
|
|
|
// Get current system status
|
|
let status = self.system_status_tool.check_system()?;
|
|
|
|
// Suggest next action based on status
|
|
let next_action = self.next_action_tool.suggest_next_action(&status)?;
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(
|
|
rust_mcp_sdk::schema::TextContent::from(format!(
|
|
"💡 Next Action:\n\n**Command:** `{}`\n\n**Description:** {}\n\n**Expected Outcome:** {}\n\n**Documentation:** {}\n\n**Priority:** {}",
|
|
next_action.command,
|
|
next_action.description,
|
|
next_action.expected_outcome,
|
|
next_action.docs_link,
|
|
next_action.priority
|
|
)),
|
|
)],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_find_docs(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let query = arguments
|
|
.get("query")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("query is required".to_string()))?;
|
|
let context = arguments.get("context").and_then(|v| v.as_str());
|
|
|
|
info!("Finding documentation for query: {}", query);
|
|
|
|
let doc_finder = self.doc_finder_tool.clone();
|
|
let results = tokio::runtime::Handle::current().block_on(async move {
|
|
let mut finder = doc_finder.lock().await;
|
|
finder.find_docs(query, context)
|
|
})?;
|
|
|
|
let mut output = String::from("📚 Documentation Results:\n\n");
|
|
for (idx, doc) in results.iter().enumerate() {
|
|
output.push_str(&format!(
|
|
"{}. **{}** (relevance: {:.1}%)\n Path: `{}`\n {}\n\n",
|
|
idx + 1,
|
|
doc.title,
|
|
doc.relevance * 100.0,
|
|
doc.path,
|
|
doc.snippet.trim()
|
|
));
|
|
}
|
|
|
|
if results.is_empty() {
|
|
output.push_str("No documentation found matching your query.\n");
|
|
}
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(
|
|
rust_mcp_sdk::schema::TextContent::from(output),
|
|
)],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_diagnose_issue(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let error = arguments
|
|
.get("error")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("error is required".to_string()))?;
|
|
|
|
info!("Diagnosing issue: {}", error);
|
|
|
|
// Get current system status for context
|
|
let status = self.system_status_tool.check_system()?;
|
|
|
|
// Diagnose the issue
|
|
let diagnosis = self.troubleshooter_tool.diagnose_issue(error, &status)?;
|
|
|
|
let mut output = String::from("🔧 Issue Diagnosis:\n\n");
|
|
output.push_str(&format!("**Error:** {}\n\n", diagnosis.error));
|
|
output.push_str(&format!("**Root Cause:** {}\n\n", diagnosis.root_cause));
|
|
output.push_str(&format!("**Confidence:** {:.1}%\n\n", diagnosis.confidence * 100.0));
|
|
|
|
output.push_str("**Suggested Fixes:**\n\n");
|
|
for (idx, fix) in diagnosis.fixes.iter().enumerate() {
|
|
output.push_str(&format!("{}. {} (Risk: {:?})\n", idx + 1, fix.description, fix.risk));
|
|
output.push_str(" Commands:\n");
|
|
for cmd in &fix.commands {
|
|
output.push_str(&format!(" ```\n {}\n ```\n", cmd));
|
|
}
|
|
output.push_str(&format!(" Expected outcome: {}\n\n", fix.expected_outcome));
|
|
}
|
|
|
|
if !diagnosis.related_docs.is_empty() {
|
|
output.push_str("\n**Related Documentation:**\n");
|
|
for doc in &diagnosis.related_docs {
|
|
output.push_str(&format!("- [{}]({})\n", doc.title, doc.path));
|
|
}
|
|
}
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(
|
|
rust_mcp_sdk::schema::TextContent::from(output),
|
|
)],
|
|
is_error: Some(false),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn handle_validate_config_file(&self) -> impl Fn(CallToolRequest) -> Result<CallToolResult> + '_ {
|
|
|request: CallToolRequest| -> Result<CallToolResult> {
|
|
let arguments = request.params.arguments.unwrap_or_default();
|
|
let config_path = arguments
|
|
.get("config_path")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| ProvisioningError::InvalidInput("config_path is required".to_string()))?;
|
|
|
|
info!("Validating configuration file: {}", config_path);
|
|
|
|
let result = self.config_validator_tool.validate_config(config_path)?;
|
|
|
|
let mut output = String::new();
|
|
if result.valid {
|
|
output.push_str("✅ Configuration Valid\n\n");
|
|
} else {
|
|
output.push_str("❌ Configuration Invalid\n\n");
|
|
}
|
|
|
|
if !result.errors.is_empty() {
|
|
output.push_str("**Errors:**\n");
|
|
for error in &result.errors {
|
|
output.push_str(&format!("- {}\n", error.message));
|
|
if let Some(fix) = &error.fix {
|
|
output.push_str(&format!(" Fix: {}\n", fix));
|
|
}
|
|
}
|
|
output.push('\n');
|
|
}
|
|
|
|
if !result.warnings.is_empty() {
|
|
output.push_str("**Warnings:**\n");
|
|
for warning in &result.warnings {
|
|
output.push_str(&format!("- {}\n", warning.message));
|
|
output.push_str(&format!(" Recommendation: {}\n", warning.recommendation));
|
|
}
|
|
output.push('\n');
|
|
}
|
|
|
|
if !result.suggestions.is_empty() {
|
|
output.push_str("**Suggestions:**\n");
|
|
for suggestion in &result.suggestions {
|
|
output.push_str(&format!("- {}\n", suggestion));
|
|
}
|
|
output.push('\n');
|
|
}
|
|
|
|
if !result.docs.is_empty() {
|
|
output.push_str("**Related Documentation:**\n");
|
|
for doc in &result.docs {
|
|
output.push_str(&format!("- [{}]({})\n", doc.title, doc.path));
|
|
}
|
|
}
|
|
|
|
Ok(CallToolResult {
|
|
content: vec![rust_mcp_sdk::schema::ContentBlock::TextContent(
|
|
rust_mcp_sdk::schema::TextContent::from(output),
|
|
)],
|
|
is_error: Some(!result.valid),
|
|
meta: None,
|
|
structured_content: None,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
let args = Args::parse();
|
|
|
|
// Initialize tracing
|
|
let log_level = args.log_level.parse().unwrap_or(tracing::Level::INFO);
|
|
tracing_subscriber::fmt()
|
|
.with_max_level(log_level)
|
|
.with_target(false)
|
|
.init();
|
|
|
|
// Load configuration
|
|
let config = Config::load(args.config, args.provisioning_path, args.debug)?;
|
|
|
|
info!("Starting Provisioning MCP Server (Rust-native)");
|
|
info!("Provisioning path: {}", config.provisioning_path.display());
|
|
|
|
// Create and run server
|
|
let server = ProvisioningMCPServer::new(config)?;
|
|
server.run().await?;
|
|
|
|
Ok(())
|
|
}
|