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 ToolNotFound,
93
94 ToolExecutionFailed,
96
97 PromptNotFound,
99
100 ResourceNotFound,
102
103 ResourceAccessDenied,
105
106 CapabilityNotSupported,
108
109 ProtocolVersionMismatch,
111
112 Validation,
117
118 BadRequest,
120
121 Internal,
123
124 Serialization,
126
127 Protocol,
129
130 Authentication,
135
136 PermissionDenied,
138
139 Transport,
141
142 Timeout,
144
145 Unavailable,
147
148 RateLimited,
150
151 ServerOverloaded,
153
154 Configuration,
156
157 ExternalService,
159
160 Cancelled,
162
163 Security,
165
166 #[deprecated(
176 since = "2.1.0",
177 note = "Use specific error kinds: ToolExecutionFailed, PromptNotFound, ResourceNotFound, etc."
178 )]
179 Handler,
180
181 #[deprecated(
188 since = "2.1.0",
189 note = "Use specific error kinds: ToolNotFound, PromptNotFound, ResourceNotFound"
190 )]
191 NotFound,
192}
193
194#[derive(Debug, Clone, Default, Serialize, Deserialize)]
196pub struct ErrorContext {
197 pub operation: Option<String>,
199
200 pub component: Option<String>,
202
203 pub request_id: Option<String>,
205
206 pub user_id: Option<String>,
208
209 pub metadata: HashMap<String, serde_json::Value>,
211
212 pub timestamp: chrono::DateTime<chrono::Utc>,
214
215 pub retry_info: Option<RetryInfo>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct RetryInfo {
222 pub attempts: u32,
224
225 pub max_attempts: u32,
227
228 pub retry_after_ms: Option<u64>,
230}
231
232impl Error {
233 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 pub fn validation(message: impl Into<String>) -> Box<Self> {
251 Self::new(ErrorKind::Validation, message)
252 }
253
254 pub fn invalid_params(message: impl Into<String>) -> Box<Self> {
269 Self::new(ErrorKind::Validation, message)
270 }
271
272 pub fn authentication(message: impl Into<String>) -> Box<Self> {
274 Self::new(ErrorKind::Authentication, message)
275 }
276
277 #[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 pub fn permission_denied(message: impl Into<String>) -> Box<Self> {
289 Self::new(ErrorKind::PermissionDenied, message)
290 }
291
292 pub fn bad_request(message: impl Into<String>) -> Box<Self> {
294 Self::new(ErrorKind::BadRequest, message)
295 }
296
297 pub fn internal(message: impl Into<String>) -> Box<Self> {
299 Self::new(ErrorKind::Internal, message)
300 }
301
302 pub fn transport(message: impl Into<String>) -> Box<Self> {
304 Self::new(ErrorKind::Transport, message)
305 }
306
307 pub fn serialization(message: impl Into<String>) -> Box<Self> {
309 Self::new(ErrorKind::Serialization, message)
310 }
311
312 pub fn protocol(message: impl Into<String>) -> Box<Self> {
314 Self::new(ErrorKind::Protocol, message)
315 }
316
317 #[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 pub fn timeout(message: impl Into<String>) -> Box<Self> {
325 Self::new(ErrorKind::Timeout, message)
326 }
327
328 pub fn unavailable(message: impl Into<String>) -> Box<Self> {
330 Self::new(ErrorKind::Unavailable, message)
331 }
332
333 pub fn rate_limited(message: impl Into<String>) -> Box<Self> {
335 Self::new(ErrorKind::RateLimited, message)
336 }
337
338 pub fn configuration(message: impl Into<String>) -> Box<Self> {
340 Self::new(ErrorKind::Configuration, message)
341 }
342
343 pub fn external_service(message: impl Into<String>) -> Box<Self> {
345 Self::new(ErrorKind::ExternalService, message)
346 }
347
348 pub fn cancelled(message: impl Into<String>) -> Box<Self> {
350 Self::new(ErrorKind::Cancelled, message)
351 }
352
353 #[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 pub fn security(message: impl Into<String>) -> Box<Self> {
365 Self::new(ErrorKind::Security, message)
366 }
367
368 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 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 pub const fn http_status_code(&self) -> u16 {
604 match self.kind {
605 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, 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 #[allow(deprecated)]
634 ErrorKind::Handler => 500,
635 #[allow(deprecated)]
636 ErrorKind::NotFound => 404,
637 }
638 }
639
640 pub const fn jsonrpc_error_code(&self) -> i32 {
642 match self.kind {
643 ErrorKind::BadRequest => -32600, ErrorKind::Protocol => -32601, ErrorKind::Validation | ErrorKind::Serialization => -32602, ErrorKind::Internal => -32603, ErrorKind::ToolNotFound => -32001, ErrorKind::ToolExecutionFailed => -32002, ErrorKind::PromptNotFound => -32003, ErrorKind::ResourceNotFound => -32004, ErrorKind::ResourceAccessDenied => -32005, ErrorKind::CapabilityNotSupported => -32006, ErrorKind::ProtocolVersionMismatch => -32007, ErrorKind::Authentication => -32008, ErrorKind::RateLimited => -32009, ErrorKind::ServerOverloaded => -32010, ErrorKind::PermissionDenied => -32011, ErrorKind::Timeout => -32012, ErrorKind::Unavailable => -32013, ErrorKind::Transport => -32014, ErrorKind::Configuration => -32015, ErrorKind::ExternalService => -32016, ErrorKind::Cancelled => -32017, ErrorKind::Security => -32018, #[allow(deprecated)]
673 ErrorKind::Handler => -32019, #[allow(deprecated)]
675 ErrorKind::NotFound => -32020, }
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 None
706 }
707}
708
709impl ErrorKind {
710 #[must_use]
712 pub const fn description(self) -> &'static str {
713 match self {
714 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 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 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 #[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#[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
775pub trait ErrorExt<T> {
777 fn with_mcp_error(self, kind: ErrorKind, message: impl Into<String>) -> Result<T>;
783
784 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
808impl 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}