1use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Serialize, Deserialize, JsonSchema)]
8pub struct McpError {
9 pub code: ErrorCode,
11 pub message: String,
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub details: Option<serde_json::Value>,
16 pub retryable: bool,
18}
19
20#[derive(Debug, Serialize, Deserialize, JsonSchema)]
21#[serde(rename_all = "snake_case")]
22pub enum ErrorCode {
23 SessionNotFound,
25 AmbiguousSessionPrefix,
27 InvalidSessionId,
29 InvalidEventIndex,
31 InvalidCursor,
33 InvalidParameter,
35 SearchTimeout,
37 InternalError,
39}
40
41impl McpError {
42 #[allow(dead_code)]
43 pub fn session_not_found(session_id: &str) -> Self {
44 Self {
45 code: ErrorCode::SessionNotFound,
46 message: format!("Session not found: {}", session_id),
47 details: Some(serde_json::json!({ "session_id": session_id })),
48 retryable: false,
49 }
50 }
51
52 #[allow(dead_code)]
53 pub fn ambiguous_prefix(prefix: &str, matches: Vec<String>) -> Self {
54 Self {
55 code: ErrorCode::AmbiguousSessionPrefix,
56 message: format!(
57 "Session ID prefix '{}' matches {} sessions. Provide more characters.",
58 prefix,
59 matches.len()
60 ),
61 details: Some(serde_json::json!({
62 "prefix": prefix,
63 "matches": matches,
64 })),
65 retryable: false,
66 }
67 }
68
69 pub fn invalid_event_index(session_id: &str, index: usize, max: usize) -> Self {
70 Self {
71 code: ErrorCode::InvalidEventIndex,
72 message: format!(
73 "Event index {} out of bounds for session {} (max: {})",
74 index, session_id, max
75 ),
76 details: Some(serde_json::json!({
77 "session_id": session_id,
78 "requested_index": index,
79 "max_index": max,
80 })),
81 retryable: false,
82 }
83 }
84
85 #[allow(dead_code)]
86 pub fn invalid_cursor(cursor: &str) -> Self {
87 Self {
88 code: ErrorCode::InvalidCursor,
89 message: "Invalid or expired pagination cursor".to_string(),
90 details: Some(serde_json::json!({ "cursor": cursor })),
91 retryable: false,
92 }
93 }
94
95 #[allow(dead_code)]
96 pub fn invalid_parameter(param_name: &str, reason: &str) -> Self {
97 Self {
98 code: ErrorCode::InvalidParameter,
99 message: format!("Invalid parameter '{}': {}", param_name, reason),
100 details: Some(serde_json::json!({
101 "parameter": param_name,
102 "reason": reason,
103 })),
104 retryable: false,
105 }
106 }
107
108 pub fn internal_error(message: String) -> Self {
109 Self {
110 code: ErrorCode::InternalError,
111 message,
112 details: None,
113 retryable: true,
114 }
115 }
116}
117
118impl From<crate::Error> for McpError {
119 fn from(err: crate::Error) -> Self {
120 Self::internal_error(err.to_string())
121 }
122}
123
124impl std::fmt::Display for McpError {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(f, "{}", self.message)
127 }
128}
129
130impl std::error::Error for McpError {}