1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fmt;
10use uuid::Uuid;
11
12#[cfg(feature = "fancy-errors")]
13use miette::Diagnostic;
14
15pub type Result<T> = std::result::Result<T, Box<Error>>;
17
18#[derive(Debug, Serialize)]
20#[cfg_attr(feature = "fancy-errors", derive(Diagnostic))]
21pub struct Error {
22 pub id: Uuid,
24
25 pub kind: ErrorKind,
27
28 pub message: String,
30
31 pub context: ErrorContext,
33
34 #[serde(skip)]
36 pub source: Option<Box<Error>>,
37
38 #[cfg(debug_assertions)]
40 #[serde(skip)]
41 pub backtrace: std::backtrace::Backtrace,
42}
43
44impl Clone for Error {
45 fn clone(&self) -> Self {
46 Self {
47 id: self.id,
48 kind: self.kind,
49 message: self.message.clone(),
50 context: self.context.clone(),
51 source: self.source.clone(),
52 #[cfg(debug_assertions)]
53 backtrace: std::backtrace::Backtrace::capture(),
54 }
55 }
56}
57
58impl<'de> Deserialize<'de> for Error {
59 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
60 where
61 D: serde::Deserializer<'de>,
62 {
63 #[derive(Deserialize)]
64 struct ErrorData {
65 id: Uuid,
66 kind: ErrorKind,
67 message: String,
68 context: ErrorContext,
69 }
70
71 let data = ErrorData::deserialize(deserializer)?;
72 Ok(Self {
73 id: data.id,
74 kind: data.kind,
75 message: data.message,
76 context: data.context,
77 source: None,
78 #[cfg(debug_assertions)]
79 backtrace: std::backtrace::Backtrace::capture(),
80 })
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87pub enum ErrorKind {
88 Validation,
90
91 Authentication,
93
94 NotFound,
96
97 PermissionDenied,
99
100 BadRequest,
102
103 Internal,
105
106 Transport,
108
109 Serialization,
111
112 Protocol,
114
115 Timeout,
117
118 Unavailable,
120
121 RateLimited,
123
124 Configuration,
126
127 ExternalService,
129
130 Cancelled,
132
133 Handler,
135}
136
137#[derive(Debug, Clone, Default, Serialize, Deserialize)]
139pub struct ErrorContext {
140 pub operation: Option<String>,
142
143 pub component: Option<String>,
145
146 pub request_id: Option<String>,
148
149 pub user_id: Option<String>,
151
152 pub metadata: HashMap<String, serde_json::Value>,
154
155 pub timestamp: chrono::DateTime<chrono::Utc>,
157
158 pub retry_info: Option<RetryInfo>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct RetryInfo {
165 pub attempts: u32,
167
168 pub max_attempts: u32,
170
171 pub retry_after_ms: Option<u64>,
173}
174
175impl Error {
176 pub fn new(kind: ErrorKind, message: impl Into<String>) -> Box<Self> {
178 Box::new(Self {
179 id: Uuid::new_v4(),
180 kind,
181 message: message.into(),
182 context: ErrorContext {
183 timestamp: chrono::Utc::now(),
184 ..Default::default()
185 },
186 source: None,
187 #[cfg(debug_assertions)]
188 backtrace: std::backtrace::Backtrace::capture(),
189 })
190 }
191
192 pub fn validation(message: impl Into<String>) -> Box<Self> {
194 Self::new(ErrorKind::Validation, message)
195 }
196
197 pub fn authentication(message: impl Into<String>) -> Box<Self> {
199 Self::new(ErrorKind::Authentication, message)
200 }
201
202 pub fn not_found(message: impl Into<String>) -> Box<Self> {
204 Self::new(ErrorKind::NotFound, message)
205 }
206
207 pub fn permission_denied(message: impl Into<String>) -> Box<Self> {
209 Self::new(ErrorKind::PermissionDenied, message)
210 }
211
212 pub fn bad_request(message: impl Into<String>) -> Box<Self> {
214 Self::new(ErrorKind::BadRequest, message)
215 }
216
217 pub fn internal(message: impl Into<String>) -> Box<Self> {
219 Self::new(ErrorKind::Internal, message)
220 }
221
222 pub fn transport(message: impl Into<String>) -> Box<Self> {
224 Self::new(ErrorKind::Transport, message)
225 }
226
227 pub fn serialization(message: impl Into<String>) -> Box<Self> {
229 Self::new(ErrorKind::Serialization, message)
230 }
231
232 pub fn protocol(message: impl Into<String>) -> Box<Self> {
234 Self::new(ErrorKind::Protocol, message)
235 }
236
237 #[must_use]
239 pub fn rpc(code: i32, message: &str) -> Box<Self> {
240 Self::new(ErrorKind::Protocol, format!("RPC error {code}: {message}"))
241 }
242
243 pub fn timeout(message: impl Into<String>) -> Box<Self> {
245 Self::new(ErrorKind::Timeout, message)
246 }
247
248 pub fn unavailable(message: impl Into<String>) -> Box<Self> {
250 Self::new(ErrorKind::Unavailable, message)
251 }
252
253 pub fn rate_limited(message: impl Into<String>) -> Box<Self> {
255 Self::new(ErrorKind::RateLimited, message)
256 }
257
258 pub fn configuration(message: impl Into<String>) -> Box<Self> {
260 Self::new(ErrorKind::Configuration, message)
261 }
262
263 pub fn external_service(message: impl Into<String>) -> Box<Self> {
265 Self::new(ErrorKind::ExternalService, message)
266 }
267
268 pub fn cancelled(message: impl Into<String>) -> Box<Self> {
270 Self::new(ErrorKind::Cancelled, message)
271 }
272
273 pub fn handler(message: impl Into<String>) -> Box<Self> {
275 Self::new(ErrorKind::Handler, message)
276 }
277
278 #[must_use]
280 pub fn with_context(
281 mut self: Box<Self>,
282 key: impl Into<String>,
283 value: impl Into<serde_json::Value>,
284 ) -> Box<Self> {
285 self.context.metadata.insert(key.into(), value.into());
286 self
287 }
288
289 #[must_use]
291 pub fn with_operation(mut self: Box<Self>, operation: impl Into<String>) -> Box<Self> {
292 self.context.operation = Some(operation.into());
293 self
294 }
295
296 #[must_use]
298 pub fn with_component(mut self: Box<Self>, component: impl Into<String>) -> Box<Self> {
299 self.context.component = Some(component.into());
300 self
301 }
302
303 #[must_use]
305 pub fn with_request_id(mut self: Box<Self>, request_id: impl Into<String>) -> Box<Self> {
306 self.context.request_id = Some(request_id.into());
307 self
308 }
309
310 #[must_use]
312 pub fn with_user_id(mut self: Box<Self>, user_id: impl Into<String>) -> Box<Self> {
313 self.context.user_id = Some(user_id.into());
314 self
315 }
316
317 #[must_use]
319 pub fn with_retry_info(mut self: Box<Self>, retry_info: RetryInfo) -> Box<Self> {
320 self.context.retry_info = Some(retry_info);
321 self
322 }
323
324 #[must_use]
326 pub fn with_source(mut self: Box<Self>, source: Box<Self>) -> Box<Self> {
327 self.source = Some(source);
328 self
329 }
330
331 pub const fn is_retryable(&self) -> bool {
333 matches!(
334 self.kind,
335 ErrorKind::Timeout
336 | ErrorKind::Unavailable
337 | ErrorKind::Transport
338 | ErrorKind::ExternalService
339 | ErrorKind::RateLimited
340 )
341 }
342
343 pub const fn is_temporary(&self) -> bool {
345 matches!(
346 self.kind,
347 ErrorKind::Timeout
348 | ErrorKind::Unavailable
349 | ErrorKind::RateLimited
350 | ErrorKind::ExternalService
351 )
352 }
353
354 pub const fn http_status_code(&self) -> u16 {
356 match self.kind {
357 ErrorKind::Validation | ErrorKind::BadRequest => 400,
358 ErrorKind::Authentication => 401,
359 ErrorKind::PermissionDenied => 403,
360 ErrorKind::NotFound => 404,
361 ErrorKind::Timeout => 408,
362 ErrorKind::RateLimited => 429,
363 ErrorKind::Internal
364 | ErrorKind::Configuration
365 | ErrorKind::Serialization
366 | ErrorKind::Protocol
367 | ErrorKind::Handler => 500,
368 ErrorKind::Transport | ErrorKind::ExternalService | ErrorKind::Unavailable => 503,
369 ErrorKind::Cancelled => 499, }
371 }
372
373 pub const fn jsonrpc_error_code(&self) -> i32 {
375 match self.kind {
376 ErrorKind::BadRequest | ErrorKind::Validation => -32600, ErrorKind::Protocol => -32601, ErrorKind::Serialization => -32602, ErrorKind::Internal => -32603, ErrorKind::NotFound => -32001, ErrorKind::Authentication => -32002, ErrorKind::PermissionDenied => -32003, ErrorKind::Timeout => -32004, ErrorKind::Unavailable => -32005, ErrorKind::RateLimited => -32006, ErrorKind::Transport => -32007, ErrorKind::Configuration => -32008, ErrorKind::ExternalService => -32009, ErrorKind::Cancelled => -32010, ErrorKind::Handler => -32011, }
392 }
393}
394
395impl fmt::Display for Error {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 write!(f, "{}", self.message)?;
398
399 if let Some(operation) = &self.context.operation {
400 write!(f, " (operation: {operation})")?;
401 }
402
403 if let Some(component) = &self.context.component {
404 write!(f, " (component: {component})")?;
405 }
406
407 if let Some(request_id) = &self.context.request_id {
408 write!(f, " (request_id: {request_id})")?;
409 }
410
411 Ok(())
412 }
413}
414
415impl std::error::Error for Error {
416 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
417 None
421 }
422}
423
424impl ErrorKind {
425 #[must_use]
427 pub const fn description(self) -> &'static str {
428 match self {
429 Self::Validation => "Input validation failed",
430 Self::Authentication => "Authentication failed",
431 Self::NotFound => "Resource not found",
432 Self::PermissionDenied => "Permission denied",
433 Self::BadRequest => "Bad request",
434 Self::Internal => "Internal server error",
435 Self::Transport => "Transport error",
436 Self::Serialization => "Serialization error",
437 Self::Protocol => "Protocol error",
438 Self::Timeout => "Operation timed out",
439 Self::Unavailable => "Service unavailable",
440 Self::RateLimited => "Rate limit exceeded",
441 Self::Configuration => "Configuration error",
442 Self::ExternalService => "External service error",
443 Self::Cancelled => "Operation cancelled",
444 Self::Handler => "Handler execution error",
445 }
446 }
447}
448
449impl fmt::Display for ErrorKind {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 write!(f, "{}", self.description())
452 }
453}
454
455#[macro_export]
457macro_rules! mcp_error {
458 ($kind:expr, $message:expr) => {
459 $crate::error::Error::new($kind, $message)
460 };
461 ($kind:expr, $message:expr, $($key:expr => $value:expr),+) => {
462 {
463 let mut error = $crate::error::Error::new($kind, $message);
464 $(
465 error = error.with_context($key, $value);
466 )+
467 error
468 }
469 };
470}
471
472pub trait ErrorExt<T> {
474 fn with_mcp_error(self, kind: ErrorKind, message: impl Into<String>) -> Result<T>;
480
481 fn with_internal_error(self, message: impl Into<String>) -> Result<T>;
487}
488
489impl<T, E> ErrorExt<T> for std::result::Result<T, E>
490where
491 E: std::error::Error + Send + Sync + 'static,
492{
493 fn with_mcp_error(self, kind: ErrorKind, message: impl Into<String>) -> Result<T> {
494 self.map_err(|e| {
495 Error::new(kind, format!("{}: {}", message.into(), e))
496 .with_context("source_error", e.to_string())
497 })
498 }
499
500 fn with_internal_error(self, message: impl Into<String>) -> Result<T> {
501 self.with_mcp_error(ErrorKind::Internal, message)
502 }
503}
504
505impl From<serde_json::Error> for Box<Error> {
507 fn from(err: serde_json::Error) -> Self {
508 Error::serialization(format!("JSON serialization error: {err}"))
509 }
510}
511
512impl From<std::io::Error> for Box<Error> {
513 fn from(err: std::io::Error) -> Self {
514 Error::transport(format!("IO error: {err}"))
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 #[test]
523 fn test_error_creation() {
524 let error = Error::validation("Invalid input");
525 assert_eq!(error.kind, ErrorKind::Validation);
526 assert_eq!(error.message, "Invalid input");
527 }
528
529 #[test]
530 fn test_error_context() {
531 let error = Error::internal("Something went wrong")
532 .with_operation("test_operation")
533 .with_component("test_component")
534 .with_request_id("req-123")
535 .with_context("key", "value");
536
537 assert_eq!(error.context.operation, Some("test_operation".to_string()));
538 assert_eq!(error.context.component, Some("test_component".to_string()));
539 assert_eq!(error.context.request_id, Some("req-123".to_string()));
540 assert_eq!(
541 error.context.metadata.get("key"),
542 Some(&serde_json::Value::String("value".to_string()))
543 );
544 }
545
546 #[test]
547 fn test_error_properties() {
548 let retryable_error = Error::timeout("Request timed out");
549 assert!(retryable_error.is_retryable());
550 assert!(retryable_error.is_temporary());
551
552 let permanent_error = Error::validation("Invalid data");
553 assert!(!permanent_error.is_retryable());
554 assert!(!permanent_error.is_temporary());
555 }
556
557 #[test]
558 fn test_http_status_codes() {
559 assert_eq!(Error::validation("test").http_status_code(), 400);
560 assert_eq!(Error::not_found("test").http_status_code(), 404);
561 assert_eq!(Error::internal("test").http_status_code(), 500);
562 }
563
564 #[test]
565 fn test_error_macro() {
566 let error = mcp_error!(ErrorKind::Validation, "test message");
567 assert_eq!(error.kind, ErrorKind::Validation);
568 assert_eq!(error.message, "test message");
569
570 let error_with_context = mcp_error!(
571 ErrorKind::Internal,
572 "test message",
573 "key1" => "value1",
574 "key2" => 42
575 );
576 assert_eq!(error_with_context.context.metadata.len(), 2);
577 }
578}