Skip to main content

prism_mcp_rs/core/
enhanced_errors.rs

1//! Enhanced error types for better ergonomics
2//! Replaces generic String messages with structured errors
3
4use std::time::Duration;
5use thiserror::Error;
6
7/// Structured transport error types
8#[derive(Error, Debug)]
9pub enum TransportError {
10    #[error("Connection failed: {message}")]
11    ConnectionFailed { message: String },
12
13    #[error("Process start failed: {command} - {reason}")]
14    ProcessStartFailed { command: String, reason: String },
15
16    #[error("I/O operation failed: {operation}")]
17    IoError {
18        operation: String,
19        #[source]
20        source: std::io::Error,
21    },
22
23    #[error("Transport not connected")]
24    NotConnected,
25
26    #[error("Transport already closed")]
27    AlreadyClosed,
28}
29
30/// Structured protocol error types
31#[derive(Error, Debug)]
32pub enum ProtocolError {
33    #[error("Invalid message format: {message}")]
34    InvalidFormat { message: String },
35
36    #[error("Method not found: {method}")]
37    MethodNotFound { method: String },
38
39    #[error("Invalid parameters for {method}: {reason}")]
40    InvalidParameters { method: String, reason: String },
41
42    #[error("Protocol version mismatch: expected {expected}, got {actual}")]
43    VersionMismatch { expected: String, actual: String },
44}
45
46/// Enhanced MCP error with structured types
47#[derive(Error, Debug)]
48pub enum McpError {
49    #[error("Transport error: {source}")]
50    Transport {
51        #[from]
52        source: TransportError,
53    },
54
55    #[error("Protocol error: {source}")]
56    Protocol {
57        #[from]
58        source: ProtocolError,
59    },
60
61    #[error("Operation timeout after {duration:?}")]
62    Timeout { duration: Duration },
63
64    #[error("Connection error: {message}")]
65    Connection { message: String },
66
67    #[error("Serialization error: {message}")]
68    Serialization { message: String },
69
70    #[error("Validation error: {message}")]
71    Validation { message: String },
72
73    #[error("Internal error: {message}")]
74    Internal { message: String },
75}
76
77/// Result type alias for convenience
78pub type McpResult<T> = Result<T, McpError>;
79
80/// Implementations for backward compatibility and convenience
81impl McpError {
82    /// Create a transport error with message
83    pub fn transport(message: impl Into<String>) -> Self {
84        Self::Transport {
85            source: TransportError::ConnectionFailed {
86                message: message.into(),
87            },
88        }
89    }
90
91    /// Create a protocol error with message
92    pub fn protocol(message: impl Into<String>) -> Self {
93        Self::Protocol {
94            source: ProtocolError::InvalidFormat {
95                message: message.into(),
96            },
97        }
98    }
99
100    /// Create a timeout error
101    pub fn timeout(_message: impl Into<String>) -> Self {
102        Self::Timeout {
103            duration: Duration::from_secs(30), // Default timeout
104        }
105    }
106
107    /// Create a connection error
108    pub fn connection(message: impl Into<String>) -> Self {
109        Self::Connection {
110            message: message.into(),
111        }
112    }
113
114    /// Create a serialization error
115    pub fn serialization(message: impl Into<String>) -> Self {
116        Self::Serialization {
117            message: message.into(),
118        }
119    }
120
121    /// Create a validation error
122    pub fn validation(message: impl Into<String>) -> Self {
123        Self::Validation {
124            message: message.into(),
125        }
126    }
127
128    /// Create an internal error
129    pub fn internal(message: impl Into<String>) -> Self {
130        Self::Internal {
131            message: message.into(),
132        }
133    }
134
135    /// Check if error is recoverable (for retry logic)
136    pub fn is_recoverable(&self) -> bool {
137        match self {
138            McpError::Transport { source } => matches!(
139                source,
140                TransportError::ConnectionFailed { .. } | TransportError::IoError { .. }
141            ),
142            McpError::Timeout { .. } => true,
143            McpError::Connection { .. } => true,
144            _ => false,
145        }
146    }
147}
148
149/// Convert from std::io::Error
150impl From<std::io::Error> for McpError {
151    fn from(err: std::io::Error) -> Self {
152        Self::Transport {
153            source: TransportError::IoError {
154                operation: "I/O operation".to_string(),
155                source: err,
156            },
157        }
158    }
159}
160
161/// Convert from serde_json::Error  
162impl From<serde_json::Error> for McpError {
163    fn from(err: serde_json::Error) -> Self {
164        Self::Serialization {
165            message: err.to_string(),
166        }
167    }
168}