turbomcp_protocol/
error.rs

1//! Comprehensive error handling with rich context preservation.
2//!
3//! This module provides a sophisticated error handling system that captures
4//! detailed context about failures, supports error chaining, and integrates
5//! with observability systems.
6
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fmt;
10use uuid::Uuid;
11
12#[cfg(feature = "fancy-errors")]
13use miette::Diagnostic;
14
15/// Result type alias for MCP operations
16pub type Result<T> = std::result::Result<T, Box<Error>>;
17
18/// Comprehensive error type with rich context information
19#[derive(Debug, Serialize)]
20#[cfg_attr(feature = "fancy-errors", derive(Diagnostic))]
21pub struct Error {
22    /// Unique identifier for this error instance
23    pub id: Uuid,
24
25    /// Error classification
26    pub kind: ErrorKind,
27
28    /// Human-readable error message
29    pub message: String,
30
31    /// Additional contextual information
32    pub context: ErrorContext,
33
34    /// Optional source error that caused this error
35    #[serde(skip)]
36    pub source: Option<Box<Error>>,
37
38    /// Stack trace information (when available)
39    #[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/// Error classification for programmatic handling
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87pub enum ErrorKind {
88    // ============================================================================
89    // MCP-Specific Errors (MCP 2025-06-18 specification)
90    // ============================================================================
91    /// Tool not found (MCP error code -32001)
92    ToolNotFound,
93
94    /// Tool execution failed (MCP error code -32002)
95    ToolExecutionFailed,
96
97    /// Prompt not found (MCP error code -32003)
98    PromptNotFound,
99
100    /// Resource not found (MCP error code -32004)
101    ResourceNotFound,
102
103    /// Resource access denied (MCP error code -32005)
104    ResourceAccessDenied,
105
106    /// Capability not supported (MCP error code -32006)
107    CapabilityNotSupported,
108
109    /// Protocol version mismatch (MCP error code -32007)
110    ProtocolVersionMismatch,
111
112    // ============================================================================
113    // JSON-RPC Standard Errors
114    // ============================================================================
115    /// Input validation failed (JSON-RPC -32602)
116    Validation,
117
118    /// Request was malformed or invalid (JSON-RPC -32600)
119    BadRequest,
120
121    /// Server internal error (JSON-RPC -32603)
122    Internal,
123
124    /// Serialization/deserialization error (JSON-RPC -32602)
125    Serialization,
126
127    /// Protocol violation or incompatibility (JSON-RPC -32601)
128    Protocol,
129
130    // ============================================================================
131    // General Application Errors
132    // ============================================================================
133    /// Authentication or authorization failed
134    Authentication,
135
136    /// Operation is not permitted
137    PermissionDenied,
138
139    /// Network or transport error
140    Transport,
141
142    /// Operation timed out
143    Timeout,
144
145    /// Resource is temporarily unavailable
146    Unavailable,
147
148    /// Rate limit exceeded (MCP error code -32009)
149    RateLimited,
150
151    /// Server overloaded (MCP error code -32010)
152    ServerOverloaded,
153
154    /// Configuration error
155    Configuration,
156
157    /// External dependency failed
158    ExternalService,
159
160    /// Operation was cancelled
161    Cancelled,
162
163    /// Security violation or constraint failure
164    Security,
165
166    // ============================================================================
167    // Deprecated
168    // ============================================================================
169    /// Generic handler execution error (deprecated - use specific error kinds)
170    ///
171    /// Replaced by:
172    /// - `ToolExecutionFailed` for tool errors
173    /// - `PromptNotFound` for prompt errors
174    /// - `ResourceNotFound` or `ResourceAccessDenied` for resource errors
175    #[deprecated(
176        since = "2.1.0",
177        note = "Use specific error kinds: ToolExecutionFailed, PromptNotFound, ResourceNotFound, etc."
178    )]
179    Handler,
180
181    /// Generic not found error (deprecated - use specific error kinds)
182    ///
183    /// Replaced by:
184    /// - `ToolNotFound` for tools
185    /// - `PromptNotFound` for prompts
186    /// - `ResourceNotFound` for resources
187    #[deprecated(
188        since = "2.1.0",
189        note = "Use specific error kinds: ToolNotFound, PromptNotFound, ResourceNotFound"
190    )]
191    NotFound,
192}
193
194/// Rich contextual information for errors
195#[derive(Debug, Clone, Default, Serialize, Deserialize)]
196pub struct ErrorContext {
197    /// Operation that was being performed
198    pub operation: Option<String>,
199
200    /// Component where error occurred
201    pub component: Option<String>,
202
203    /// Request ID for tracing
204    pub request_id: Option<String>,
205
206    /// User ID (if applicable)
207    pub user_id: Option<String>,
208
209    /// Additional metadata
210    pub metadata: HashMap<String, serde_json::Value>,
211
212    /// Timestamp when error occurred
213    pub timestamp: chrono::DateTime<chrono::Utc>,
214
215    /// Retry information
216    pub retry_info: Option<RetryInfo>,
217}
218
219/// Information about retry attempts
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct RetryInfo {
222    /// Number of attempts made
223    pub attempts: u32,
224
225    /// Maximum attempts allowed
226    pub max_attempts: u32,
227
228    /// Next retry delay in milliseconds
229    pub retry_after_ms: Option<u64>,
230}
231
232impl Error {
233    /// Create a new error with the specified kind and message
234    pub fn new(kind: ErrorKind, message: impl Into<String>) -> Box<Self> {
235        Box::new(Self {
236            id: Uuid::new_v4(),
237            kind,
238            message: message.into(),
239            context: ErrorContext {
240                timestamp: chrono::Utc::now(),
241                ..Default::default()
242            },
243            source: None,
244            #[cfg(debug_assertions)]
245            backtrace: std::backtrace::Backtrace::capture(),
246        })
247    }
248
249    /// Create a validation error
250    pub fn validation(message: impl Into<String>) -> Box<Self> {
251        Self::new(ErrorKind::Validation, message)
252    }
253
254    /// Create an invalid parameters error (MCP -32602)
255    ///
256    /// This is the standard MCP error code for parameter validation failures,
257    /// including missing required parameters, invalid types, out-of-range values,
258    /// or any other parameter-related validation errors.
259    ///
260    /// # Example
261    ///
262    /// ```rust
263    /// use turbomcp_protocol::Error;
264    ///
265    /// let error = Error::invalid_params("Email must be valid");
266    /// assert_eq!(error.jsonrpc_error_code(), -32602);
267    /// ```
268    pub fn invalid_params(message: impl Into<String>) -> Box<Self> {
269        Self::new(ErrorKind::Validation, message)
270    }
271
272    /// Create an authentication error
273    pub fn authentication(message: impl Into<String>) -> Box<Self> {
274        Self::new(ErrorKind::Authentication, message)
275    }
276
277    /// Create a not found error
278    #[deprecated(
279        since = "2.1.0",
280        note = "Use specific constructors: tool_not_found(), prompt_not_found(), or resource_not_found()"
281    )]
282    pub fn not_found(message: impl Into<String>) -> Box<Self> {
283        #[allow(deprecated)]
284        Self::new(ErrorKind::NotFound, message)
285    }
286
287    /// Create a permission denied error
288    pub fn permission_denied(message: impl Into<String>) -> Box<Self> {
289        Self::new(ErrorKind::PermissionDenied, message)
290    }
291
292    /// Create a bad request error
293    pub fn bad_request(message: impl Into<String>) -> Box<Self> {
294        Self::new(ErrorKind::BadRequest, message)
295    }
296
297    /// Create an internal error
298    pub fn internal(message: impl Into<String>) -> Box<Self> {
299        Self::new(ErrorKind::Internal, message)
300    }
301
302    /// Create a transport error
303    pub fn transport(message: impl Into<String>) -> Box<Self> {
304        Self::new(ErrorKind::Transport, message)
305    }
306
307    /// Create a serialization error
308    pub fn serialization(message: impl Into<String>) -> Box<Self> {
309        Self::new(ErrorKind::Serialization, message)
310    }
311
312    /// Create a protocol error
313    pub fn protocol(message: impl Into<String>) -> Box<Self> {
314        Self::new(ErrorKind::Protocol, message)
315    }
316
317    /// Create a JSON-RPC error
318    #[must_use]
319    pub fn rpc(code: i32, message: &str) -> Box<Self> {
320        Self::new(ErrorKind::Protocol, format!("RPC error {code}: {message}"))
321    }
322
323    /// Create a timeout error
324    pub fn timeout(message: impl Into<String>) -> Box<Self> {
325        Self::new(ErrorKind::Timeout, message)
326    }
327
328    /// Create an unavailable error
329    pub fn unavailable(message: impl Into<String>) -> Box<Self> {
330        Self::new(ErrorKind::Unavailable, message)
331    }
332
333    /// Create a rate limited error
334    pub fn rate_limited(message: impl Into<String>) -> Box<Self> {
335        Self::new(ErrorKind::RateLimited, message)
336    }
337
338    /// Create a configuration error
339    pub fn configuration(message: impl Into<String>) -> Box<Self> {
340        Self::new(ErrorKind::Configuration, message)
341    }
342
343    /// Create an external service error
344    pub fn external_service(message: impl Into<String>) -> Box<Self> {
345        Self::new(ErrorKind::ExternalService, message)
346    }
347
348    /// Create a cancelled error
349    pub fn cancelled(message: impl Into<String>) -> Box<Self> {
350        Self::new(ErrorKind::Cancelled, message)
351    }
352
353    /// Create a handler error - for compatibility with macro-generated code
354    #[deprecated(
355        since = "2.1.0",
356        note = "Use specific error constructors: tool_not_found(), tool_execution_failed(), etc."
357    )]
358    pub fn handler(message: impl Into<String>) -> Box<Self> {
359        #[allow(deprecated)]
360        Self::new(ErrorKind::Handler, message)
361    }
362
363    /// Create a security error
364    pub fn security(message: impl Into<String>) -> Box<Self> {
365        Self::new(ErrorKind::Security, message)
366    }
367
368    // ============================================================================
369    // MCP-Specific Error Constructors (MCP 2025-06-18)
370    // ============================================================================
371
372    /// Create a tool not found error (MCP error code -32001)
373    ///
374    /// # Example
375    /// ```rust
376    /// use turbomcp_protocol::Error;
377    ///
378    /// let error = Error::tool_not_found("calculate");
379    /// assert_eq!(error.jsonrpc_error_code(), -32001);
380    /// ```
381    pub fn tool_not_found(tool_name: impl Into<String>) -> Box<Self> {
382        Self::new(
383            ErrorKind::ToolNotFound,
384            format!("Tool not found: {}", tool_name.into()),
385        )
386        .with_operation("tool_lookup")
387        .with_component("tool_registry")
388    }
389
390    /// Create a tool execution failed error (MCP error code -32002)
391    ///
392    /// # Example
393    /// ```rust
394    /// use turbomcp_protocol::Error;
395    ///
396    /// let error = Error::tool_execution_failed("calculate", "Division by zero");
397    /// assert_eq!(error.jsonrpc_error_code(), -32002);
398    /// ```
399    pub fn tool_execution_failed(
400        tool_name: impl Into<String>,
401        reason: impl Into<String>,
402    ) -> Box<Self> {
403        Self::new(
404            ErrorKind::ToolExecutionFailed,
405            format!("Tool '{}' failed: {}", tool_name.into(), reason.into()),
406        )
407        .with_operation("tool_execution")
408    }
409
410    /// Create a prompt not found error (MCP error code -32003)
411    ///
412    /// # Example
413    /// ```rust
414    /// use turbomcp_protocol::Error;
415    ///
416    /// let error = Error::prompt_not_found("code_review");
417    /// assert_eq!(error.jsonrpc_error_code(), -32003);
418    /// ```
419    pub fn prompt_not_found(prompt_name: impl Into<String>) -> Box<Self> {
420        Self::new(
421            ErrorKind::PromptNotFound,
422            format!("Prompt not found: {}", prompt_name.into()),
423        )
424        .with_operation("prompt_lookup")
425        .with_component("prompt_registry")
426    }
427
428    /// Create a resource not found error (MCP error code -32004)
429    ///
430    /// # Example
431    /// ```rust
432    /// use turbomcp_protocol::Error;
433    ///
434    /// let error = Error::resource_not_found("file:///docs/api.md");
435    /// assert_eq!(error.jsonrpc_error_code(), -32004);
436    /// ```
437    pub fn resource_not_found(uri: impl Into<String>) -> Box<Self> {
438        Self::new(
439            ErrorKind::ResourceNotFound,
440            format!("Resource not found: {}", uri.into()),
441        )
442        .with_operation("resource_lookup")
443        .with_component("resource_provider")
444    }
445
446    /// Create a resource access denied error (MCP error code -32005)
447    ///
448    /// # Example
449    /// ```rust
450    /// use turbomcp_protocol::Error;
451    ///
452    /// let error = Error::resource_access_denied("file:///etc/passwd", "Path outside allowed directory");
453    /// assert_eq!(error.jsonrpc_error_code(), -32005);
454    /// ```
455    pub fn resource_access_denied(uri: impl Into<String>, reason: impl Into<String>) -> Box<Self> {
456        Self::new(
457            ErrorKind::ResourceAccessDenied,
458            format!(
459                "Access denied to resource '{}': {}",
460                uri.into(),
461                reason.into()
462            ),
463        )
464        .with_operation("resource_access")
465        .with_component("resource_security")
466    }
467
468    /// Create a capability not supported error (MCP error code -32006)
469    ///
470    /// # Example
471    /// ```rust
472    /// use turbomcp_protocol::Error;
473    ///
474    /// let error = Error::capability_not_supported("sampling");
475    /// assert_eq!(error.jsonrpc_error_code(), -32006);
476    /// ```
477    pub fn capability_not_supported(capability: impl Into<String>) -> Box<Self> {
478        Self::new(
479            ErrorKind::CapabilityNotSupported,
480            format!("Capability not supported: {}", capability.into()),
481        )
482        .with_operation("capability_check")
483    }
484
485    /// Create a protocol version mismatch error (MCP error code -32007)
486    ///
487    /// # Example
488    /// ```rust
489    /// use turbomcp_protocol::Error;
490    ///
491    /// let error = Error::protocol_version_mismatch("2024-11-05", "2025-06-18");
492    /// assert_eq!(error.jsonrpc_error_code(), -32007);
493    /// ```
494    pub fn protocol_version_mismatch(
495        client_version: impl Into<String>,
496        server_version: impl Into<String>,
497    ) -> Box<Self> {
498        Self::new(
499            ErrorKind::ProtocolVersionMismatch,
500            format!(
501                "Protocol version mismatch: client={}, server={}",
502                client_version.into(),
503                server_version.into()
504            ),
505        )
506        .with_operation("version_negotiation")
507    }
508
509    /// Create a server overloaded error (MCP error code -32010)
510    ///
511    /// # Example
512    /// ```rust
513    /// use turbomcp_protocol::Error;
514    ///
515    /// let error = Error::server_overloaded();
516    /// assert_eq!(error.jsonrpc_error_code(), -32010);
517    /// ```
518    pub fn server_overloaded() -> Box<Self> {
519        Self::new(
520            ErrorKind::ServerOverloaded,
521            "Server is currently overloaded",
522        )
523        .with_operation("request_processing")
524    }
525
526    /// Add context to this error
527    #[must_use]
528    pub fn with_context(
529        mut self: Box<Self>,
530        key: impl Into<String>,
531        value: impl Into<serde_json::Value>,
532    ) -> Box<Self> {
533        self.context.metadata.insert(key.into(), value.into());
534        self
535    }
536
537    /// Set the operation being performed
538    #[must_use]
539    pub fn with_operation(mut self: Box<Self>, operation: impl Into<String>) -> Box<Self> {
540        self.context.operation = Some(operation.into());
541        self
542    }
543
544    /// Set the component where error occurred
545    #[must_use]
546    pub fn with_component(mut self: Box<Self>, component: impl Into<String>) -> Box<Self> {
547        self.context.component = Some(component.into());
548        self
549    }
550
551    /// Set the request ID for tracing
552    #[must_use]
553    pub fn with_request_id(mut self: Box<Self>, request_id: impl Into<String>) -> Box<Self> {
554        self.context.request_id = Some(request_id.into());
555        self
556    }
557
558    /// Set the user ID
559    #[must_use]
560    pub fn with_user_id(mut self: Box<Self>, user_id: impl Into<String>) -> Box<Self> {
561        self.context.user_id = Some(user_id.into());
562        self
563    }
564
565    /// Add retry information
566    #[must_use]
567    pub fn with_retry_info(mut self: Box<Self>, retry_info: RetryInfo) -> Box<Self> {
568        self.context.retry_info = Some(retry_info);
569        self
570    }
571
572    /// Chain this error with a source error
573    #[must_use]
574    pub fn with_source(mut self: Box<Self>, source: Box<Self>) -> Box<Self> {
575        self.source = Some(source);
576        self
577    }
578
579    /// Check if this error is retryable based on its kind
580    pub const fn is_retryable(&self) -> bool {
581        matches!(
582            self.kind,
583            ErrorKind::Timeout
584                | ErrorKind::Unavailable
585                | ErrorKind::Transport
586                | ErrorKind::ExternalService
587                | ErrorKind::RateLimited
588        )
589    }
590
591    /// Check if this error indicates a temporary failure
592    pub const fn is_temporary(&self) -> bool {
593        matches!(
594            self.kind,
595            ErrorKind::Timeout
596                | ErrorKind::Unavailable
597                | ErrorKind::RateLimited
598                | ErrorKind::ExternalService
599        )
600    }
601
602    /// Get the HTTP status code equivalent for this error
603    pub const fn http_status_code(&self) -> u16 {
604        match self.kind {
605            // Client errors (4xx)
606            ErrorKind::Validation | ErrorKind::BadRequest => 400,
607            ErrorKind::Authentication => 401,
608            ErrorKind::PermissionDenied | ErrorKind::Security | ErrorKind::ResourceAccessDenied => {
609                403
610            }
611            ErrorKind::ToolNotFound | ErrorKind::PromptNotFound | ErrorKind::ResourceNotFound => {
612                404
613            }
614            ErrorKind::Timeout => 408,
615            ErrorKind::RateLimited => 429,
616            ErrorKind::Cancelled => 499, // Client closed request
617
618            // Server errors (5xx)
619            ErrorKind::Internal
620            | ErrorKind::Configuration
621            | ErrorKind::Serialization
622            | ErrorKind::Protocol
623            | ErrorKind::ToolExecutionFailed
624            | ErrorKind::CapabilityNotSupported
625            | ErrorKind::ProtocolVersionMismatch => 500,
626
627            ErrorKind::Transport
628            | ErrorKind::ExternalService
629            | ErrorKind::Unavailable
630            | ErrorKind::ServerOverloaded => 503,
631
632            // Deprecated (backwards compatibility)
633            #[allow(deprecated)]
634            ErrorKind::Handler => 500,
635            #[allow(deprecated)]
636            ErrorKind::NotFound => 404,
637        }
638    }
639
640    /// Convert to a JSON-RPC error code per MCP 2025-06-18 specification
641    pub const fn jsonrpc_error_code(&self) -> i32 {
642        match self.kind {
643            // JSON-RPC standard error codes
644            ErrorKind::BadRequest => -32600, // Invalid Request
645            ErrorKind::Protocol => -32601,   // Method not found
646            ErrorKind::Validation | ErrorKind::Serialization => -32602, // Invalid params
647            ErrorKind::Internal => -32603,   // Internal error
648
649            // MCP-specific error codes (2025-06-18 specification)
650            ErrorKind::ToolNotFound => -32001, // Tool not found
651            ErrorKind::ToolExecutionFailed => -32002, // Tool execution error
652            ErrorKind::PromptNotFound => -32003, // Prompt not found
653            ErrorKind::ResourceNotFound => -32004, // Resource not found
654            ErrorKind::ResourceAccessDenied => -32005, // Resource access denied
655            ErrorKind::CapabilityNotSupported => -32006, // Capability not supported
656            ErrorKind::ProtocolVersionMismatch => -32007, // Protocol version mismatch
657            ErrorKind::Authentication => -32008, // Authentication required
658            ErrorKind::RateLimited => -32009,  // Rate limited
659            ErrorKind::ServerOverloaded => -32010, // Server overloaded
660
661            // General application errors (application-defined codes)
662            ErrorKind::PermissionDenied => -32011, // Permission denied
663            ErrorKind::Timeout => -32012,          // Timeout
664            ErrorKind::Unavailable => -32013,      // Service unavailable
665            ErrorKind::Transport => -32014,        // Transport error
666            ErrorKind::Configuration => -32015,    // Configuration error
667            ErrorKind::ExternalService => -32016,  // External service error
668            ErrorKind::Cancelled => -32017,        // Operation cancelled
669            ErrorKind::Security => -32018,         // Security constraint violation
670
671            // Deprecated (backwards compatibility)
672            #[allow(deprecated)]
673            ErrorKind::Handler => -32019, // Deprecated: Handler error
674            #[allow(deprecated)]
675            ErrorKind::NotFound => -32020, // Deprecated: Generic not found
676        }
677    }
678}
679
680impl fmt::Display for Error {
681    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682        write!(f, "{}", self.message)?;
683
684        if let Some(operation) = &self.context.operation {
685            write!(f, " (operation: {operation})")?;
686        }
687
688        if let Some(component) = &self.context.component {
689            write!(f, " (component: {component})")?;
690        }
691
692        if let Some(request_id) = &self.context.request_id {
693            write!(f, " (request_id: {request_id})")?;
694        }
695
696        Ok(())
697    }
698}
699
700impl std::error::Error for Error {
701    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
702        // Note: We can't return the source error because it's also an Error type
703        // which would create infinite recursion. Current design returns None to avoid this.
704        // Future enhancement could add proper error chaining with boxed std::error::Error.
705        None
706    }
707}
708
709impl ErrorKind {
710    /// Get a human-readable description of this error kind
711    #[must_use]
712    pub const fn description(self) -> &'static str {
713        match self {
714            // MCP-specific errors
715            Self::ToolNotFound => "Tool not found",
716            Self::ToolExecutionFailed => "Tool execution failed",
717            Self::PromptNotFound => "Prompt not found",
718            Self::ResourceNotFound => "Resource not found",
719            Self::ResourceAccessDenied => "Resource access denied",
720            Self::CapabilityNotSupported => "Capability not supported",
721            Self::ProtocolVersionMismatch => "Protocol version mismatch",
722
723            // JSON-RPC standard errors
724            Self::Validation => "Input validation failed",
725            Self::BadRequest => "Bad request",
726            Self::Internal => "Internal server error",
727            Self::Serialization => "Serialization error",
728            Self::Protocol => "Protocol error",
729
730            // General application errors
731            Self::Authentication => "Authentication failed",
732            Self::PermissionDenied => "Permission denied",
733            Self::Transport => "Transport error",
734            Self::Timeout => "Operation timed out",
735            Self::Unavailable => "Service unavailable",
736            Self::RateLimited => "Rate limit exceeded",
737            Self::ServerOverloaded => "Server overloaded",
738            Self::Configuration => "Configuration error",
739            Self::ExternalService => "External service error",
740            Self::Cancelled => "Operation cancelled",
741            Self::Security => "Security constraint violation",
742
743            // Deprecated
744            #[allow(deprecated)]
745            Self::Handler => "Handler execution error (deprecated)",
746            #[allow(deprecated)]
747            Self::NotFound => "Resource not found (deprecated)",
748        }
749    }
750}
751
752impl fmt::Display for ErrorKind {
753    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754        write!(f, "{}", self.description())
755    }
756}
757
758/// Convenience macro for creating errors with context
759#[macro_export]
760macro_rules! mcp_error {
761    ($kind:expr, $message:expr) => {
762        $crate::error::Error::new($kind, $message)
763    };
764    ($kind:expr, $message:expr, $($key:expr => $value:expr),+) => {
765        {
766            let mut error = $crate::error::Error::new($kind, $message);
767            $(
768                error = error.with_context($key, $value);
769            )+
770            error
771        }
772    };
773}
774
775/// Extension trait for adding MCP error context to other error types
776pub trait ErrorExt<T> {
777    /// Convert any error to an MCP error with the specified kind
778    ///
779    /// # Errors
780    ///
781    /// Returns an `Error` with the specified kind and message, preserving the source error context.
782    fn with_mcp_error(self, kind: ErrorKind, message: impl Into<String>) -> Result<T>;
783
784    /// Convert any error to an MCP internal error
785    ///
786    /// # Errors
787    ///
788    /// Returns an `Error` with internal error kind and the provided message.
789    fn with_internal_error(self, message: impl Into<String>) -> Result<T>;
790}
791
792impl<T, E> ErrorExt<T> for std::result::Result<T, E>
793where
794    E: std::error::Error + Send + Sync + 'static,
795{
796    fn with_mcp_error(self, kind: ErrorKind, message: impl Into<String>) -> Result<T> {
797        self.map_err(|e| {
798            Error::new(kind, format!("{}: {}", message.into(), e))
799                .with_context("source_error", e.to_string())
800        })
801    }
802
803    fn with_internal_error(self, message: impl Into<String>) -> Result<T> {
804        self.with_mcp_error(ErrorKind::Internal, message)
805    }
806}
807
808// Implement From for common error types
809impl From<serde_json::Error> for Box<Error> {
810    fn from(err: serde_json::Error) -> Self {
811        Error::serialization(format!("JSON serialization error: {err}"))
812    }
813}
814
815impl From<std::io::Error> for Box<Error> {
816    fn from(err: std::io::Error) -> Self {
817        Error::transport(format!("IO error: {err}"))
818    }
819}
820
821#[cfg(test)]
822mod tests {
823    use super::*;
824
825    #[test]
826    fn test_error_creation() {
827        let error = Error::validation("Invalid input");
828        assert_eq!(error.kind, ErrorKind::Validation);
829        assert_eq!(error.message, "Invalid input");
830    }
831
832    #[test]
833    fn test_error_context() {
834        let error = Error::internal("Something went wrong")
835            .with_operation("test_operation")
836            .with_component("test_component")
837            .with_request_id("req-123")
838            .with_context("key", "value");
839
840        assert_eq!(error.context.operation, Some("test_operation".to_string()));
841        assert_eq!(error.context.component, Some("test_component".to_string()));
842        assert_eq!(error.context.request_id, Some("req-123".to_string()));
843        assert_eq!(
844            error.context.metadata.get("key"),
845            Some(&serde_json::Value::String("value".to_string()))
846        );
847    }
848
849    #[test]
850    fn test_error_properties() {
851        let retryable_error = Error::timeout("Request timed out");
852        assert!(retryable_error.is_retryable());
853        assert!(retryable_error.is_temporary());
854
855        let permanent_error = Error::validation("Invalid data");
856        assert!(!permanent_error.is_retryable());
857        assert!(!permanent_error.is_temporary());
858    }
859
860    #[test]
861    fn test_http_status_codes() {
862        assert_eq!(Error::validation("test").http_status_code(), 400);
863        assert_eq!(Error::tool_not_found("test").http_status_code(), 404);
864        assert_eq!(Error::internal("test").http_status_code(), 500);
865    }
866
867    #[test]
868    fn test_error_macro() {
869        let error = mcp_error!(ErrorKind::Validation, "test message");
870        assert_eq!(error.kind, ErrorKind::Validation);
871        assert_eq!(error.message, "test message");
872
873        let error_with_context = mcp_error!(
874            ErrorKind::Internal,
875            "test message",
876            "key1" => "value1",
877            "key2" => 42
878        );
879        assert_eq!(error_with_context.context.metadata.len(), 2);
880    }
881}