Skip to main content

mcpkit_server/
server.rs

1//! Server runtime for MCP servers.
2//!
3//! This module provides the runtime that executes an MCP server over
4//! a transport, handling message routing, request correlation, and
5//! the connection lifecycle.
6//!
7//! # Overview
8//!
9//! The server runtime:
10//! 1. Accepts a transport for communication
11//! 2. Handles the initialize/initialized handshake
12//! 3. Routes incoming requests to the appropriate handlers
13//! 4. Manages the connection lifecycle
14//!
15//! # Example
16//!
17//! ```rust
18//! use mcpkit_server::{ServerBuilder, ServerHandler, ServerState};
19//! use mcpkit_core::capability::{ServerInfo, ServerCapabilities};
20//!
21//! struct MyHandler;
22//! impl ServerHandler for MyHandler {
23//!     fn server_info(&self) -> ServerInfo {
24//!         ServerInfo::new("my-server", "1.0.0")
25//!     }
26//! }
27//!
28//! // Build a server and create server state
29//! let server = ServerBuilder::new(MyHandler).build();
30//! let state = ServerState::new(server.capabilities().clone());
31//!
32//! assert!(!state.is_initialized());
33//! ```
34
35use crate::builder::{NotRegistered, Registered, Server};
36use crate::context::{CancellationToken, Context, Peer};
37use crate::handler::{PromptHandler, ResourceHandler, ServerHandler, ToolHandler};
38use mcpkit_core::capability::{ClientCapabilities, ServerCapabilities};
39use mcpkit_core::error::McpError;
40use mcpkit_core::protocol::{Message, Notification, ProgressToken, Request, Response};
41use mcpkit_core::protocol_version::ProtocolVersion;
42use mcpkit_core::types::CallToolResult;
43use mcpkit_transport::Transport;
44use std::collections::HashMap;
45use std::sync::Arc;
46use std::sync::RwLock;
47use std::sync::atomic::{AtomicBool, Ordering};
48
49/// State for a running server.
50pub struct ServerState {
51    /// Client capabilities negotiated during initialization.
52    pub client_caps: RwLock<ClientCapabilities>,
53    /// Server capabilities advertised during initialization.
54    pub server_caps: ServerCapabilities,
55    /// Whether the server has been initialized.
56    pub initialized: AtomicBool,
57    /// Active cancellation tokens by request ID.
58    pub cancellations: RwLock<HashMap<String, CancellationToken>>,
59    /// The protocol version negotiated during initialization.
60    ///
61    /// This is stored as a `ProtocolVersion` enum for type-safe feature detection.
62    /// Use methods like `protocol_version().supports_tasks()` to check capabilities.
63    pub negotiated_version: RwLock<Option<ProtocolVersion>>,
64}
65
66impl ServerState {
67    /// Create a new server state.
68    #[must_use]
69    pub fn new(server_caps: ServerCapabilities) -> Self {
70        Self {
71            client_caps: RwLock::new(ClientCapabilities::default()),
72            server_caps,
73            initialized: AtomicBool::new(false),
74            cancellations: RwLock::new(HashMap::new()),
75            negotiated_version: RwLock::new(None),
76        }
77    }
78
79    /// Get the negotiated protocol version.
80    ///
81    /// Returns `None` if not yet initialized.
82    ///
83    /// # Example
84    ///
85    /// ```rust,ignore
86    /// if let Some(version) = state.protocol_version() {
87    ///     if version.supports_tasks() {
88    ///         // Tasks are available in this session
89    ///     }
90    /// }
91    /// ```
92    pub fn protocol_version(&self) -> Option<ProtocolVersion> {
93        self.negotiated_version.read().ok().and_then(|guard| *guard)
94    }
95
96    /// Set the negotiated protocol version.
97    ///
98    /// Silently fails if the lock is poisoned.
99    pub fn set_protocol_version(&self, version: ProtocolVersion) {
100        if let Ok(mut guard) = self.negotiated_version.write() {
101            *guard = Some(version);
102        }
103    }
104
105    /// Get a snapshot of client capabilities.
106    ///
107    /// Returns default capabilities if the lock is poisoned.
108    pub fn client_caps(&self) -> ClientCapabilities {
109        self.client_caps
110            .read()
111            .map(|guard| guard.clone())
112            .unwrap_or_default()
113    }
114
115    /// Update client capabilities.
116    ///
117    /// Silently fails if the lock is poisoned.
118    pub fn set_client_caps(&self, caps: ClientCapabilities) {
119        if let Ok(mut guard) = self.client_caps.write() {
120            *guard = caps;
121        }
122    }
123
124    /// Check if the server is initialized.
125    pub fn is_initialized(&self) -> bool {
126        self.initialized.load(Ordering::Acquire)
127    }
128
129    /// Mark the server as initialized.
130    pub fn set_initialized(&self) {
131        self.initialized.store(true, Ordering::Release);
132    }
133
134    /// Register a cancellation token for a request.
135    pub fn register_cancellation(&self, request_id: &str, token: CancellationToken) {
136        if let Ok(mut cancellations) = self.cancellations.write() {
137            cancellations.insert(request_id.to_string(), token);
138        }
139    }
140
141    /// Cancel a request by ID.
142    pub fn cancel_request(&self, request_id: &str) {
143        if let Ok(cancellations) = self.cancellations.read() {
144            if let Some(token) = cancellations.get(request_id) {
145                token.cancel();
146            }
147        }
148    }
149
150    /// Remove a cancellation token after request completion.
151    pub fn remove_cancellation(&self, request_id: &str) {
152        if let Ok(mut cancellations) = self.cancellations.write() {
153            cancellations.remove(request_id);
154        }
155    }
156}
157
158/// A peer implementation that sends notifications over a transport.
159pub struct TransportPeer<T: Transport> {
160    transport: Arc<T>,
161}
162
163impl<T: Transport> TransportPeer<T> {
164    /// Create a new transport peer.
165    pub const fn new(transport: Arc<T>) -> Self {
166        Self { transport }
167    }
168}
169
170impl<T: Transport + 'static> Peer for TransportPeer<T>
171where
172    T::Error: Into<McpError>,
173{
174    fn notify(
175        &self,
176        notification: Notification,
177    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), McpError>> + Send + '_>>
178    {
179        let transport = self.transport.clone();
180        Box::pin(async move {
181            transport
182                .send(Message::Notification(notification))
183                .await
184                .map_err(std::convert::Into::into)
185        })
186    }
187}
188
189/// Server runtime configuration.
190#[derive(Debug, Clone)]
191pub struct RuntimeConfig {
192    /// Whether to automatically send initialized notification.
193    pub auto_initialized: bool,
194    /// Maximum concurrent requests to process.
195    pub max_concurrent_requests: usize,
196}
197
198impl Default for RuntimeConfig {
199    fn default() -> Self {
200        Self {
201            auto_initialized: true,
202            max_concurrent_requests: 100,
203        }
204    }
205}
206
207/// Server runtime that handles the message loop.
208///
209/// This runtime manages the connection lifecycle, routes requests to
210/// handlers, and coordinates response delivery.
211pub struct ServerRuntime<S, Tr>
212where
213    Tr: Transport,
214{
215    server: S,
216    transport: Arc<Tr>,
217    state: Arc<ServerState>,
218    /// Runtime configuration (concurrency limit, etc.).
219    config: RuntimeConfig,
220}
221
222impl<S, Tr> ServerRuntime<S, Tr>
223where
224    S: RequestRouter + Send + Sync,
225    Tr: Transport + 'static,
226    Tr::Error: Into<McpError>,
227{
228    /// Get the server state.
229    pub const fn state(&self) -> &Arc<ServerState> {
230        &self.state
231    }
232
233    /// Run the server message loop.
234    ///
235    /// This method runs until the connection is closed or an error occurs.
236    ///
237    /// Requests are processed concurrently (interleaved on this task) up to
238    /// `config.max_concurrent_requests` in flight at once; once that limit is
239    /// reached, no new messages are accepted until an in-flight request
240    /// completes (backpressure). Each request runs with panic isolation, so a
241    /// panicking handler returns a JSON-RPC internal error instead of tearing
242    /// down the connection. Notifications are handled inline.
243    pub async fn run(&self) -> Result<(), McpError> {
244        use futures::future::{Either, select};
245        use futures::stream::{FuturesUnordered, StreamExt};
246
247        let max = self.config.max_concurrent_requests.max(1);
248        let mut in_flight = FuturesUnordered::new();
249
250        let outcome = loop {
251            // Obtain the next incoming message, making progress on in-flight
252            // requests in the meantime. `in_flight.next()` is only awaited while
253            // the set is non-empty, so it never spuriously yields `None`.
254            let message = if in_flight.is_empty() {
255                match self.transport.recv().await {
256                    Ok(opt) => opt,
257                    Err(e) => break Err(e.into()),
258                }
259            } else if in_flight.len() < max {
260                // Race the next message against in-flight completions.
261                let recv = std::pin::pin!(self.transport.recv());
262                match select(recv, in_flight.next()).await {
263                    Either::Left((Ok(opt), _)) => opt,
264                    Either::Left((Err(e), _)) => break Err(e.into()),
265                    // An in-flight request finished; its response was already
266                    // sent. Loop to keep accepting work.
267                    Either::Right((_, _)) => continue,
268                }
269            } else {
270                // At the concurrency limit: drain one in-flight request before
271                // accepting any new message (backpressure).
272                in_flight.next().await;
273                continue;
274            };
275
276            match message {
277                Some(Message::Request(request)) => {
278                    in_flight.push(self.handle_request_isolated(request));
279                }
280                Some(Message::Notification(notification)) => {
281                    if let Err(e) = self.handle_notification(notification).await {
282                        tracing::error!(error = %e, "Error handling notification");
283                    }
284                }
285                Some(Message::Response(_)) => {
286                    tracing::warn!("Received unexpected response message");
287                }
288                None => {
289                    tracing::info!("Connection closed");
290                    break Ok(());
291                }
292            }
293        };
294
295        // Drain any still-running requests so their responses are delivered
296        // before we return.
297        while in_flight.next().await.is_some() {}
298
299        if let Err(ref err) = outcome {
300            tracing::error!(error = %err, "Transport error");
301        }
302        outcome
303    }
304
305    /// Compute the result for a request without sending it.
306    async fn compute_response(&self, request: &Request) -> Result<serde_json::Value, McpError> {
307        match request.method.as_ref() {
308            "initialize" => self.handle_initialize(request).await,
309            _ if !self.state.is_initialized() => {
310                Err(McpError::invalid_request("Server not initialized"))
311            }
312            _ => self.route_request(request).await,
313        }
314    }
315
316    /// Handle a request with panic isolation, sending the response when done.
317    ///
318    /// A panic in the handler is caught and converted into a JSON-RPC internal
319    /// error response so a single misbehaving handler cannot tear down the
320    /// whole connection.
321    async fn handle_request_isolated(&self, request: Request) {
322        use futures::FutureExt;
323        use std::panic::AssertUnwindSafe;
324
325        let id = request.id.clone();
326        tracing::debug!(method = %request.method, id = %id, "Handling request");
327
328        let computed = AssertUnwindSafe(self.compute_response(&request))
329            .catch_unwind()
330            .await;
331
332        let response_msg = match computed {
333            Ok(Ok(result)) => Response::success(id, result),
334            Ok(Err(e)) => Response::error(id, e.into()),
335            Err(panic) => {
336                let detail = panic_message(&*panic);
337                tracing::error!(method = %request.method, panic = %detail, "Handler panicked");
338                Response::error(
339                    id,
340                    McpError::internal(format!("handler panicked: {detail}")).into(),
341                )
342            }
343        };
344
345        if let Err(e) = self.transport.send(Message::Response(response_msg)).await {
346            let err: McpError = e.into();
347            tracing::error!(error = %err, "Failed to send response");
348        }
349    }
350
351    /// Handle the initialize request.
352    ///
353    /// This performs protocol version negotiation according to the MCP specification:
354    /// 1. Client sends its preferred protocol version
355    /// 2. Server responds with the same version if supported, or its preferred version
356    /// 3. Client must support the returned version or disconnect
357    async fn handle_initialize(&self, request: &Request) -> Result<serde_json::Value, McpError> {
358        if self.state.is_initialized() {
359            return Err(McpError::invalid_request("Already initialized"));
360        }
361
362        // Parse initialize params
363        let params = request
364            .params
365            .as_ref()
366            .ok_or_else(|| McpError::invalid_params("initialize", "missing params"))?;
367
368        // Extract and negotiate protocol version using type-safe enum
369        let requested_version_str = params
370            .get("protocolVersion")
371            .and_then(|v| v.as_str())
372            .unwrap_or("");
373
374        // Negotiate using the ProtocolVersion enum for type safety
375        let negotiated_version =
376            ProtocolVersion::negotiate(requested_version_str, ProtocolVersion::ALL)
377                .unwrap_or(ProtocolVersion::LATEST);
378
379        // Log version negotiation details for debugging
380        if requested_version_str == negotiated_version.as_str() {
381            tracing::debug!(
382                version = %negotiated_version,
383                "Protocol version negotiated successfully"
384            );
385        } else {
386            tracing::info!(
387                requested = %requested_version_str,
388                negotiated = %negotiated_version,
389                supported = ?ProtocolVersion::ALL.iter().map(ProtocolVersion::as_str).collect::<Vec<_>>(),
390                "Protocol version negotiation: client requested different version"
391            );
392        }
393
394        // Store the negotiated version (type-safe enum)
395        self.state.set_protocol_version(negotiated_version);
396
397        // Extract client info and capabilities
398        if let Some(caps) = params.get("capabilities") {
399            if let Ok(client_caps) = serde_json::from_value::<ClientCapabilities>(caps.clone()) {
400                self.state.set_client_caps(client_caps);
401            }
402        }
403
404        // Build response with negotiated version (serialized to string by serde)
405        let result = serde_json::json!({
406            "protocolVersion": negotiated_version.as_str(),
407            "serverInfo": self.server.server_info(),
408            "capabilities": self.state.server_caps
409        });
410
411        self.state.set_initialized();
412
413        Ok(result)
414    }
415
416    /// Route a request to the appropriate handler.
417    async fn route_request(&self, request: &Request) -> Result<serde_json::Value, McpError> {
418        let method = request.method.as_ref();
419        let params = request.params.as_ref();
420
421        // Extract progress token from params._meta.progressToken if present
422        let progress_token = extract_progress_token(params);
423
424        // Create context for the handler
425        let peer = TransportPeer::new(self.transport.clone());
426        let client_caps = self.state.client_caps();
427        let protocol_version = self
428            .state
429            .protocol_version()
430            .unwrap_or(ProtocolVersion::LATEST);
431        let ctx = Context::new(
432            &request.id,
433            progress_token.as_ref(),
434            &client_caps,
435            &self.state.server_caps,
436            protocol_version,
437            &peer,
438        );
439
440        // Delegate to the router
441        self.server.route(method, params, &ctx).await
442    }
443
444    /// Handle a notification.
445    async fn handle_notification(&self, notification: Notification) -> Result<(), McpError> {
446        let method = notification.method.as_ref();
447
448        tracing::debug!(method = %method, "Handling notification");
449
450        match method {
451            "notifications/initialized" => {
452                tracing::info!("Client sent initialized notification");
453                Ok(())
454            }
455            "notifications/cancelled" => {
456                if let Some(params) = &notification.params {
457                    if let Some(request_id) = params.get("requestId").and_then(|v| v.as_str()) {
458                        self.state.cancel_request(request_id);
459                    }
460                }
461                Ok(())
462            }
463            _ => {
464                tracing::debug!(method = %method, "Ignoring unknown notification");
465                Ok(())
466            }
467        }
468    }
469}
470
471// Constructor implementations for ServerRuntime with different server types
472impl<H, T, R, P, K, Tr> ServerRuntime<Server<H, T, R, P, K>, Tr>
473where
474    H: ServerHandler + Send + Sync,
475    T: Send + Sync,
476    R: Send + Sync,
477    P: Send + Sync,
478    K: Send + Sync,
479    Tr: Transport + 'static,
480    Tr::Error: Into<McpError>,
481{
482    /// Create a new server runtime.
483    pub fn new(server: Server<H, T, R, P, K>, transport: Tr) -> Self {
484        let caps = server.capabilities().clone();
485        Self {
486            server,
487            transport: Arc::new(transport),
488            state: Arc::new(ServerState::new(caps)),
489            config: RuntimeConfig::default(),
490        }
491    }
492
493    /// Create a new server runtime with custom configuration.
494    pub fn with_config(
495        server: Server<H, T, R, P, K>,
496        transport: Tr,
497        config: RuntimeConfig,
498    ) -> Self {
499        let caps = server.capabilities().clone();
500        Self {
501            server,
502            transport: Arc::new(transport),
503            state: Arc::new(ServerState::new(caps)),
504            config,
505        }
506    }
507}
508
509/// Trait for routing requests to handlers.
510///
511/// This trait is implemented by Server with different bounds depending on
512/// which handlers are registered.
513#[allow(async_fn_in_trait)]
514pub trait RequestRouter: Send + Sync {
515    /// Get the server info.
516    fn server_info(&self) -> mcpkit_core::capability::ServerInfo;
517
518    /// Route a request and return the result.
519    async fn route(
520        &self,
521        method: &str,
522        params: Option<&serde_json::Value>,
523        ctx: &Context<'_>,
524    ) -> Result<serde_json::Value, McpError>;
525}
526
527/// Extension methods for Server to run with a transport.
528impl<H, T, R, P, K> Server<H, T, R, P, K>
529where
530    H: ServerHandler + Send + Sync + 'static,
531    T: Send + Sync + 'static,
532    R: Send + Sync + 'static,
533    P: Send + Sync + 'static,
534    K: Send + Sync + 'static,
535    Self: RequestRouter,
536{
537    /// Run this server over the given transport.
538    pub async fn serve<Tr>(self, transport: Tr) -> Result<(), McpError>
539    where
540        Tr: Transport + 'static,
541        Tr::Error: Into<McpError>,
542    {
543        let runtime = ServerRuntime::new(self, transport);
544        runtime.run().await
545    }
546}
547
548// ============================================================================
549// RequestRouter implementations via macro
550// ============================================================================
551
552// Internal routing functions to reduce code duplication.
553// Each function handles a specific handler type's methods.
554
555async fn route_tools<TH: ToolHandler + Send + Sync>(
556    handler: &TH,
557    method: &str,
558    params: Option<&serde_json::Value>,
559    ctx: &Context<'_>,
560) -> Option<Result<serde_json::Value, McpError>> {
561    match method {
562        "tools/list" => {
563            tracing::debug!("Listing available tools");
564            let result = handler.list_tools(ctx).await;
565            match &result {
566                Ok(tools) => tracing::debug!(count = tools.len(), "Listed tools"),
567                Err(e) => tracing::warn!(error = %e, "Failed to list tools"),
568            }
569            Some(result.map(|tools| serde_json::json!({ "tools": tools })))
570        }
571        "tools/call" => {
572            let result = async {
573                let params = params.ok_or_else(|| {
574                    McpError::invalid_params("tools/call", "missing params")
575                })?;
576                let name = params.get("name")
577                    .and_then(|v| v.as_str())
578                    .ok_or_else(|| McpError::invalid_params("tools/call", "missing tool name"))?;
579                let args = params.get("arguments")
580                    .cloned()
581                    .unwrap_or_else(|| serde_json::json!({}));
582
583                tracing::info!(tool = %name, "Calling tool");
584                let start = std::time::Instant::now();
585                let output = handler.call_tool(name, args, ctx).await;
586                let duration = start.elapsed();
587
588                match &output {
589                    Ok(_) => tracing::info!(tool = %name, duration_ms = duration.as_millis(), "Tool call completed"),
590                    Err(e) => tracing::warn!(tool = %name, duration_ms = duration.as_millis(), error = %e, "Tool call failed"),
591                }
592
593                let output = output?;
594                let result: CallToolResult = output.into();
595                Ok(serde_json::to_value(result).unwrap_or_else(|_| serde_json::json!({})))
596            }.await;
597            Some(result)
598        }
599        _ => None,
600    }
601}
602
603async fn route_resources<RH: ResourceHandler + Send + Sync>(
604    handler: &RH,
605    method: &str,
606    params: Option<&serde_json::Value>,
607    ctx: &Context<'_>,
608) -> Option<Result<serde_json::Value, McpError>> {
609    match method {
610        "resources/list" => {
611            tracing::debug!("Listing available resources");
612            let result = handler.list_resources(ctx).await;
613            match &result {
614                Ok(resources) => tracing::debug!(count = resources.len(), "Listed resources"),
615                Err(e) => tracing::warn!(error = %e, "Failed to list resources"),
616            }
617            Some(result.map(|resources| serde_json::json!({ "resources": resources })))
618        }
619        "resources/templates/list" => {
620            tracing::debug!("Listing available resource templates");
621            let result = handler.list_resource_templates(ctx).await;
622            match &result {
623                Ok(templates) => {
624                    tracing::debug!(count = templates.len(), "Listed resource templates");
625                }
626                Err(e) => tracing::warn!(error = %e, "Failed to list resource templates"),
627            }
628            Some(result.map(|templates| serde_json::json!({ "resourceTemplates": templates })))
629        }
630        "resources/read" => {
631            let result = async {
632                let params = params.ok_or_else(|| {
633                    McpError::invalid_params("resources/read", "missing params")
634                })?;
635                let uri = params.get("uri")
636                    .and_then(|v| v.as_str())
637                    .ok_or_else(|| McpError::invalid_params("resources/read", "missing uri"))?;
638
639                tracing::info!(uri = %uri, "Reading resource");
640                let start = std::time::Instant::now();
641                let contents = handler.read_resource(uri, ctx).await;
642                let duration = start.elapsed();
643
644                match &contents {
645                    Ok(_) => tracing::info!(uri = %uri, duration_ms = duration.as_millis(), "Resource read completed"),
646                    Err(e) => tracing::warn!(uri = %uri, duration_ms = duration.as_millis(), error = %e, "Resource read failed"),
647                }
648
649                let contents = contents?;
650                Ok(serde_json::json!({ "contents": contents }))
651            }.await;
652            Some(result)
653        }
654        _ => None,
655    }
656}
657
658async fn route_prompts<PH: PromptHandler + Send + Sync>(
659    handler: &PH,
660    method: &str,
661    params: Option<&serde_json::Value>,
662    ctx: &Context<'_>,
663) -> Option<Result<serde_json::Value, McpError>> {
664    match method {
665        "prompts/list" => {
666            tracing::debug!("Listing available prompts");
667            let result = handler.list_prompts(ctx).await;
668            match &result {
669                Ok(prompts) => tracing::debug!(count = prompts.len(), "Listed prompts"),
670                Err(e) => tracing::warn!(error = %e, "Failed to list prompts"),
671            }
672            Some(result.map(|prompts| serde_json::json!({ "prompts": prompts })))
673        }
674        "prompts/get" => {
675            let result = async {
676                let params = params.ok_or_else(|| {
677                    McpError::invalid_params("prompts/get", "missing params")
678                })?;
679                let name = params.get("name")
680                    .and_then(|v| v.as_str())
681                    .ok_or_else(|| McpError::invalid_params("prompts/get", "missing prompt name"))?;
682                let args = params.get("arguments")
683                    .and_then(|v| v.as_object())
684                    .cloned();
685
686                tracing::info!(prompt = %name, "Getting prompt");
687                let start = std::time::Instant::now();
688                let prompt_result = handler.get_prompt(name, args, ctx).await;
689                let duration = start.elapsed();
690
691                match &prompt_result {
692                    Ok(_) => tracing::info!(prompt = %name, duration_ms = duration.as_millis(), "Prompt retrieval completed"),
693                    Err(e) => tracing::warn!(prompt = %name, duration_ms = duration.as_millis(), error = %e, "Prompt retrieval failed"),
694                }
695
696                let result = prompt_result?;
697                Ok(serde_json::to_value(result).unwrap_or_else(|_| serde_json::json!({})))
698            }.await;
699            Some(result)
700        }
701        _ => None,
702    }
703}
704
705/// Macro to generate `RequestRouter` implementations for all handler combinations.
706///
707/// This macro reduces code duplication by generating all 2^3 = 8 combinations
708/// of tool/resource/prompt handler registration states.
709macro_rules! impl_request_router {
710    // Base case: no handlers
711    (base; $($bounds:tt)*) => {
712        impl<H $($bounds)*> RequestRouter for Server<H, NotRegistered, NotRegistered, NotRegistered, NotRegistered>
713        where
714            H: ServerHandler + Send + Sync,
715        {
716            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
717                self.handler().server_info()
718            }
719
720            async fn route(
721                &self,
722                method: &str,
723                _params: Option<&serde_json::Value>,
724                _ctx: &Context<'_>,
725            ) -> Result<serde_json::Value, McpError> {
726                match method {
727                    "ping" => Ok(serde_json::json!({})),
728                    _ => Err(McpError::method_not_found(method)),
729                }
730            }
731        }
732    };
733
734    // Tools only
735    (tools; $($bounds:tt)*) => {
736        impl<H, TH $($bounds)*> RequestRouter for Server<H, Registered<TH>, NotRegistered, NotRegistered, NotRegistered>
737        where
738            H: ServerHandler + Send + Sync,
739            TH: ToolHandler + Send + Sync,
740        {
741            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
742                self.handler().server_info()
743            }
744
745            async fn route(
746                &self,
747                method: &str,
748                params: Option<&serde_json::Value>,
749                ctx: &Context<'_>,
750            ) -> Result<serde_json::Value, McpError> {
751                if method == "ping" {
752                    return Ok(serde_json::json!({}));
753                }
754                if let Some(result) = route_tools(self.tool_handler(), method, params, ctx).await {
755                    return result;
756                }
757                Err(McpError::method_not_found(method))
758            }
759        }
760    };
761
762    // Resources only
763    (resources; $($bounds:tt)*) => {
764        impl<H, RH $($bounds)*> RequestRouter for Server<H, NotRegistered, Registered<RH>, NotRegistered, NotRegistered>
765        where
766            H: ServerHandler + Send + Sync,
767            RH: ResourceHandler + Send + Sync,
768        {
769            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
770                self.handler().server_info()
771            }
772
773            async fn route(
774                &self,
775                method: &str,
776                params: Option<&serde_json::Value>,
777                ctx: &Context<'_>,
778            ) -> Result<serde_json::Value, McpError> {
779                if method == "ping" {
780                    return Ok(serde_json::json!({}));
781                }
782                if let Some(result) = route_resources(self.resource_handler(), method, params, ctx).await {
783                    return result;
784                }
785                Err(McpError::method_not_found(method))
786            }
787        }
788    };
789
790    // Prompts only
791    (prompts; $($bounds:tt)*) => {
792        impl<H, PH $($bounds)*> RequestRouter for Server<H, NotRegistered, NotRegistered, Registered<PH>, NotRegistered>
793        where
794            H: ServerHandler + Send + Sync,
795            PH: PromptHandler + Send + Sync,
796        {
797            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
798                self.handler().server_info()
799            }
800
801            async fn route(
802                &self,
803                method: &str,
804                params: Option<&serde_json::Value>,
805                ctx: &Context<'_>,
806            ) -> Result<serde_json::Value, McpError> {
807                if method == "ping" {
808                    return Ok(serde_json::json!({}));
809                }
810                if let Some(result) = route_prompts(self.prompt_handler(), method, params, ctx).await {
811                    return result;
812                }
813                Err(McpError::method_not_found(method))
814            }
815        }
816    };
817
818    // Tools + Resources
819    (tools_resources; $($bounds:tt)*) => {
820        impl<H, TH, RH $($bounds)*> RequestRouter for Server<H, Registered<TH>, Registered<RH>, NotRegistered, NotRegistered>
821        where
822            H: ServerHandler + Send + Sync,
823            TH: ToolHandler + Send + Sync,
824            RH: ResourceHandler + Send + Sync,
825        {
826            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
827                self.handler().server_info()
828            }
829
830            async fn route(
831                &self,
832                method: &str,
833                params: Option<&serde_json::Value>,
834                ctx: &Context<'_>,
835            ) -> Result<serde_json::Value, McpError> {
836                if method == "ping" {
837                    return Ok(serde_json::json!({}));
838                }
839                if let Some(result) = route_tools(self.tool_handler(), method, params, ctx).await {
840                    return result;
841                }
842                if let Some(result) = route_resources(self.resource_handler(), method, params, ctx).await {
843                    return result;
844                }
845                Err(McpError::method_not_found(method))
846            }
847        }
848    };
849
850    // Tools + Prompts
851    (tools_prompts; $($bounds:tt)*) => {
852        impl<H, TH, PH $($bounds)*> RequestRouter for Server<H, Registered<TH>, NotRegistered, Registered<PH>, NotRegistered>
853        where
854            H: ServerHandler + Send + Sync,
855            TH: ToolHandler + Send + Sync,
856            PH: PromptHandler + Send + Sync,
857        {
858            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
859                self.handler().server_info()
860            }
861
862            async fn route(
863                &self,
864                method: &str,
865                params: Option<&serde_json::Value>,
866                ctx: &Context<'_>,
867            ) -> Result<serde_json::Value, McpError> {
868                if method == "ping" {
869                    return Ok(serde_json::json!({}));
870                }
871                if let Some(result) = route_tools(self.tool_handler(), method, params, ctx).await {
872                    return result;
873                }
874                if let Some(result) = route_prompts(self.prompt_handler(), method, params, ctx).await {
875                    return result;
876                }
877                Err(McpError::method_not_found(method))
878            }
879        }
880    };
881
882    // Resources + Prompts
883    (resources_prompts; $($bounds:tt)*) => {
884        impl<H, RH, PH $($bounds)*> RequestRouter for Server<H, NotRegistered, Registered<RH>, Registered<PH>, NotRegistered>
885        where
886            H: ServerHandler + Send + Sync,
887            RH: ResourceHandler + Send + Sync,
888            PH: PromptHandler + Send + Sync,
889        {
890            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
891                self.handler().server_info()
892            }
893
894            async fn route(
895                &self,
896                method: &str,
897                params: Option<&serde_json::Value>,
898                ctx: &Context<'_>,
899            ) -> Result<serde_json::Value, McpError> {
900                if method == "ping" {
901                    return Ok(serde_json::json!({}));
902                }
903                if let Some(result) = route_resources(self.resource_handler(), method, params, ctx).await {
904                    return result;
905                }
906                if let Some(result) = route_prompts(self.prompt_handler(), method, params, ctx).await {
907                    return result;
908                }
909                Err(McpError::method_not_found(method))
910            }
911        }
912    };
913
914    // Tools + Resources + Prompts
915    (tools_resources_prompts; $($bounds:tt)*) => {
916        impl<H, TH, RH, PH $($bounds)*> RequestRouter for Server<H, Registered<TH>, Registered<RH>, Registered<PH>, NotRegistered>
917        where
918            H: ServerHandler + Send + Sync,
919            TH: ToolHandler + Send + Sync,
920            RH: ResourceHandler + Send + Sync,
921            PH: PromptHandler + Send + Sync,
922        {
923            fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
924                self.handler().server_info()
925            }
926
927            async fn route(
928                &self,
929                method: &str,
930                params: Option<&serde_json::Value>,
931                ctx: &Context<'_>,
932            ) -> Result<serde_json::Value, McpError> {
933                if method == "ping" {
934                    return Ok(serde_json::json!({}));
935                }
936                if let Some(result) = route_tools(self.tool_handler(), method, params, ctx).await {
937                    return result;
938                }
939                if let Some(result) = route_resources(self.resource_handler(), method, params, ctx).await {
940                    return result;
941                }
942                if let Some(result) = route_prompts(self.prompt_handler(), method, params, ctx).await {
943                    return result;
944                }
945                Err(McpError::method_not_found(method))
946            }
947        }
948    };
949}
950
951// Generate all RequestRouter implementations
952impl_request_router!(base;);
953impl_request_router!(tools;);
954impl_request_router!(resources;);
955impl_request_router!(prompts;);
956impl_request_router!(tools_resources;);
957impl_request_router!(tools_prompts;);
958impl_request_router!(resources_prompts;);
959impl_request_router!(tools_resources_prompts;);
960
961// ============================================================================
962// Helper functions
963// ============================================================================
964
965/// Extract a progress token from request parameters.
966///
967/// Per the MCP specification, progress tokens are sent in the `_meta.progressToken`
968/// field of request parameters. This function attempts to extract and parse that
969/// field into a `ProgressToken`.
970///
971/// # Example JSON structure
972/// ```json
973/// {
974///   "_meta": {
975///     "progressToken": "token-123"
976///   },
977///   "name": "my-tool",
978///   "arguments": {}
979/// }
980/// ```
981/// Extract a human-readable message from a caught panic payload.
982fn panic_message(panic: &(dyn std::any::Any + Send)) -> String {
983    if let Some(s) = panic.downcast_ref::<&str>() {
984        (*s).to_string()
985    } else if let Some(s) = panic.downcast_ref::<String>() {
986        s.clone()
987    } else {
988        "unknown panic".to_string()
989    }
990}
991
992fn extract_progress_token(params: Option<&serde_json::Value>) -> Option<ProgressToken> {
993    params?
994        .get("_meta")?
995        .get("progressToken")
996        .and_then(|v| serde_json::from_value(v.clone()).ok())
997}
998
999#[cfg(test)]
1000mod tests {
1001    use super::*;
1002
1003    use mcpkit_core::capability::ServerInfo;
1004    use mcpkit_core::protocol::RequestId;
1005    use mcpkit_transport::MemoryTransport;
1006    use std::time::Duration;
1007    use tokio::sync::Notify;
1008    use tokio::time::timeout;
1009
1010    /// A minimal router whose `route` can panic, succeed, or 404.
1011    struct PanicRouter;
1012
1013    impl RequestRouter for PanicRouter {
1014        fn server_info(&self) -> ServerInfo {
1015            ServerInfo::new("panic-test", "0.0.0")
1016        }
1017        async fn route(
1018            &self,
1019            method: &str,
1020            _params: Option<&serde_json::Value>,
1021            _ctx: &Context<'_>,
1022        ) -> Result<serde_json::Value, McpError> {
1023            match method {
1024                "panic" => panic!("boom in handler"),
1025                "ok" => Ok(serde_json::json!("ok")),
1026                other => Err(McpError::method_not_found(other)),
1027            }
1028        }
1029    }
1030
1031    /// A router that parks the "blocker" request until released, to prove
1032    /// requests are processed concurrently rather than serially.
1033    struct CoordRouter {
1034        started: Arc<Notify>,
1035        release: Arc<Notify>,
1036    }
1037
1038    impl RequestRouter for CoordRouter {
1039        fn server_info(&self) -> ServerInfo {
1040            ServerInfo::new("coord-test", "0.0.0")
1041        }
1042        async fn route(
1043            &self,
1044            method: &str,
1045            _params: Option<&serde_json::Value>,
1046            _ctx: &Context<'_>,
1047        ) -> Result<serde_json::Value, McpError> {
1048            match method {
1049                "blocker" => {
1050                    self.started.notify_one();
1051                    self.release.notified().await;
1052                    Ok(serde_json::json!("blocked-done"))
1053                }
1054                "fast" => Ok(serde_json::json!("fast-done")),
1055                other => Err(McpError::method_not_found(other)),
1056            }
1057        }
1058    }
1059
1060    fn req(method: &'static str, id: u64) -> Message {
1061        Message::Request(Request::new(method, id))
1062    }
1063
1064    async fn next_response(transport: &MemoryTransport) -> Response {
1065        let msg = timeout(Duration::from_secs(2), transport.recv())
1066            .await
1067            .expect("no response (connection died?)")
1068            .expect("recv ok")
1069            .expect("some message");
1070        match msg {
1071            Message::Response(r) => r,
1072            other => panic!("expected response, got {other:?}"),
1073        }
1074    }
1075
1076    #[tokio::test]
1077    async fn panic_in_handler_returns_internal_error_and_keeps_connection() {
1078        let (client, server) = MemoryTransport::pair();
1079        let state = Arc::new(ServerState::new(ServerCapabilities::default()));
1080        state.set_initialized();
1081        let runtime = ServerRuntime {
1082            server: PanicRouter,
1083            transport: Arc::new(server),
1084            state,
1085            config: RuntimeConfig::default(),
1086        };
1087        let handle = tokio::spawn(async move { runtime.run().await });
1088
1089        // A panicking handler must yield a JSON-RPC error, not kill the loop.
1090        client.send(req("panic", 1)).await.expect("send");
1091        let resp = next_response(&client).await;
1092        assert_eq!(resp.id, RequestId::Number(1));
1093        let err = resp.error.expect("expected error response");
1094        assert!(
1095            err.message.contains("panicked"),
1096            "unexpected error message: {}",
1097            err.message
1098        );
1099
1100        // The connection must still be alive for subsequent requests.
1101        client.send(req("ok", 2)).await.expect("send");
1102        let resp = next_response(&client).await;
1103        assert_eq!(resp.id, RequestId::Number(2));
1104        assert!(
1105            resp.result.is_some(),
1106            "expected success after a prior panic"
1107        );
1108
1109        drop(client);
1110        let _ = timeout(Duration::from_secs(2), handle).await;
1111    }
1112
1113    #[tokio::test]
1114    async fn requests_are_processed_concurrently() {
1115        let (client, server) = MemoryTransport::pair();
1116        let started = Arc::new(Notify::new());
1117        let release = Arc::new(Notify::new());
1118        let state = Arc::new(ServerState::new(ServerCapabilities::default()));
1119        state.set_initialized();
1120        let runtime = ServerRuntime {
1121            server: CoordRouter {
1122                started: started.clone(),
1123                release: release.clone(),
1124            },
1125            transport: Arc::new(server),
1126            state,
1127            config: RuntimeConfig::default(),
1128        };
1129        let handle = tokio::spawn(async move { runtime.run().await });
1130
1131        client.send(req("blocker", 1)).await.expect("send");
1132        client.send(req("fast", 2)).await.expect("send");
1133
1134        // Wait until the blocker is in-flight and parked.
1135        timeout(Duration::from_secs(2), started.notified())
1136            .await
1137            .expect("blocker never started");
1138
1139        // If processing were serial, the parked blocker would prevent the fast
1140        // request from completing. Concurrency means the fast response (id 2)
1141        // arrives while the blocker is still parked.
1142        let resp = next_response(&client).await;
1143        assert_eq!(
1144            resp.id,
1145            RequestId::Number(2),
1146            "fast request should finish first"
1147        );
1148
1149        // Release the blocker; its response should now arrive.
1150        release.notify_one();
1151        let resp = next_response(&client).await;
1152        assert_eq!(resp.id, RequestId::Number(1));
1153
1154        drop(client);
1155        let _ = timeout(Duration::from_secs(2), handle).await;
1156    }
1157
1158    #[tokio::test]
1159    async fn max_concurrent_requests_limits_in_flight() {
1160        // With a limit of 1, a parked blocker must prevent a second request
1161        // from being picked up until the blocker completes.
1162        let (client, server) = MemoryTransport::pair();
1163        let started = Arc::new(Notify::new());
1164        let release = Arc::new(Notify::new());
1165        let state = Arc::new(ServerState::new(ServerCapabilities::default()));
1166        state.set_initialized();
1167        let runtime = ServerRuntime {
1168            server: CoordRouter {
1169                started: started.clone(),
1170                release: release.clone(),
1171            },
1172            transport: Arc::new(server),
1173            state,
1174            config: RuntimeConfig {
1175                auto_initialized: true,
1176                max_concurrent_requests: 1,
1177            },
1178        };
1179        let handle = tokio::spawn(async move { runtime.run().await });
1180
1181        client.send(req("blocker", 1)).await.expect("send");
1182        client.send(req("fast", 2)).await.expect("send");
1183
1184        timeout(Duration::from_secs(2), started.notified())
1185            .await
1186            .expect("blocker never started");
1187
1188        // The fast request must NOT be processed while the blocker holds the
1189        // single slot: no response should arrive yet.
1190        let early = timeout(Duration::from_millis(200), client.recv()).await;
1191        assert!(
1192            early.is_err(),
1193            "fast request was processed despite max_concurrent_requests = 1"
1194        );
1195
1196        // Release the blocker; both responses arrive, blocker first.
1197        release.notify_one();
1198        assert_eq!(next_response(&client).await.id, RequestId::Number(1));
1199        assert_eq!(next_response(&client).await.id, RequestId::Number(2));
1200
1201        drop(client);
1202        let _ = timeout(Duration::from_secs(2), handle).await;
1203    }
1204
1205    #[test]
1206    fn test_server_state_initialization() {
1207        let state = ServerState::new(ServerCapabilities::default());
1208        assert!(!state.is_initialized());
1209
1210        state.set_initialized();
1211        assert!(state.is_initialized());
1212    }
1213
1214    #[test]
1215    fn test_cancellation_management() {
1216        let state = ServerState::new(ServerCapabilities::default());
1217        let token = CancellationToken::new();
1218
1219        state.register_cancellation("req-1", token.clone());
1220        assert!(!token.is_cancelled());
1221
1222        state.cancel_request("req-1");
1223        assert!(token.is_cancelled());
1224
1225        state.remove_cancellation("req-1");
1226    }
1227
1228    #[test]
1229    fn test_runtime_config_default() {
1230        let config = RuntimeConfig::default();
1231        assert!(config.auto_initialized);
1232        assert_eq!(config.max_concurrent_requests, 100);
1233    }
1234
1235    #[test]
1236    fn test_extract_progress_token_string() -> Result<(), Box<dyn std::error::Error>> {
1237        let params = serde_json::json!({
1238            "_meta": {
1239                "progressToken": "my-token-123"
1240            },
1241            "name": "test-tool"
1242        });
1243        let token = extract_progress_token(Some(&params));
1244        assert!(token.is_some());
1245        assert_eq!(
1246            token.ok_or("Token not found")?,
1247            ProgressToken::String("my-token-123".to_string())
1248        );
1249
1250        Ok(())
1251    }
1252
1253    #[test]
1254    fn test_extract_progress_token_number() -> Result<(), Box<dyn std::error::Error>> {
1255        let params = serde_json::json!({
1256            "_meta": {
1257                "progressToken": 42
1258            },
1259            "arguments": {}
1260        });
1261        let token = extract_progress_token(Some(&params));
1262        assert!(token.is_some());
1263        assert_eq!(token.ok_or("Token not found")?, ProgressToken::Number(42));
1264
1265        Ok(())
1266    }
1267
1268    #[test]
1269    fn test_extract_progress_token_missing_meta() {
1270        let params = serde_json::json!({
1271            "name": "test-tool",
1272            "arguments": {}
1273        });
1274        let token = extract_progress_token(Some(&params));
1275        assert!(token.is_none());
1276    }
1277
1278    #[test]
1279    fn test_extract_progress_token_missing_token() {
1280        let params = serde_json::json!({
1281            "_meta": {},
1282            "name": "test-tool"
1283        });
1284        let token = extract_progress_token(Some(&params));
1285        assert!(token.is_none());
1286    }
1287
1288    #[test]
1289    fn test_extract_progress_token_none_params() {
1290        let token = extract_progress_token(None);
1291        assert!(token.is_none());
1292    }
1293}