1pub type ServerResult<T> = Result<T, ServerError>;
5
6#[derive(Debug, thiserror::Error)]
8pub enum ServerError {
9 #[error("Core error: {0}")]
11 Core(#[from] turbomcp_core::registry::RegistryError),
12
13 #[error("Transport error: {0}")]
15 Transport(#[from] turbomcp_transport::TransportError),
16
17 #[error("Handler error: {message}")]
19 Handler {
20 message: String,
22 context: Option<String>,
24 },
25
26 #[error("Configuration error: {message}")]
28 Configuration {
29 message: String,
31 key: Option<String>,
33 },
34
35 #[error("Authentication error: {message}")]
37 Authentication {
38 message: String,
40 method: Option<String>,
42 },
43
44 #[error("Authorization error: {message}")]
46 Authorization {
47 message: String,
49 resource: Option<String>,
51 },
52
53 #[error("Rate limit exceeded: {message}")]
55 RateLimit {
56 message: String,
58 retry_after: Option<u64>,
60 },
61
62 #[error("Lifecycle error: {0}")]
64 Lifecycle(String),
65
66 #[error("Shutdown error: {0}")]
68 Shutdown(String),
69
70 #[error("Middleware error: {name}: {message}")]
72 Middleware {
73 name: String,
75 message: String,
77 },
78
79 #[error("Registry error: {0}")]
81 Registry(String),
82
83 #[error("Routing error: {message}")]
85 Routing {
86 message: String,
88 method: Option<String>,
90 },
91
92 #[error("Resource not found: {resource}")]
94 NotFound {
95 resource: String,
97 },
98
99 #[error("Internal server error: {0}")]
101 Internal(String),
102
103 #[error("IO error: {0}")]
105 Io(#[from] std::io::Error),
106
107 #[error("Serialization error: {0}")]
109 Serialization(#[from] serde_json::Error),
110
111 #[error("Timeout error: {operation} timed out after {timeout_ms}ms")]
113 Timeout {
114 operation: String,
116 timeout_ms: u64,
118 },
119
120 #[error("Resource exhausted: {resource}")]
122 ResourceExhausted {
123 resource: String,
125 current: Option<usize>,
127 max: Option<usize>,
129 },
130}
131
132impl ServerError {
133 pub fn handler(message: impl Into<String>) -> Self {
135 Self::Handler {
136 message: message.into(),
137 context: None,
138 }
139 }
140
141 pub fn handler_with_context(message: impl Into<String>, context: impl Into<String>) -> Self {
143 Self::Handler {
144 message: message.into(),
145 context: Some(context.into()),
146 }
147 }
148
149 pub fn configuration(message: impl Into<String>) -> Self {
151 Self::Configuration {
152 message: message.into(),
153 key: None,
154 }
155 }
156
157 pub fn configuration_with_key(message: impl Into<String>, key: impl Into<String>) -> Self {
159 Self::Configuration {
160 message: message.into(),
161 key: Some(key.into()),
162 }
163 }
164
165 pub fn authentication(message: impl Into<String>) -> Self {
167 Self::Authentication {
168 message: message.into(),
169 method: None,
170 }
171 }
172
173 pub fn authentication_with_method(
175 message: impl Into<String>,
176 method: impl Into<String>,
177 ) -> Self {
178 Self::Authentication {
179 message: message.into(),
180 method: Some(method.into()),
181 }
182 }
183
184 pub fn authorization(message: impl Into<String>) -> Self {
186 Self::Authorization {
187 message: message.into(),
188 resource: None,
189 }
190 }
191
192 pub fn authorization_with_resource(
194 message: impl Into<String>,
195 resource: impl Into<String>,
196 ) -> Self {
197 Self::Authorization {
198 message: message.into(),
199 resource: Some(resource.into()),
200 }
201 }
202
203 pub fn rate_limit(message: impl Into<String>) -> Self {
205 Self::RateLimit {
206 message: message.into(),
207 retry_after: None,
208 }
209 }
210
211 pub fn rate_limit_with_retry(message: impl Into<String>, retry_after: u64) -> Self {
213 Self::RateLimit {
214 message: message.into(),
215 retry_after: Some(retry_after),
216 }
217 }
218
219 pub fn middleware(name: impl Into<String>, message: impl Into<String>) -> Self {
221 Self::Middleware {
222 name: name.into(),
223 message: message.into(),
224 }
225 }
226
227 pub fn routing(message: impl Into<String>) -> Self {
229 Self::Routing {
230 message: message.into(),
231 method: None,
232 }
233 }
234
235 pub fn routing_with_method(message: impl Into<String>, method: impl Into<String>) -> Self {
237 Self::Routing {
238 message: message.into(),
239 method: Some(method.into()),
240 }
241 }
242
243 pub fn not_found(resource: impl Into<String>) -> Self {
245 Self::NotFound {
246 resource: resource.into(),
247 }
248 }
249
250 pub fn timeout(operation: impl Into<String>, timeout_ms: u64) -> Self {
252 Self::Timeout {
253 operation: operation.into(),
254 timeout_ms,
255 }
256 }
257
258 pub fn resource_exhausted(resource: impl Into<String>) -> Self {
260 Self::ResourceExhausted {
261 resource: resource.into(),
262 current: None,
263 max: None,
264 }
265 }
266
267 pub fn resource_exhausted_with_usage(
269 resource: impl Into<String>,
270 current: usize,
271 max: usize,
272 ) -> Self {
273 Self::ResourceExhausted {
274 resource: resource.into(),
275 current: Some(current),
276 max: Some(max),
277 }
278 }
279
280 #[must_use]
282 pub const fn is_retryable(&self) -> bool {
283 matches!(
284 self,
285 Self::Timeout { .. } | Self::ResourceExhausted { .. } | Self::RateLimit { .. }
286 )
287 }
288
289 #[must_use]
291 pub const fn is_fatal(&self) -> bool {
292 matches!(
293 self,
294 Self::Lifecycle(_) | Self::Shutdown(_) | Self::Internal(_)
295 )
296 }
297
298 #[must_use]
300 pub const fn error_code(&self) -> i32 {
301 match self {
302 Self::Core(_) => -32603,
303 Self::NotFound { .. } => -32004,
304 Self::Authentication { .. } => -32008,
305 Self::Authorization { .. } => -32005,
306 Self::RateLimit { .. } => -32009,
307 Self::ResourceExhausted { .. } => -32010,
308 Self::Timeout { .. } => -32603,
309 Self::Handler { .. } => -32002,
310 _ => -32603,
311 }
312 }
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub enum ErrorRecovery {
318 Retry,
320 Skip,
322 Fail,
324 Degrade,
326}
327
328#[derive(Debug, Clone)]
330pub struct ErrorContext {
331 pub category: String,
333 pub operation: String,
335 pub request_id: Option<String>,
337 pub client_id: Option<String>,
339 pub metadata: std::collections::HashMap<String, String>,
341}
342
343impl ErrorContext {
344 pub fn new(category: impl Into<String>, operation: impl Into<String>) -> Self {
346 Self {
347 category: category.into(),
348 operation: operation.into(),
349 request_id: None,
350 client_id: None,
351 metadata: std::collections::HashMap::new(),
352 }
353 }
354
355 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
357 self.request_id = Some(request_id.into());
358 self
359 }
360
361 pub fn with_client_id(mut self, client_id: impl Into<String>) -> Self {
363 self.client_id = Some(client_id.into());
364 self
365 }
366
367 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
369 self.metadata.insert(key.into(), value.into());
370 self
371 }
372}
373
374impl From<Box<turbomcp_core::Error>> for ServerError {
376 fn from(core_error: Box<turbomcp_core::Error>) -> Self {
377 use turbomcp_core::ErrorKind;
378
379 match core_error.kind {
380 ErrorKind::Handler => Self::Handler {
381 message: core_error.message,
382 context: core_error.context.operation,
383 },
384 ErrorKind::Authentication => Self::Authentication {
385 message: core_error.message,
386 method: None,
387 },
388 ErrorKind::PermissionDenied => Self::Authorization {
389 message: core_error.message,
390 resource: None,
391 },
392 ErrorKind::NotFound => Self::NotFound {
393 resource: core_error.message,
394 },
395 ErrorKind::BadRequest | ErrorKind::Validation => Self::Handler {
396 message: format!("Validation error: {}", core_error.message),
397 context: None,
398 },
399 ErrorKind::Timeout => Self::Timeout {
400 operation: core_error
401 .context
402 .operation
403 .unwrap_or_else(|| "unknown".to_string()),
404 timeout_ms: 30000, },
406 ErrorKind::RateLimited => Self::RateLimit {
407 message: core_error.message,
408 retry_after: None,
409 },
410 ErrorKind::Configuration => Self::Configuration {
411 message: core_error.message,
412 key: None,
413 },
414 ErrorKind::Transport => {
415 Self::Internal(format!("Transport error: {}", core_error.message))
416 }
417 ErrorKind::Serialization => {
418 Self::Internal(format!("Serialization error: {}", core_error.message))
419 }
420 ErrorKind::Protocol => {
421 Self::Internal(format!("Protocol error: {}", core_error.message))
422 }
423 ErrorKind::Unavailable => Self::ResourceExhausted {
424 resource: "service".to_string(),
425 current: None,
426 max: None,
427 },
428 ErrorKind::ExternalService => {
429 Self::Internal(format!("External service error: {}", core_error.message))
430 }
431 ErrorKind::Cancelled => {
432 Self::Internal(format!("Operation cancelled: {}", core_error.message))
433 }
434 ErrorKind::Internal => Self::Internal(core_error.message),
435 }
436 }
437}
438
439