coderlib 0.1.0

A Rust library for AI-powered code assistance and agentic system
Documentation
//! MCP error types and handling

use thiserror::Error;

/// Errors that can occur during MCP operations
#[derive(Debug, Error)]
pub enum McpError {
    /// Error from the MCP client
    #[error("MCP client error: {0}")]
    #[cfg(feature = "mcp")]
    Client(#[from] crate::mcp::minimal::MinimalMcpError),
    
    /// Transport-related errors
    #[error("Transport error: {0}")]
    Transport(String),
    
    /// Configuration errors
    #[error("Configuration error: {0}")]
    Configuration(String),
    
    /// Tool execution errors
    #[error("Tool execution error: {0}")]
    ToolExecution(String),
    
    /// Permission denied errors
    #[error("Permission denied: {0}")]
    PermissionDenied(String),
    
    /// Server connection errors
    #[error("Connection error: {0}")]
    Connection(String),
    
    /// Server not found
    #[error("Server not found: {0}")]
    ServerNotFound(String),
    
    /// Tool not found
    #[error("Tool not found: {0}")]
    ToolNotFound(String),
    
    /// Timeout errors
    #[error("Operation timed out: {0}")]
    Timeout(String),
    
    /// Serialization/deserialization errors
    #[error("Serialization error: {0}")]
    Serialization(#[from] serde_json::Error),
    
    /// I/O errors
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),
    
    /// Server is not available
    #[error("Server not available: {0}")]
    ServerUnavailable(String),
    
    /// Invalid server state
    #[error("Invalid server state: {0}")]
    InvalidState(String),
    
    /// Resource not found
    #[error("Resource not found: {0}")]
    ResourceNotFound(String),
    
    /// Prompt not found
    #[error("Prompt not found: {0}")]
    PromptNotFound(String),
    
    /// Protocol version mismatch
    #[error("Protocol version mismatch: {0}")]
    ProtocolMismatch(String),
    
    /// Authentication failed
    #[error("Authentication failed: {0}")]
    AuthenticationFailed(String),
    
    /// Rate limit exceeded
    #[error("Rate limit exceeded: {0}")]
    RateLimitExceeded(String),
    
    /// Server capacity exceeded
    #[error("Server capacity exceeded: {0}")]
    CapacityExceeded(String),
    
    /// Generic MCP error
    #[error("MCP error: {0}")]
    Generic(String),
}

/// Result type for MCP operations
pub type McpResult<T> = Result<T, McpError>;

impl McpError {
    /// Create a transport error
    pub fn transport<S: Into<String>>(message: S) -> Self {
        Self::Transport(message.into())
    }
    
    /// Create a configuration error
    pub fn configuration<S: Into<String>>(message: S) -> Self {
        Self::Configuration(message.into())
    }
    
    /// Create a tool execution error
    pub fn tool_execution<S: Into<String>>(message: S) -> Self {
        Self::ToolExecution(message.into())
    }
    
    /// Create a permission denied error
    pub fn permission_denied<S: Into<String>>(message: S) -> Self {
        Self::PermissionDenied(message.into())
    }
    
    /// Create a connection error
    pub fn connection<S: Into<String>>(message: S) -> Self {
        Self::Connection(message.into())
    }
    
    /// Create a server not found error
    pub fn server_not_found<S: Into<String>>(server_name: S) -> Self {
        Self::ServerNotFound(server_name.into())
    }
    
    /// Create a tool not found error
    pub fn tool_not_found<S: Into<String>>(tool_name: S) -> Self {
        Self::ToolNotFound(tool_name.into())
    }
    
    /// Create a timeout error
    pub fn timeout<S: Into<String>>(message: S) -> Self {
        Self::Timeout(message.into())
    }
    
    /// Create a server unavailable error
    pub fn server_unavailable<S: Into<String>>(server_name: S) -> Self {
        Self::ServerUnavailable(server_name.into())
    }
    
    /// Create an invalid state error
    pub fn invalid_state<S: Into<String>>(message: S) -> Self {
        Self::InvalidState(message.into())
    }
    
    /// Create a resource not found error
    pub fn resource_not_found<S: Into<String>>(resource_uri: S) -> Self {
        Self::ResourceNotFound(resource_uri.into())
    }
    
    /// Create a prompt not found error
    pub fn prompt_not_found<S: Into<String>>(prompt_name: S) -> Self {
        Self::PromptNotFound(prompt_name.into())
    }
    
    /// Create a protocol mismatch error
    pub fn protocol_mismatch<S: Into<String>>(message: S) -> Self {
        Self::ProtocolMismatch(message.into())
    }
    
    /// Create an authentication failed error
    pub fn authentication_failed<S: Into<String>>(message: S) -> Self {
        Self::AuthenticationFailed(message.into())
    }
    
    /// Create a rate limit exceeded error
    pub fn rate_limit_exceeded<S: Into<String>>(message: S) -> Self {
        Self::RateLimitExceeded(message.into())
    }
    
    /// Create a capacity exceeded error
    pub fn capacity_exceeded<S: Into<String>>(message: S) -> Self {
        Self::CapacityExceeded(message.into())
    }
    
    /// Create a generic error
    pub fn generic<S: Into<String>>(message: S) -> Self {
        Self::Generic(message.into())
    }
    
    /// Check if this error is retryable
    pub fn is_retryable(&self) -> bool {
        matches!(
            self,
            McpError::Transport(_)
                | McpError::Connection(_)
                | McpError::Timeout(_)
                | McpError::ServerUnavailable(_)
                | McpError::RateLimitExceeded(_)
                | McpError::CapacityExceeded(_)
        )
    }
    
    /// Check if this error is a client error (4xx equivalent)
    pub fn is_client_error(&self) -> bool {
        matches!(
            self,
            McpError::Configuration(_)
                | McpError::PermissionDenied(_)
                | McpError::ServerNotFound(_)
                | McpError::ToolNotFound(_)
                | McpError::ResourceNotFound(_)
                | McpError::PromptNotFound(_)
                | McpError::AuthenticationFailed(_)
                | McpError::Serialization(_)
        )
    }
    
    /// Check if this error is a server error (5xx equivalent)
    pub fn is_server_error(&self) -> bool {
        matches!(
            self,
            McpError::ToolExecution(_)
                | McpError::ServerUnavailable(_)
                | McpError::InvalidState(_)
                | McpError::CapacityExceeded(_)
                | McpError::Generic(_)
        )
    }
    
    /// Get error category for logging/metrics
    pub fn category(&self) -> &'static str {
        match self {
            #[cfg(feature = "mcp")]
            McpError::Client(_) => "client",
            McpError::Transport(_) => "transport",
            McpError::Configuration(_) => "configuration",
            McpError::ToolExecution(_) => "tool_execution",
            McpError::PermissionDenied(_) => "permission",
            McpError::Connection(_) => "connection",
            McpError::ServerNotFound(_) => "server_not_found",
            McpError::ToolNotFound(_) => "tool_not_found",
            McpError::Timeout(_) => "timeout",
            McpError::Serialization(_) => "serialization",
            McpError::Io(_) => "io",
            McpError::ServerUnavailable(_) => "server_unavailable",
            McpError::InvalidState(_) => "invalid_state",
            McpError::ResourceNotFound(_) => "resource_not_found",
            McpError::PromptNotFound(_) => "prompt_not_found",
            McpError::ProtocolMismatch(_) => "protocol_mismatch",
            McpError::AuthenticationFailed(_) => "authentication",
            McpError::RateLimitExceeded(_) => "rate_limit",
            McpError::CapacityExceeded(_) => "capacity",
            McpError::Generic(_) => "generic",
        }
    }
}

/// Convert tool errors to MCP errors
impl From<crate::tools::ToolError> for McpError {
    fn from(error: crate::tools::ToolError) -> Self {
        match error {
            crate::tools::ToolError::NotFound(msg) => McpError::ToolNotFound(msg),
            crate::tools::ToolError::PermissionDenied(msg) => McpError::PermissionDenied(msg),
            crate::tools::ToolError::ExecutionFailed(msg) => McpError::ToolExecution(msg),
            crate::tools::ToolError::InvalidParameters(msg) => McpError::Configuration(msg),
            crate::tools::ToolError::Timeout(msg) => McpError::Timeout(msg),
            crate::tools::ToolError::Unavailable(msg) => McpError::ServerUnavailable(msg),
            crate::tools::ToolError::SecurityViolation(msg) => McpError::PermissionDenied(msg),
            crate::tools::ToolError::Io(err) => McpError::Io(err),
            crate::tools::ToolError::Json(err) => McpError::Serialization(err),
            crate::tools::ToolError::Git(err) => McpError::ToolExecution(err.to_string()),
        }
    }
}

/// Convert MCP errors to tool errors
impl From<McpError> for crate::tools::ToolError {
    fn from(error: McpError) -> Self {
        match error {
            McpError::ToolNotFound(msg) => crate::tools::ToolError::NotFound(msg),
            McpError::PermissionDenied(msg) => crate::tools::ToolError::PermissionDenied(msg),
            McpError::ToolExecution(msg) => crate::tools::ToolError::ExecutionFailed(msg),
            McpError::Configuration(msg) => crate::tools::ToolError::InvalidParameters(msg),
            McpError::Timeout(msg) => crate::tools::ToolError::Timeout(msg),
            McpError::ServerUnavailable(msg) => crate::tools::ToolError::Unavailable(msg),
            McpError::Serialization(err) => crate::tools::ToolError::Json(err),
            McpError::Io(err) => crate::tools::ToolError::Io(err),
            _ => crate::tools::ToolError::ExecutionFailed(error.to_string()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_error_creation() {
        let error = McpError::server_not_found("test-server");
        assert_eq!(error.to_string(), "Server not found: test-server");
        assert_eq!(error.category(), "server_not_found");
    }
    
    #[test]
    fn test_error_retryable() {
        assert!(McpError::timeout("test").is_retryable());
        assert!(McpError::connection("test").is_retryable());
        assert!(!McpError::configuration("test").is_retryable());
        assert!(!McpError::permission_denied("test").is_retryable());
    }
    
    #[test]
    fn test_error_categories() {
        assert!(McpError::configuration("test").is_client_error());
        assert!(McpError::tool_execution("test").is_server_error());
        assert!(!McpError::timeout("test").is_client_error());
        assert!(!McpError::timeout("test").is_server_error());
    }
}