coderlib 0.1.0

A Rust library for AI-powered code assistance and agentic system
Documentation
//! MCP type definitions and data structures

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// MCP server status enumeration
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum McpServerStatus {
    /// Server is healthy and responding
    Healthy,
    /// Server is responding but with degraded performance
    Degraded,
    /// Server is not responding or has errors
    Unhealthy,
    /// Server is not connected
    Disconnected,
    /// Server is starting up
    Starting,
    /// Server is shutting down
    Stopping,
}

/// Health information for an MCP server
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpServerHealth {
    /// Name of the server
    pub server_name: String,
    /// Current status
    pub status: McpServerStatus,
    /// Last successful tool call timestamp
    pub last_successful_call: Option<chrono::DateTime<chrono::Utc>>,
    /// Number of errors since last reset
    pub error_count: u64,
    /// Total number of calls made
    pub total_calls: u64,
    /// Average response time for tool calls
    pub average_response_time: std::time::Duration,
    /// Last error message if any
    pub last_error: Option<String>,
}

/// Statistics for MCP tool usage
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolStats {
    /// Tool name
    pub tool_name: String,
    /// Server name
    pub server_name: String,
    /// Number of times called
    pub call_count: u64,
    /// Number of successful calls
    pub success_count: u64,
    /// Number of failed calls
    pub error_count: u64,
    /// Average execution time
    pub average_execution_time: std::time::Duration,
    /// Last call timestamp
    pub last_call: Option<chrono::DateTime<chrono::Utc>>,
}

/// MCP connection information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpConnectionInfo {
    /// Server name
    pub server_name: String,
    /// Connection type (stdio, sse, etc.)
    pub transport_type: String,
    /// Connection established timestamp
    pub connected_at: chrono::DateTime<chrono::Utc>,
    /// Number of reconnections
    pub reconnection_count: u32,
    /// Server capabilities
    pub capabilities: Option<McpServerCapabilities>,
}

/// MCP server capabilities
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpServerCapabilities {
    /// Whether server supports tools
    pub tools: bool,
    /// Whether server supports resources
    pub resources: bool,
    /// Whether server supports prompts
    pub prompts: bool,
    /// Whether server supports logging
    pub logging: bool,
    /// Whether server supports progress notifications
    pub progress: bool,
}

impl Default for McpServerHealth {
    fn default() -> Self {
        Self {
            server_name: String::new(),
            status: McpServerStatus::Disconnected,
            last_successful_call: None,
            error_count: 0,
            total_calls: 0,
            average_response_time: std::time::Duration::from_millis(0),
            last_error: None,
        }
    }
}

impl Default for McpServerCapabilities {
    fn default() -> Self {
        Self {
            tools: true,
            resources: false,
            prompts: false,
            logging: false,
            progress: false,
        }
    }
}

impl McpServerHealth {
    /// Create a new health status for a server
    pub fn new(server_name: String) -> Self {
        Self {
            server_name,
            ..Default::default()
        }
    }
    
    /// Update health status after a successful call
    pub fn record_success(&mut self, response_time: std::time::Duration) {
        self.total_calls += 1;
        self.last_successful_call = Some(chrono::Utc::now());
        self.status = McpServerStatus::Healthy;
        
        // Update average response time
        let total_time = self.average_response_time.as_millis() as u64 * (self.total_calls - 1) + response_time.as_millis() as u64;
        self.average_response_time = std::time::Duration::from_millis(total_time / self.total_calls);
    }
    
    /// Update health status after a failed call
    pub fn record_error(&mut self, error: String) {
        self.total_calls += 1;
        self.error_count += 1;
        self.last_error = Some(error);
        
        // Determine status based on error rate
        let error_rate = self.error_count as f64 / self.total_calls as f64;
        self.status = if error_rate > 0.5 {
            McpServerStatus::Unhealthy
        } else if error_rate > 0.1 {
            McpServerStatus::Degraded
        } else {
            McpServerStatus::Healthy
        };
    }
    
    /// Check if the server is considered healthy
    pub fn is_healthy(&self) -> bool {
        matches!(self.status, McpServerStatus::Healthy)
    }
    
    /// Check if the server is available for use
    pub fn is_available(&self) -> bool {
        matches!(
            self.status,
            McpServerStatus::Healthy | McpServerStatus::Degraded
        )
    }
}

impl McpToolStats {
    /// Create new tool statistics
    pub fn new(tool_name: String, server_name: String) -> Self {
        Self {
            tool_name,
            server_name,
            call_count: 0,
            success_count: 0,
            error_count: 0,
            average_execution_time: std::time::Duration::from_millis(0),
            last_call: None,
        }
    }
    
    /// Record a successful tool execution
    pub fn record_success(&mut self, execution_time: std::time::Duration) {
        self.call_count += 1;
        self.success_count += 1;
        self.last_call = Some(chrono::Utc::now());
        
        // Update average execution time
        let total_time = self.average_execution_time.as_millis() as u64 * (self.call_count - 1) + execution_time.as_millis() as u64;
        self.average_execution_time = std::time::Duration::from_millis(total_time / self.call_count);
    }
    
    /// Record a failed tool execution
    pub fn record_error(&mut self) {
        self.call_count += 1;
        self.error_count += 1;
        self.last_call = Some(chrono::Utc::now());
    }
    
    /// Get success rate as a percentage
    pub fn success_rate(&self) -> f64 {
        if self.call_count == 0 {
            0.0
        } else {
            (self.success_count as f64 / self.call_count as f64) * 100.0
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_server_health_success_recording() {
        let mut health = McpServerHealth::new("test-server".to_string());
        
        health.record_success(std::time::Duration::from_millis(100));
        
        assert_eq!(health.total_calls, 1);
        assert_eq!(health.status, McpServerStatus::Healthy);
        assert!(health.last_successful_call.is_some());
        assert_eq!(health.average_response_time, std::time::Duration::from_millis(100));
    }
    
    #[test]
    fn test_server_health_error_recording() {
        let mut health = McpServerHealth::new("test-server".to_string());
        
        health.record_error("Test error".to_string());
        
        assert_eq!(health.total_calls, 1);
        assert_eq!(health.error_count, 1);
        assert_eq!(health.last_error, Some("Test error".to_string()));
    }
    
    #[test]
    fn test_tool_stats() {
        let mut stats = McpToolStats::new("test-tool".to_string(), "test-server".to_string());
        
        stats.record_success(std::time::Duration::from_millis(50));
        stats.record_error();
        
        assert_eq!(stats.call_count, 2);
        assert_eq!(stats.success_count, 1);
        assert_eq!(stats.error_count, 1);
        assert_eq!(stats.success_rate(), 50.0);
    }
}