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