ultrafast_mcp_core/
error.rs

1//! Error handling for UltraFast MCP Core
2
3use thiserror::Error;
4
5/// MCPResult is the canonical result type for all MCP operations.
6pub type MCPResult<T> = Result<T, MCPError>;
7
8/// Main error type for MCP operations
9#[derive(Debug, Error)]
10pub enum MCPError {
11    #[error("Protocol error: {0}")]
12    Protocol(#[from] ProtocolError),
13
14    #[error("Transport error: {0}")]
15    Transport(#[from] TransportError),
16
17    #[error("Tool execution error: {0}")]
18    ToolExecution(#[from] ToolError),
19
20    #[error("Resource error: {0}")]
21    Resource(#[from] ResourceError),
22
23    #[error("Authentication error: {0}")]
24    Authentication(#[from] AuthenticationError),
25
26    #[error("Validation error: {0}")]
27    Validation(#[from] ValidationError),
28
29    #[error("Rate limiting error: {0}")]
30    RateLimit(#[from] RateLimitError),
31
32    #[error("Serialization error: {0}")]
33    Serialization(#[from] serde_json::Error),
34
35    #[error("IO error: {0}")]
36    Io(#[from] std::io::Error),
37
38    #[error("Other error: {0}")]
39    Other(#[from] anyhow::Error),
40}
41
42impl MCPError {
43    pub fn invalid_params(msg: String) -> Self {
44        MCPError::Protocol(ProtocolError::InvalidParams(msg))
45    }
46
47    pub fn method_not_found(msg: String) -> Self {
48        MCPError::Protocol(ProtocolError::MethodNotFound(msg))
49    }
50
51    pub fn not_found(msg: String) -> Self {
52        MCPError::Protocol(ProtocolError::NotFound(msg))
53    }
54
55    pub fn invalid_request(msg: String) -> Self {
56        MCPError::Protocol(ProtocolError::InvalidRequest(msg))
57    }
58
59    pub fn invalid_response(msg: String) -> Self {
60        MCPError::Protocol(ProtocolError::InvalidResponse(msg))
61    }
62
63    pub fn serialization_error(msg: String) -> Self {
64        MCPError::Protocol(ProtocolError::SerializationError(msg))
65    }
66
67    pub fn transport_error(msg: String) -> Self {
68        MCPError::Transport(TransportError::ConnectionFailed(msg))
69    }
70
71    pub fn request_timeout() -> Self {
72        MCPError::Protocol(ProtocolError::RequestTimeout)
73    }
74
75    pub fn internal_error(msg: String) -> Self {
76        MCPError::Protocol(ProtocolError::InternalError(msg))
77    }
78}
79
80/// Protocol-related errors
81#[derive(Debug, Error)]
82pub enum ProtocolError {
83    #[error("Invalid JSON-RPC version: {0}")]
84    InvalidVersion(String),
85
86    #[error("Invalid request ID: {0}")]
87    InvalidRequestId(String),
88
89    #[error("Method not found: {0}")]
90    MethodNotFound(String),
91
92    #[error("Invalid parameters: {0}")]
93    InvalidParams(String),
94
95    #[error("Invalid request: {0}")]
96    InvalidRequest(String),
97
98    #[error("Invalid response: {0}")]
99    InvalidResponse(String),
100
101    #[error("Request timeout")]
102    RequestTimeout,
103
104    #[error("Internal error: {0}")]
105    InternalError(String),
106
107    #[error("Initialization failed: {0}")]
108    InitializationFailed(String),
109
110    #[error("Capability not supported: {0}")]
111    CapabilityNotSupported(String),
112
113    #[error("Not found: {0}")]
114    NotFound(String),
115
116    #[error("Connection closed")]
117    ConnectionClosed,
118
119    #[error("Transport error: {0}")]
120    TransportError(String),
121
122    #[error("Serialization error: {0}")]
123    SerializationError(String),
124
125    #[error("Authentication error: {0}")]
126    AuthenticationError(String),
127}
128
129/// Transport-related errors
130#[derive(Debug, Error)]
131pub enum TransportError {
132    #[error("Connection failed: {0}")]
133    ConnectionFailed(String),
134
135    #[error("Connection closed")]
136    ConnectionClosed,
137
138    #[error("Send failed: {0}")]
139    SendFailed(String),
140
141    #[error("Receive failed: {0}")]
142    ReceiveFailed(String),
143}
144
145/// Tool execution errors
146#[derive(Debug, Error)]
147pub enum ToolError {
148    #[error("Tool not found: {0}")]
149    NotFound(String),
150
151    #[error("Tool execution failed: {0}")]
152    ExecutionFailed(String),
153
154    #[error("Invalid input: {0}")]
155    InvalidInput(String),
156
157    #[error("Schema validation failed: {0}")]
158    SchemaValidation(String),
159}
160
161/// Resource-related errors
162#[derive(Debug, Error)]
163pub enum ResourceError {
164    #[error("Resource not found: {0}")]
165    NotFound(String),
166
167    #[error("Access denied: {0}")]
168    AccessDenied(String),
169
170    #[error("Invalid URI: {0}")]
171    InvalidUri(String),
172
173    #[error("Content type mismatch: expected {expected}, got {actual}")]
174    ContentTypeMismatch { expected: String, actual: String },
175}
176
177/// Authentication errors
178#[derive(Debug, Error)]
179pub enum AuthenticationError {
180    #[error("Invalid credentials")]
181    InvalidCredentials,
182
183    #[error("Token expired")]
184    TokenExpired,
185
186    #[error("Insufficient permissions for resource: {resource}")]
187    InsufficientPermissions { resource: String },
188
189    #[error("OAuth error: {error} - {description}")]
190    OAuthError { error: String, description: String },
191}
192
193/// Validation errors
194#[derive(Debug, Error)]
195pub enum ValidationError {
196    #[error("Schema validation failed for field '{field}': {details}")]
197    SchemaValidation { field: String, details: String },
198
199    #[error("Required field '{field}' is missing")]
200    RequiredField { field: String },
201
202    #[error("Invalid format for field '{field}': expected {expected}")]
203    InvalidFormat { field: String, expected: String },
204
205    #[error("Value for field '{field}' is out of range: {actual} (expected {min}..{max})")]
206    ValueOutOfRange {
207        field: String,
208        min: String,
209        max: String,
210        actual: String,
211    },
212}
213
214/// Rate limiting errors
215#[derive(Debug, Error)]
216pub enum RateLimitError {
217    #[error("Too many requests. Retry after {retry_after}ms. Limit: {limit}")]
218    TooManyRequests { retry_after: u64, limit: u32 },
219
220    #[error("Quota exceeded: {quota} requests per {period}")]
221    QuotaExceeded { quota: u32, period: String },
222}
223
224/// Standard JSON-RPC error codes
225pub mod error_codes {
226    pub const PARSE_ERROR: i32 = -32700;
227    pub const INVALID_REQUEST: i32 = -32600;
228    pub const METHOD_NOT_FOUND: i32 = -32601;
229    pub const INVALID_PARAMS: i32 = -32602;
230    pub const INTERNAL_ERROR: i32 = -32603;
231
232    // MCP-specific error codes
233    pub const INITIALIZATION_FAILED: i32 = -32000;
234    pub const CAPABILITY_NOT_SUPPORTED: i32 = -32001;
235    pub const RESOURCE_NOT_FOUND: i32 = -32002;
236    pub const TOOL_EXECUTION_ERROR: i32 = -32003;
237    pub const INVALID_URI: i32 = -32004;
238    pub const ACCESS_DENIED: i32 = -32005;
239    pub const AUTHENTICATION_ERROR: i32 = -32006;
240    pub const VALIDATION_ERROR: i32 = -32007;
241    pub const RATE_LIMIT_ERROR: i32 = -32008;
242}
243
244impl From<crate::protocol::jsonrpc::JsonRpcError> for MCPError {
245    fn from(err: crate::protocol::jsonrpc::JsonRpcError) -> Self {
246        match err.code {
247            error_codes::PARSE_ERROR => {
248                MCPError::Protocol(ProtocolError::SerializationError(err.message))
249            }
250            error_codes::INVALID_REQUEST => {
251                MCPError::Protocol(ProtocolError::InvalidRequest(err.message))
252            }
253            error_codes::METHOD_NOT_FOUND => {
254                MCPError::Protocol(ProtocolError::MethodNotFound(err.message))
255            }
256            error_codes::INVALID_PARAMS => {
257                MCPError::Protocol(ProtocolError::InvalidParams(err.message))
258            }
259            error_codes::INTERNAL_ERROR => {
260                MCPError::Protocol(ProtocolError::InternalError(err.message))
261            }
262            error_codes::INITIALIZATION_FAILED => {
263                MCPError::Protocol(ProtocolError::InitializationFailed(err.message))
264            }
265            error_codes::CAPABILITY_NOT_SUPPORTED => {
266                MCPError::Protocol(ProtocolError::CapabilityNotSupported(err.message))
267            }
268            error_codes::RESOURCE_NOT_FOUND => {
269                MCPError::Resource(ResourceError::NotFound(err.message))
270            }
271            error_codes::TOOL_EXECUTION_ERROR => {
272                MCPError::ToolExecution(ToolError::ExecutionFailed(err.message))
273            }
274            error_codes::INVALID_URI => MCPError::Resource(ResourceError::InvalidUri(err.message)),
275            error_codes::ACCESS_DENIED => {
276                MCPError::Resource(ResourceError::AccessDenied(err.message))
277            }
278            error_codes::AUTHENTICATION_ERROR => {
279                MCPError::Authentication(AuthenticationError::InvalidCredentials)
280            }
281            error_codes::VALIDATION_ERROR => {
282                MCPError::Validation(ValidationError::SchemaValidation {
283                    field: "unknown".to_string(),
284                    details: err.message,
285                })
286            }
287            error_codes::RATE_LIMIT_ERROR => MCPError::RateLimit(RateLimitError::TooManyRequests {
288                retry_after: 0,
289                limit: 0,
290            }),
291            _ => MCPError::Protocol(ProtocolError::InternalError(err.message)),
292        }
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn test_error_variant_conversion() {
302        let error = MCPError::invalid_params("test".to_string());
303        assert!(matches!(
304            error,
305            MCPError::Protocol(ProtocolError::InvalidParams(_))
306        ));
307
308        let error = MCPError::method_not_found("test".to_string());
309        assert!(matches!(
310            error,
311            MCPError::Protocol(ProtocolError::MethodNotFound(_))
312        ));
313    }
314
315    #[test]
316    fn test_error_creation() {
317        let error = MCPError::internal_error("test error".to_string());
318        assert!(matches!(
319            error,
320            MCPError::Protocol(ProtocolError::InternalError(_))
321        ));
322    }
323}