use chrono::{DateTime, Utc};
use mcp_execution_core::{ServerConfig, ServerId};
use mcp_execution_introspector::ServerInfo;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use uuid::Uuid;
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct IntrospectServerParams {
pub server_id: String,
pub command: String,
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub env: HashMap<String, String>,
pub output_dir: Option<PathBuf>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct IntrospectServerResult {
pub server_id: String,
pub server_name: String,
pub tools_found: usize,
pub tools: Vec<ToolMetadata>,
pub session_id: Uuid,
pub expires_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ToolMetadata {
pub name: String,
pub description: String,
pub parameters: Vec<String>,
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct SaveCategorizedToolsParams {
pub session_id: Uuid,
pub categorized_tools: Vec<CategorizedTool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct CategorizedTool {
pub name: String,
pub category: String,
pub keywords: String,
pub short_description: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct SaveCategorizedToolsResult {
pub success: bool,
pub files_generated: usize,
pub output_dir: String,
pub categories: HashMap<String, usize>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub errors: Vec<ToolGenerationError>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ToolGenerationError {
pub tool_name: String,
pub error: String,
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct ListGeneratedServersParams {
pub base_dir: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ListGeneratedServersResult {
pub servers: Vec<GeneratedServerInfo>,
pub total_servers: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct GeneratedServerInfo {
pub id: String,
pub tool_count: usize,
pub generated_at: Option<DateTime<Utc>>,
pub output_dir: String,
}
#[derive(Debug, Clone)]
pub struct PendingGeneration {
pub server_id: ServerId,
pub server_info: ServerInfo,
pub config: ServerConfig,
pub output_dir: PathBuf,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
}
impl PendingGeneration {
pub const DEFAULT_TIMEOUT_MINUTES: i64 = 30;
#[must_use]
pub fn new(
server_id: ServerId,
server_info: ServerInfo,
config: ServerConfig,
output_dir: PathBuf,
) -> Self {
let now = Utc::now();
Self {
server_id,
server_info,
config,
output_dir,
created_at: now,
expires_at: now + chrono::Duration::minutes(Self::DEFAULT_TIMEOUT_MINUTES),
}
}
#[must_use]
pub fn is_expired(&self) -> bool {
Utc::now() > self.expires_at
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pending_generation_not_expired() {
let pending = create_test_pending();
assert!(!pending.is_expired());
}
#[test]
fn test_categorized_tool_serialization() {
let tool = CategorizedTool {
name: "create_issue".to_string(),
category: "issues".to_string(),
keywords: "create,issue,new".to_string(),
short_description: "Create a new issue".to_string(),
};
let json = serde_json::to_string(&tool).unwrap();
let _deserialized: CategorizedTool = serde_json::from_str(&json).unwrap();
}
fn create_test_pending() -> PendingGeneration {
use mcp_execution_core::ToolName;
use mcp_execution_introspector::{ServerCapabilities, ToolInfo};
let server_id = ServerId::new("test");
let server_info = ServerInfo {
id: server_id.clone(),
name: "Test Server".to_string(),
version: "1.0.0".to_string(),
capabilities: ServerCapabilities {
supports_tools: true,
supports_resources: false,
supports_prompts: false,
},
tools: vec![ToolInfo {
name: ToolName::new("test_tool"),
description: "Test tool description".to_string(),
input_schema: serde_json::json!({}),
output_schema: None,
}],
};
let config = ServerConfig::builder().command("echo".to_string()).build();
let output_dir = PathBuf::from("/tmp/test");
PendingGeneration::new(server_id, server_info, config, output_dir)
}
}