1use serde::{Deserialize, Serialize};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[repr(i32)]
35pub enum ErrorCode {
36 ParseError = -32700,
38 InvalidRequest = -32600,
40 MethodNotFound = -32601,
42 InvalidParams = -32602,
44 InternalError = -32603,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[repr(i32)]
51pub enum McpErrorCode {
52 ConnectionClosed = -32000,
54 RequestTimeout = -32001,
56 ResourceNotFound = -32002,
58 AlreadySubscribed = -32003,
60 NotSubscribed = -32004,
62 SessionNotFound = -32005,
64 SessionRequired = -32006,
66}
67
68impl McpErrorCode {
69 pub fn code(self) -> i32 {
70 self as i32
71 }
72}
73
74impl ErrorCode {
75 pub fn code(self) -> i32 {
76 self as i32
77 }
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct JsonRpcError {
83 pub code: i32,
84 pub message: String,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub data: Option<serde_json::Value>,
87}
88
89impl JsonRpcError {
90 pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
91 Self {
92 code: code.code(),
93 message: message.into(),
94 data: None,
95 }
96 }
97
98 pub fn with_data(mut self, data: serde_json::Value) -> Self {
99 self.data = Some(data);
100 self
101 }
102
103 pub fn parse_error(message: impl Into<String>) -> Self {
104 Self::new(ErrorCode::ParseError, message)
105 }
106
107 pub fn invalid_request(message: impl Into<String>) -> Self {
108 Self::new(ErrorCode::InvalidRequest, message)
109 }
110
111 pub fn method_not_found(method: &str) -> Self {
112 Self::new(
113 ErrorCode::MethodNotFound,
114 format!("Method not found: {}", method),
115 )
116 }
117
118 pub fn invalid_params(message: impl Into<String>) -> Self {
119 Self::new(ErrorCode::InvalidParams, message)
120 }
121
122 pub fn internal_error(message: impl Into<String>) -> Self {
123 Self::new(ErrorCode::InternalError, message)
124 }
125
126 pub fn mcp_error(code: McpErrorCode, message: impl Into<String>) -> Self {
128 Self {
129 code: code.code(),
130 message: message.into(),
131 data: None,
132 }
133 }
134
135 pub fn connection_closed(message: impl Into<String>) -> Self {
137 Self::mcp_error(McpErrorCode::ConnectionClosed, message)
138 }
139
140 pub fn request_timeout(message: impl Into<String>) -> Self {
142 Self::mcp_error(McpErrorCode::RequestTimeout, message)
143 }
144
145 pub fn resource_not_found(uri: &str) -> Self {
147 Self::mcp_error(
148 McpErrorCode::ResourceNotFound,
149 format!("Resource not found: {}", uri),
150 )
151 }
152
153 pub fn already_subscribed(uri: &str) -> Self {
155 Self::mcp_error(
156 McpErrorCode::AlreadySubscribed,
157 format!("Already subscribed to: {}", uri),
158 )
159 }
160
161 pub fn not_subscribed(uri: &str) -> Self {
163 Self::mcp_error(
164 McpErrorCode::NotSubscribed,
165 format!("Not subscribed to: {}", uri),
166 )
167 }
168
169 pub fn session_not_found() -> Self {
174 Self::mcp_error(
175 McpErrorCode::SessionNotFound,
176 "Session not found or expired. Please re-initialize the connection.",
177 )
178 }
179
180 pub fn session_not_found_with_id(session_id: &str) -> Self {
182 Self::mcp_error(
183 McpErrorCode::SessionNotFound,
184 format!(
185 "Session '{}' not found or expired. Please re-initialize the connection.",
186 session_id
187 ),
188 )
189 }
190
191 pub fn session_required() -> Self {
193 Self::mcp_error(
194 McpErrorCode::SessionRequired,
195 "MCP-Session-Id header is required for this request.",
196 )
197 }
198}
199
200#[derive(Debug)]
202pub struct ToolError {
203 pub tool: Option<String>,
205 pub message: String,
207 pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
209}
210
211impl std::fmt::Display for ToolError {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 if let Some(tool) = &self.tool {
214 write!(f, "Tool '{}' error: {}", tool, self.message)
215 } else {
216 write!(f, "Tool error: {}", self.message)
217 }
218 }
219}
220
221impl std::error::Error for ToolError {
222 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
223 self.source
224 .as_ref()
225 .map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
226 }
227}
228
229impl ToolError {
230 pub fn new(message: impl Into<String>) -> Self {
232 Self {
233 tool: None,
234 message: message.into(),
235 source: None,
236 }
237 }
238
239 pub fn with_tool(tool: impl Into<String>, message: impl Into<String>) -> Self {
241 Self {
242 tool: Some(tool.into()),
243 message: message.into(),
244 source: None,
245 }
246 }
247
248 pub fn with_source(mut self, source: impl std::error::Error + Send + Sync + 'static) -> Self {
250 self.source = Some(Box::new(source));
251 self
252 }
253}
254
255#[derive(Debug, thiserror::Error)]
257pub enum Error {
258 #[error("JSON-RPC error: {0:?}")]
259 JsonRpc(JsonRpcError),
260
261 #[error("Serialization error: {0}")]
262 Serialization(#[from] serde_json::Error),
263
264 #[error("{0}")]
265 Tool(#[from] ToolError),
266
267 #[error("Transport error: {0}")]
268 Transport(String),
269
270 #[error("Internal error: {0}")]
271 Internal(String),
272}
273
274impl Error {
275 pub fn tool(message: impl Into<String>) -> Self {
277 Error::Tool(ToolError::new(message))
278 }
279
280 pub fn tool_with_name(tool: impl Into<String>, message: impl Into<String>) -> Self {
282 Error::Tool(ToolError::with_tool(tool, message))
283 }
284
285 pub fn tool_from<E: std::fmt::Display>(err: E) -> Self {
298 Error::Tool(ToolError::new(err.to_string()))
299 }
300
301 pub fn tool_context<E: std::fmt::Display>(context: impl Into<String>, err: E) -> Self {
314 Error::Tool(ToolError::new(format!("{}: {}", context.into(), err)))
315 }
316}
317
318impl From<JsonRpcError> for Error {
319 fn from(err: JsonRpcError) -> Self {
320 Error::JsonRpc(err)
321 }
322}
323
324pub type Result<T> = std::result::Result<T, Error>;