1pub type ServerResult<T> = Result<T, ServerError>;
5
6#[derive(Debug, thiserror::Error)]
8#[non_exhaustive]
9pub enum ServerError {
10 #[error("Core error: {0}")]
12 Core(#[from] turbomcp_protocol::registry::RegistryError),
13
14 #[error("Transport error: {0}")]
16 Transport(#[from] turbomcp_transport::TransportError),
17
18 #[error("Handler error: {message}")]
20 Handler {
21 message: String,
23 context: Option<String>,
25 },
26
27 #[error("Configuration error: {message}")]
29 Configuration {
30 message: String,
32 key: Option<String>,
34 },
35
36 #[error("Authentication error: {message}")]
38 Authentication {
39 message: String,
41 method: Option<String>,
43 },
44
45 #[error("Authorization error: {message}")]
47 Authorization {
48 message: String,
50 resource: Option<String>,
52 },
53
54 #[error("Rate limit exceeded: {message}")]
56 RateLimit {
57 message: String,
59 retry_after: Option<u64>,
61 },
62
63 #[error("Lifecycle error: {0}")]
65 Lifecycle(String),
66
67 #[error("Shutdown error: {0}")]
69 Shutdown(String),
70
71 #[error("Middleware error: {name}: {message}")]
73 Middleware {
74 name: String,
76 message: String,
78 },
79
80 #[error("Registry error: {0}")]
82 Registry(String),
83
84 #[error("Routing error: {message}")]
86 Routing {
87 message: String,
89 method: Option<String>,
91 },
92
93 #[error("Resource not found: {resource}")]
95 NotFound {
96 resource: String,
98 },
99
100 #[error("Internal server error: {0}")]
102 Internal(String),
103
104 #[error("IO error: {0}")]
106 Io(#[from] std::io::Error),
107
108 #[error("Serialization error: {0}")]
110 Serialization(#[from] serde_json::Error),
111
112 #[error("Timeout error: {operation} timed out after {timeout_ms}ms")]
114 Timeout {
115 operation: String,
117 timeout_ms: u64,
119 },
120
121 #[error("Resource exhausted: {resource}")]
123 ResourceExhausted {
124 resource: String,
126 current: Option<usize>,
128 max: Option<usize>,
130 },
131}
132
133impl ServerError {
134 pub fn handler(message: impl Into<String>) -> Self {
136 Self::Handler {
137 message: message.into(),
138 context: None,
139 }
140 }
141
142 pub fn handler_with_context(message: impl Into<String>, context: impl Into<String>) -> Self {
144 Self::Handler {
145 message: message.into(),
146 context: Some(context.into()),
147 }
148 }
149
150 pub fn configuration(message: impl Into<String>) -> Self {
152 Self::Configuration {
153 message: message.into(),
154 key: None,
155 }
156 }
157
158 pub fn configuration_with_key(message: impl Into<String>, key: impl Into<String>) -> Self {
160 Self::Configuration {
161 message: message.into(),
162 key: Some(key.into()),
163 }
164 }
165
166 pub fn authentication(message: impl Into<String>) -> Self {
168 Self::Authentication {
169 message: message.into(),
170 method: None,
171 }
172 }
173
174 pub fn authentication_with_method(
176 message: impl Into<String>,
177 method: impl Into<String>,
178 ) -> Self {
179 Self::Authentication {
180 message: message.into(),
181 method: Some(method.into()),
182 }
183 }
184
185 pub fn authorization(message: impl Into<String>) -> Self {
187 Self::Authorization {
188 message: message.into(),
189 resource: None,
190 }
191 }
192
193 pub fn authorization_with_resource(
195 message: impl Into<String>,
196 resource: impl Into<String>,
197 ) -> Self {
198 Self::Authorization {
199 message: message.into(),
200 resource: Some(resource.into()),
201 }
202 }
203
204 pub fn rate_limit(message: impl Into<String>) -> Self {
206 Self::RateLimit {
207 message: message.into(),
208 retry_after: None,
209 }
210 }
211
212 pub fn rate_limit_with_retry(message: impl Into<String>, retry_after: u64) -> Self {
214 Self::RateLimit {
215 message: message.into(),
216 retry_after: Some(retry_after),
217 }
218 }
219
220 pub fn middleware(name: impl Into<String>, message: impl Into<String>) -> Self {
222 Self::Middleware {
223 name: name.into(),
224 message: message.into(),
225 }
226 }
227
228 pub fn routing(message: impl Into<String>) -> Self {
230 Self::Routing {
231 message: message.into(),
232 method: None,
233 }
234 }
235
236 pub fn routing_with_method(message: impl Into<String>, method: impl Into<String>) -> Self {
238 Self::Routing {
239 message: message.into(),
240 method: Some(method.into()),
241 }
242 }
243
244 pub fn not_found(resource: impl Into<String>) -> Self {
246 Self::NotFound {
247 resource: resource.into(),
248 }
249 }
250
251 pub fn timeout(operation: impl Into<String>, timeout_ms: u64) -> Self {
253 Self::Timeout {
254 operation: operation.into(),
255 timeout_ms,
256 }
257 }
258
259 pub fn resource_exhausted(resource: impl Into<String>) -> Self {
261 Self::ResourceExhausted {
262 resource: resource.into(),
263 current: None,
264 max: None,
265 }
266 }
267
268 pub fn resource_exhausted_with_usage(
270 resource: impl Into<String>,
271 current: usize,
272 max: usize,
273 ) -> Self {
274 Self::ResourceExhausted {
275 resource: resource.into(),
276 current: Some(current),
277 max: Some(max),
278 }
279 }
280
281 #[must_use]
283 pub const fn is_retryable(&self) -> bool {
284 matches!(
285 self,
286 Self::Timeout { .. } | Self::ResourceExhausted { .. } | Self::RateLimit { .. }
287 )
288 }
289
290 #[must_use]
292 pub const fn is_fatal(&self) -> bool {
293 matches!(
294 self,
295 Self::Lifecycle(_) | Self::Shutdown(_) | Self::Internal(_)
296 )
297 }
298
299 #[must_use]
301 pub const fn error_code(&self) -> i32 {
302 match self {
303 Self::Core(_) => -32603,
304 Self::NotFound { .. } => -32004,
305 Self::Authentication { .. } => -32008,
306 Self::Authorization { .. } => -32005,
307 Self::RateLimit { .. } => -32009,
308 Self::ResourceExhausted { .. } => -32010,
309 Self::Timeout { .. } => -32603,
310 Self::Handler { .. } => -32002,
311 _ => -32603,
312 }
313 }
314}
315
316#[derive(Debug, Clone, Copy, PartialEq, Eq)]
318pub enum ErrorRecovery {
319 Retry,
321 Skip,
323 Fail,
325 Degrade,
327}
328
329#[derive(Debug, Clone)]
331pub struct ErrorContext {
332 pub category: String,
334 pub operation: String,
336 pub request_id: Option<String>,
338 pub client_id: Option<String>,
340 pub metadata: std::collections::HashMap<String, String>,
342}
343
344impl ErrorContext {
345 pub fn new(category: impl Into<String>, operation: impl Into<String>) -> Self {
347 Self {
348 category: category.into(),
349 operation: operation.into(),
350 request_id: None,
351 client_id: None,
352 metadata: std::collections::HashMap::new(),
353 }
354 }
355
356 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
358 self.request_id = Some(request_id.into());
359 self
360 }
361
362 pub fn with_client_id(mut self, client_id: impl Into<String>) -> Self {
364 self.client_id = Some(client_id.into());
365 self
366 }
367
368 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
370 self.metadata.insert(key.into(), value.into());
371 self
372 }
373}
374
375impl From<Box<turbomcp_protocol::Error>> for ServerError {
377 fn from(core_error: Box<turbomcp_protocol::Error>) -> Self {
378 use turbomcp_protocol::ErrorKind;
379
380 match core_error.kind {
381 ErrorKind::ToolNotFound | ErrorKind::PromptNotFound | ErrorKind::ResourceNotFound => {
383 Self::NotFound {
384 resource: core_error.message,
385 }
386 }
387 ErrorKind::ToolExecutionFailed => Self::Handler {
388 message: core_error.message,
389 context: core_error.context.operation,
390 },
391 ErrorKind::ResourceAccessDenied => Self::Authorization {
392 message: core_error.message,
393 resource: core_error.context.component,
394 },
395 ErrorKind::CapabilityNotSupported => Self::Handler {
396 message: format!("Capability not supported: {}", core_error.message),
397 context: None,
398 },
399 ErrorKind::ProtocolVersionMismatch => Self::Configuration {
400 message: core_error.message,
401 key: Some("protocol_version".to_string()),
402 },
403 ErrorKind::ServerOverloaded => Self::ResourceExhausted {
404 resource: "server_capacity".to_string(),
405 current: None,
406 max: None,
407 },
408
409 #[allow(deprecated)]
411 ErrorKind::Handler => Self::Handler {
412 message: core_error.message,
413 context: core_error.context.operation,
414 },
415 #[allow(deprecated)]
416 ErrorKind::NotFound => Self::NotFound {
417 resource: core_error.message,
418 },
419
420 ErrorKind::Authentication => Self::Authentication {
422 message: core_error.message,
423 method: None,
424 },
425 ErrorKind::PermissionDenied => Self::Authorization {
426 message: core_error.message,
427 resource: None,
428 },
429 ErrorKind::BadRequest | ErrorKind::Validation => Self::Handler {
430 message: format!("Validation error: {}", core_error.message),
431 context: None,
432 },
433 ErrorKind::Timeout => Self::Timeout {
434 operation: core_error
435 .context
436 .operation
437 .unwrap_or_else(|| "unknown".to_string()),
438 timeout_ms: 30000, },
440 ErrorKind::RateLimited => Self::RateLimit {
441 message: core_error.message,
442 retry_after: None,
443 },
444 ErrorKind::Configuration => Self::Configuration {
445 message: core_error.message,
446 key: None,
447 },
448 ErrorKind::Transport => {
449 Self::Internal(format!("Transport error: {}", core_error.message))
450 }
451 ErrorKind::Serialization => {
452 Self::Internal(format!("Serialization error: {}", core_error.message))
453 }
454 ErrorKind::Protocol => {
455 Self::Internal(format!("Protocol error: {}", core_error.message))
456 }
457 ErrorKind::Unavailable => Self::ResourceExhausted {
458 resource: "service".to_string(),
459 current: None,
460 max: None,
461 },
462 ErrorKind::ExternalService => {
463 Self::Internal(format!("External service error: {}", core_error.message))
464 }
465 ErrorKind::Cancelled => {
466 Self::Internal(format!("Operation cancelled: {}", core_error.message))
467 }
468 ErrorKind::Internal => Self::Internal(core_error.message),
469 ErrorKind::Security => Self::Authorization {
470 message: format!("Security error: {}", core_error.message),
471 resource: None,
472 },
473 }
474 }
475}
476
477#[cfg(test)]
482mod tests;