1pub type ServerResult<T> = Result<T, ServerError>;
5
6#[derive(Debug, thiserror::Error)]
8#[non_exhaustive]
9pub enum ServerError {
10 #[error("Protocol error: {0}")]
20 Protocol(Box<turbomcp_protocol::Error>),
21
22 #[error("Core error: {0}")]
24 Core(#[from] turbomcp_protocol::registry::RegistryError),
25
26 #[error("Transport error: {0}")]
28 Transport(#[from] turbomcp_transport::TransportError),
29
30 #[error("Handler error: {message}")]
32 Handler {
33 message: String,
35 context: Option<String>,
37 },
38
39 #[error("Configuration error: {message}")]
41 Configuration {
42 message: String,
44 key: Option<String>,
46 },
47
48 #[error("Authentication error: {message}")]
50 Authentication {
51 message: String,
53 method: Option<String>,
55 },
56
57 #[error("Authorization error: {message}")]
59 Authorization {
60 message: String,
62 resource: Option<String>,
64 },
65
66 #[error("Rate limit exceeded: {message}")]
68 RateLimit {
69 message: String,
71 retry_after: Option<u64>,
73 },
74
75 #[error("Lifecycle error: {0}")]
77 Lifecycle(String),
78
79 #[error("Shutdown error: {0}")]
81 Shutdown(String),
82
83 #[error("Middleware error: {name}: {message}")]
85 Middleware {
86 name: String,
88 message: String,
90 },
91
92 #[error("Registry error: {0}")]
94 Registry(String),
95
96 #[error("Routing error: {message}")]
98 Routing {
99 message: String,
101 method: Option<String>,
103 },
104
105 #[error("Resource not found: {resource}")]
107 NotFound {
108 resource: String,
110 },
111
112 #[error("Internal server error: {0}")]
114 Internal(String),
115
116 #[error("IO error: {0}")]
118 Io(#[from] std::io::Error),
119
120 #[error("Serialization error: {0}")]
122 Serialization(#[from] serde_json::Error),
123
124 #[error("Timeout error: {operation} timed out after {timeout_ms}ms")]
126 Timeout {
127 operation: String,
129 timeout_ms: u64,
131 },
132
133 #[error("Resource exhausted: {resource}")]
135 ResourceExhausted {
136 resource: String,
138 current: Option<usize>,
140 max: Option<usize>,
142 },
143}
144
145impl ServerError {
146 pub fn handler(message: impl Into<String>) -> Self {
148 Self::Handler {
149 message: message.into(),
150 context: None,
151 }
152 }
153
154 pub fn handler_with_context(message: impl Into<String>, context: impl Into<String>) -> Self {
156 Self::Handler {
157 message: message.into(),
158 context: Some(context.into()),
159 }
160 }
161
162 pub fn configuration(message: impl Into<String>) -> Self {
164 Self::Configuration {
165 message: message.into(),
166 key: None,
167 }
168 }
169
170 pub fn configuration_with_key(message: impl Into<String>, key: impl Into<String>) -> Self {
172 Self::Configuration {
173 message: message.into(),
174 key: Some(key.into()),
175 }
176 }
177
178 pub fn authentication(message: impl Into<String>) -> Self {
180 Self::Authentication {
181 message: message.into(),
182 method: None,
183 }
184 }
185
186 pub fn authentication_with_method(
188 message: impl Into<String>,
189 method: impl Into<String>,
190 ) -> Self {
191 Self::Authentication {
192 message: message.into(),
193 method: Some(method.into()),
194 }
195 }
196
197 pub fn authorization(message: impl Into<String>) -> Self {
199 Self::Authorization {
200 message: message.into(),
201 resource: None,
202 }
203 }
204
205 pub fn authorization_with_resource(
207 message: impl Into<String>,
208 resource: impl Into<String>,
209 ) -> Self {
210 Self::Authorization {
211 message: message.into(),
212 resource: Some(resource.into()),
213 }
214 }
215
216 pub fn rate_limit(message: impl Into<String>) -> Self {
218 Self::RateLimit {
219 message: message.into(),
220 retry_after: None,
221 }
222 }
223
224 pub fn rate_limit_with_retry(message: impl Into<String>, retry_after: u64) -> Self {
226 Self::RateLimit {
227 message: message.into(),
228 retry_after: Some(retry_after),
229 }
230 }
231
232 pub fn middleware(name: impl Into<String>, message: impl Into<String>) -> Self {
234 Self::Middleware {
235 name: name.into(),
236 message: message.into(),
237 }
238 }
239
240 pub fn routing(message: impl Into<String>) -> Self {
242 Self::Routing {
243 message: message.into(),
244 method: None,
245 }
246 }
247
248 pub fn routing_with_method(message: impl Into<String>, method: impl Into<String>) -> Self {
250 Self::Routing {
251 message: message.into(),
252 method: Some(method.into()),
253 }
254 }
255
256 pub fn not_found(resource: impl Into<String>) -> Self {
258 Self::NotFound {
259 resource: resource.into(),
260 }
261 }
262
263 pub fn timeout(operation: impl Into<String>, timeout_ms: u64) -> Self {
265 Self::Timeout {
266 operation: operation.into(),
267 timeout_ms,
268 }
269 }
270
271 pub fn resource_exhausted(resource: impl Into<String>) -> Self {
273 Self::ResourceExhausted {
274 resource: resource.into(),
275 current: None,
276 max: None,
277 }
278 }
279
280 pub fn resource_exhausted_with_usage(
282 resource: impl Into<String>,
283 current: usize,
284 max: usize,
285 ) -> Self {
286 Self::ResourceExhausted {
287 resource: resource.into(),
288 current: Some(current),
289 max: Some(max),
290 }
291 }
292
293 #[must_use]
295 pub const fn is_retryable(&self) -> bool {
296 matches!(
297 self,
298 Self::Timeout { .. } | Self::ResourceExhausted { .. } | Self::RateLimit { .. }
299 )
300 }
301
302 #[must_use]
304 pub const fn is_fatal(&self) -> bool {
305 matches!(
306 self,
307 Self::Lifecycle(_) | Self::Shutdown(_) | Self::Internal(_)
308 )
309 }
310
311 #[must_use]
313 pub fn error_code(&self) -> i32 {
314 let code = match self {
315 Self::Protocol(protocol_err) => {
317 let extracted_code = protocol_err.jsonrpc_error_code();
318 tracing::info!(
319 "🔍 [ServerError::error_code] Protocol variant - extracted code: {}, kind: {:?}",
320 extracted_code,
321 protocol_err.kind
322 );
323 extracted_code
324 }
325
326 Self::Core(_) => -32603,
328 Self::NotFound { .. } => -32004,
329 Self::Authentication { .. } => -32008,
330 Self::Authorization { .. } => -32005,
331 Self::RateLimit { .. } => -32009,
332 Self::ResourceExhausted { .. } => -32010,
333 Self::Timeout { .. } => -32603,
334 Self::Handler { .. } => -32002,
335 Self::Transport(_) => -32603,
336 Self::Configuration { .. } => -32015,
337 Self::Lifecycle(_) => -32603,
338 Self::Shutdown(_) => -32603,
339 Self::Middleware { .. } => -32603,
340 Self::Registry(_) => -32603,
341 Self::Routing { .. } => -32603,
342 Self::Internal(_) => -32603,
343 Self::Io(_) => -32603,
344 Self::Serialization(_) => -32602,
345 };
346 tracing::info!(
347 "🔍 [ServerError::error_code] Returning code: {} for variant: {:?}",
348 code,
349 std::mem::discriminant(self)
350 );
351 code
352 }
353}
354
355#[derive(Debug, Clone, Copy, PartialEq, Eq)]
357pub enum ErrorRecovery {
358 Retry,
360 Skip,
362 Fail,
364 Degrade,
366}
367
368#[derive(Debug, Clone)]
370pub struct ErrorContext {
371 pub category: String,
373 pub operation: String,
375 pub request_id: Option<String>,
377 pub client_id: Option<String>,
379 pub metadata: std::collections::HashMap<String, String>,
381}
382
383impl ErrorContext {
384 pub fn new(category: impl Into<String>, operation: impl Into<String>) -> Self {
386 Self {
387 category: category.into(),
388 operation: operation.into(),
389 request_id: None,
390 client_id: None,
391 metadata: std::collections::HashMap::new(),
392 }
393 }
394
395 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
397 self.request_id = Some(request_id.into());
398 self
399 }
400
401 pub fn with_client_id(mut self, client_id: impl Into<String>) -> Self {
403 self.client_id = Some(client_id.into());
404 self
405 }
406
407 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
409 self.metadata.insert(key.into(), value.into());
410 self
411 }
412}
413
414impl From<Box<turbomcp_protocol::Error>> for ServerError {
416 fn from(core_error: Box<turbomcp_protocol::Error>) -> Self {
417 use turbomcp_protocol::ErrorKind;
418
419 match core_error.kind {
420 ErrorKind::UserRejected => Self::Handler {
422 message: core_error.message,
423 context: core_error.context.operation,
424 },
425 ErrorKind::ToolNotFound | ErrorKind::PromptNotFound | ErrorKind::ResourceNotFound => {
426 Self::NotFound {
427 resource: core_error.message,
428 }
429 }
430 ErrorKind::ToolExecutionFailed => Self::Handler {
431 message: core_error.message,
432 context: core_error.context.operation,
433 },
434 ErrorKind::ResourceAccessDenied => Self::Authorization {
435 message: core_error.message,
436 resource: core_error.context.component,
437 },
438 ErrorKind::CapabilityNotSupported => Self::Handler {
439 message: format!("Capability not supported: {}", core_error.message),
440 context: None,
441 },
442 ErrorKind::ProtocolVersionMismatch => Self::Configuration {
443 message: core_error.message,
444 key: Some("protocol_version".to_string()),
445 },
446 ErrorKind::ServerOverloaded => Self::ResourceExhausted {
447 resource: "server_capacity".to_string(),
448 current: None,
449 max: None,
450 },
451
452 #[allow(deprecated)]
454 ErrorKind::Handler => Self::Handler {
455 message: core_error.message,
456 context: core_error.context.operation,
457 },
458 #[allow(deprecated)]
459 ErrorKind::NotFound => Self::NotFound {
460 resource: core_error.message,
461 },
462
463 ErrorKind::Authentication => Self::Authentication {
465 message: core_error.message,
466 method: None,
467 },
468 ErrorKind::PermissionDenied => Self::Authorization {
469 message: core_error.message,
470 resource: None,
471 },
472 ErrorKind::BadRequest | ErrorKind::Validation => Self::Handler {
473 message: format!("Validation error: {}", core_error.message),
474 context: None,
475 },
476 ErrorKind::Timeout => Self::Timeout {
477 operation: core_error
478 .context
479 .operation
480 .unwrap_or_else(|| "unknown".to_string()),
481 timeout_ms: 30000, },
483 ErrorKind::RateLimited => Self::RateLimit {
484 message: core_error.message,
485 retry_after: None,
486 },
487 ErrorKind::Configuration => Self::Configuration {
488 message: core_error.message,
489 key: None,
490 },
491 ErrorKind::Transport => {
492 Self::Internal(format!("Transport error: {}", core_error.message))
493 }
494 ErrorKind::Serialization => {
495 Self::Internal(format!("Serialization error: {}", core_error.message))
496 }
497 ErrorKind::Protocol => {
498 Self::Internal(format!("Protocol error: {}", core_error.message))
499 }
500 ErrorKind::Unavailable => Self::ResourceExhausted {
501 resource: "service".to_string(),
502 current: None,
503 max: None,
504 },
505 ErrorKind::ExternalService => {
506 Self::Internal(format!("External service error: {}", core_error.message))
507 }
508 ErrorKind::Cancelled => {
509 Self::Internal(format!("Operation cancelled: {}", core_error.message))
510 }
511 ErrorKind::Internal => Self::Internal(core_error.message),
512 ErrorKind::Security => Self::Authorization {
513 message: format!("Security error: {}", core_error.message),
514 resource: None,
515 },
516 }
517 }
518}
519
520impl From<ServerError> for Box<turbomcp_protocol::Error> {
533 fn from(server_error: ServerError) -> Self {
534 match server_error {
535 ServerError::Protocol(protocol_err) => protocol_err,
537
538 ServerError::Transport(transport_err) => {
540 turbomcp_protocol::Error::transport(format!("Transport error: {}", transport_err))
541 }
542 ServerError::Handler { message, context } => {
543 turbomcp_protocol::Error::internal(format!(
544 "Handler error{}: {}",
545 context
546 .as_ref()
547 .map(|c| format!(" ({})", c))
548 .unwrap_or_default(),
549 message
550 ))
551 }
552 ServerError::Core(err) => {
553 turbomcp_protocol::Error::internal(format!("Core error: {}", err))
554 }
555 ServerError::Configuration { message, .. } => {
556 turbomcp_protocol::Error::configuration(message)
557 }
558 ServerError::Authentication { message, .. } => {
559 turbomcp_protocol::Error::new(turbomcp_protocol::ErrorKind::Authentication, message)
560 }
561 ServerError::Authorization { message, .. } => turbomcp_protocol::Error::new(
562 turbomcp_protocol::ErrorKind::PermissionDenied,
563 message,
564 ),
565 ServerError::RateLimit { message, .. } => {
566 turbomcp_protocol::Error::rate_limited(message)
567 }
568 ServerError::Timeout {
569 operation,
570 timeout_ms,
571 } => turbomcp_protocol::Error::timeout(format!(
572 "Operation '{}' timed out after {}ms",
573 operation, timeout_ms
574 )),
575 ServerError::NotFound { resource } => turbomcp_protocol::Error::new(
576 turbomcp_protocol::ErrorKind::ResourceNotFound,
577 format!("Resource not found: {}", resource),
578 ),
579 ServerError::ResourceExhausted { resource, .. } => turbomcp_protocol::Error::new(
580 turbomcp_protocol::ErrorKind::Unavailable,
581 format!("Resource exhausted: {}", resource),
582 ),
583 ServerError::Internal(message) => turbomcp_protocol::Error::internal(message),
584 ServerError::Lifecycle(message) => {
585 turbomcp_protocol::Error::internal(format!("Lifecycle error: {}", message))
586 }
587 ServerError::Shutdown(message) => {
588 turbomcp_protocol::Error::internal(format!("Shutdown error: {}", message))
589 }
590 ServerError::Middleware { name, message } => turbomcp_protocol::Error::internal(
591 format!("Middleware error ({}): {}", name, message),
592 ),
593 ServerError::Registry(message) => {
594 turbomcp_protocol::Error::internal(format!("Registry error: {}", message))
595 }
596 ServerError::Routing { message, .. } => {
597 turbomcp_protocol::Error::internal(format!("Routing error: {}", message))
598 }
599 ServerError::Io(err) => {
600 turbomcp_protocol::Error::internal(format!("IO error: {}", err))
601 }
602 ServerError::Serialization(err) => turbomcp_protocol::Error::new(
603 turbomcp_protocol::ErrorKind::Serialization,
604 format!("Serialization error: {}", err),
605 ),
606 }
607 }
608}
609
610#[cfg(test)]
612mod tests;