mcpkit_server/
handler.rs

1//! Composable handler traits for MCP servers.
2//!
3//! This module defines the traits that MCP servers can implement to handle
4//! different protocol capabilities. Unlike monolithic handler approaches,
5//! these traits are composable - implement only what you need.
6//!
7//! # Overview
8//!
9//! - [`ServerHandler`]: Minimal required trait for all servers
10//! - [`ToolHandler`]: Handle tool discovery and execution
11//! - [`ResourceHandler`]: Handle resource discovery and reading
12//! - [`PromptHandler`]: Handle prompt discovery and rendering
13//! - [`TaskHandler`]: Handle long-running task operations
14//! - [`SamplingHandler`]: Handle sampling requests (client-to-server)
15//! - [`ElicitationHandler`]: Handle elicitation requests (client-to-server)
16//!
17//! # Example
18//!
19//! ```rust
20//! use mcpkit_server::{ServerHandler, ServerBuilder};
21//! use mcpkit_core::capability::{ServerInfo, ServerCapabilities};
22//!
23//! struct MyServer;
24//!
25//! impl ServerHandler for MyServer {
26//!     fn server_info(&self) -> ServerInfo {
27//!         ServerInfo::new("my-server", "1.0.0")
28//!     }
29//!
30//!     fn capabilities(&self) -> ServerCapabilities {
31//!         ServerCapabilities::new().with_tools()
32//!     }
33//! }
34//!
35//! let server = ServerBuilder::new(MyServer).build();
36//! assert_eq!(server.server_info().name, "my-server");
37//! ```
38
39use mcpkit_core::capability::{ServerCapabilities, ServerInfo};
40use mcpkit_core::error::McpError;
41use mcpkit_core::types::{
42    GetPromptResult, Prompt, Resource, ResourceContents, Task, TaskId, Tool, ToolOutput,
43    elicitation::{ElicitRequest, ElicitResult},
44    sampling::{CreateMessageRequest, CreateMessageResult},
45};
46use serde_json::Value;
47use std::future::Future;
48
49use crate::context::Context;
50
51/// Core server handler trait - required for all MCP servers.
52///
53/// This trait defines the minimal requirements for an MCP server.
54/// All servers must implement this trait. Additional capabilities
55/// are added by implementing optional handler traits.
56///
57/// Note: Context uses lifetime references (no `'static` requirement).
58pub trait ServerHandler: Send + Sync {
59    /// Return information about this server.
60    ///
61    /// This is called during the initialization handshake.
62    fn server_info(&self) -> ServerInfo;
63
64    /// Return the capabilities of this server.
65    ///
66    /// The default implementation returns empty capabilities.
67    /// Override this to advertise specific capabilities.
68    fn capabilities(&self) -> ServerCapabilities {
69        ServerCapabilities::default()
70    }
71
72    /// Return optional instructions for using this server.
73    ///
74    /// These instructions are sent to the client during initialization
75    /// and can help the AI assistant understand how to use this server.
76    fn instructions(&self) -> Option<String> {
77        None
78    }
79
80    /// Called after initialization is complete.
81    ///
82    /// This is a good place to set up any state that requires
83    /// the connection to be established.
84    fn on_initialized(&self, _ctx: &Context<'_>) -> impl Future<Output = ()> + Send {
85        async {}
86    }
87
88    /// Called when the connection is about to be closed.
89    fn on_shutdown(&self) -> impl Future<Output = ()> + Send {
90        async {}
91    }
92}
93
94/// Handler for tool-related operations.
95///
96/// Implement this trait to expose tools that AI assistants can call.
97pub trait ToolHandler: Send + Sync {
98    /// List all available tools.
99    ///
100    /// This is called when the client requests the tool list.
101    fn list_tools(
102        &self,
103        ctx: &Context<'_>,
104    ) -> impl Future<Output = Result<Vec<Tool>, McpError>> + Send;
105
106    /// Call a tool with the given arguments.
107    ///
108    /// # Arguments
109    ///
110    /// * `name` - The name of the tool to call
111    /// * `args` - The arguments as a JSON value
112    /// * `ctx` - The request context
113    fn call_tool(
114        &self,
115        name: &str,
116        args: Value,
117        ctx: &Context<'_>,
118    ) -> impl Future<Output = Result<ToolOutput, McpError>> + Send;
119
120    /// Called when a tool's definition has changed.
121    ///
122    /// Override this to dynamically add/remove/update tools.
123    fn on_tools_changed(&self) -> impl Future<Output = ()> + Send {
124        async {}
125    }
126}
127
128/// Handler for resource-related operations.
129///
130/// Implement this trait to expose resources that AI assistants can read.
131pub trait ResourceHandler: Send + Sync {
132    /// List all available resources.
133    fn list_resources(
134        &self,
135        ctx: &Context<'_>,
136    ) -> impl Future<Output = Result<Vec<Resource>, McpError>> + Send;
137
138    /// Read a resource by URI.
139    fn read_resource(
140        &self,
141        uri: &str,
142        ctx: &Context<'_>,
143    ) -> impl Future<Output = Result<Vec<ResourceContents>, McpError>> + Send;
144
145    /// Subscribe to resource updates.
146    ///
147    /// Returns true if the subscription was successful.
148    fn subscribe(
149        &self,
150        _uri: &str,
151        _ctx: &Context<'_>,
152    ) -> impl Future<Output = Result<bool, McpError>> + Send {
153        async { Ok(false) }
154    }
155
156    /// Unsubscribe from resource updates.
157    fn unsubscribe(
158        &self,
159        _uri: &str,
160        _ctx: &Context<'_>,
161    ) -> impl Future<Output = Result<bool, McpError>> + Send {
162        async { Ok(false) }
163    }
164}
165
166/// Handler for prompt-related operations.
167///
168/// Implement this trait to expose prompts that AI assistants can use.
169pub trait PromptHandler: Send + Sync {
170    /// List all available prompts.
171    fn list_prompts(
172        &self,
173        ctx: &Context<'_>,
174    ) -> impl Future<Output = Result<Vec<Prompt>, McpError>> + Send;
175
176    /// Get a prompt with the given arguments.
177    fn get_prompt(
178        &self,
179        name: &str,
180        args: Option<serde_json::Map<String, Value>>,
181        ctx: &Context<'_>,
182    ) -> impl Future<Output = Result<GetPromptResult, McpError>> + Send;
183}
184
185/// Handler for task-related operations.
186///
187/// Implement this trait to support long-running operations that can be
188/// tracked, monitored, and cancelled.
189pub trait TaskHandler: Send + Sync {
190    /// List all tasks, optionally filtered by status.
191    fn list_tasks(
192        &self,
193        ctx: &Context<'_>,
194    ) -> impl Future<Output = Result<Vec<Task>, McpError>> + Send;
195
196    /// Get the current state of a task.
197    fn get_task(
198        &self,
199        id: &TaskId,
200        ctx: &Context<'_>,
201    ) -> impl Future<Output = Result<Option<Task>, McpError>> + Send;
202
203    /// Cancel a running task.
204    fn cancel_task(
205        &self,
206        id: &TaskId,
207        ctx: &Context<'_>,
208    ) -> impl Future<Output = Result<bool, McpError>> + Send;
209}
210
211/// Handler for completion suggestions.
212///
213/// Implement this trait to provide autocomplete suggestions for
214/// resource URIs, prompt arguments, etc.
215pub trait CompletionHandler: Send + Sync {
216    /// Complete a partial resource URI.
217    fn complete_resource(
218        &self,
219        partial_uri: &str,
220        ctx: &Context<'_>,
221    ) -> impl Future<Output = Result<Vec<String>, McpError>> + Send;
222
223    /// Complete a partial prompt argument.
224    fn complete_prompt_arg(
225        &self,
226        prompt_name: &str,
227        arg_name: &str,
228        partial_value: &str,
229        ctx: &Context<'_>,
230    ) -> impl Future<Output = Result<Vec<String>, McpError>> + Send;
231}
232
233/// Handler for logging operations.
234///
235/// Implement this trait to handle log messages from the client.
236pub trait LoggingHandler: Send + Sync {
237    /// Set the current logging level.
238    fn set_level(&self, level: LogLevel) -> impl Future<Output = Result<(), McpError>> + Send;
239}
240
241/// Log levels for MCP logging.
242#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
243pub enum LogLevel {
244    /// Debug level - most verbose.
245    Debug,
246    /// Info level.
247    #[default]
248    Info,
249    /// Notice level.
250    Notice,
251    /// Warning level.
252    Warning,
253    /// Error level.
254    Error,
255    /// Critical level.
256    Critical,
257    /// Alert level.
258    Alert,
259    /// Emergency level - most severe.
260    Emergency,
261}
262
263impl std::fmt::Display for LogLevel {
264    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265        match self {
266            Self::Debug => write!(f, "debug"),
267            Self::Info => write!(f, "info"),
268            Self::Notice => write!(f, "notice"),
269            Self::Warning => write!(f, "warning"),
270            Self::Error => write!(f, "error"),
271            Self::Critical => write!(f, "critical"),
272            Self::Alert => write!(f, "alert"),
273            Self::Emergency => write!(f, "emergency"),
274        }
275    }
276}
277
278/// Handler for sampling requests (server-initiated LLM calls).
279///
280/// Implement this trait to allow the server to request LLM completions
281/// from the client. This enables agentic workflows where servers
282/// can leverage the client's AI capabilities.
283///
284/// # Example
285///
286/// ```ignore
287/// use mcpkit_server::{SamplingHandler, Context};
288/// use mcpkit_core::types::sampling::{CreateMessageRequest, CreateMessageResult};
289/// use mcpkit_core::error::McpError;
290///
291/// struct MyServer;
292///
293/// impl SamplingHandler for MyServer {
294///     async fn create_message(
295///         &self,
296///         request: CreateMessageRequest,
297///         ctx: &Context<'_>,
298///     ) -> Result<CreateMessageResult, McpError> {
299///         // The client will handle this request and invoke the LLM
300///         ctx.peer().create_message(request).await
301///     }
302/// }
303/// ```
304///
305/// # Note
306///
307/// Sampling is a client-side capability. The server sends a sampling request
308/// to the client, which then invokes the LLM and returns the result. The
309/// handler implementation typically just forwards the request through the
310/// peer interface.
311pub trait SamplingHandler: Send + Sync {
312    /// Request the client to create an LLM message.
313    ///
314    /// This allows the server to leverage the client's AI capabilities
315    /// for generating completions, answering questions, or performing
316    /// other LLM-based operations.
317    ///
318    /// # Arguments
319    ///
320    /// * `request` - The sampling request specifying messages, model preferences, etc.
321    /// * `ctx` - The request context providing access to the peer connection.
322    ///
323    /// # Returns
324    ///
325    /// The result of the LLM completion, including the generated content
326    /// and metadata about the completion.
327    fn create_message(
328        &self,
329        request: CreateMessageRequest,
330        ctx: &Context<'_>,
331    ) -> impl Future<Output = Result<CreateMessageResult, McpError>> + Send;
332}
333
334/// Handler for elicitation requests (structured user input).
335///
336/// Implement this trait to allow the server to request structured input
337/// from the user through the client. This enables interactive workflows
338/// where servers can gather user preferences, confirmations, or data.
339///
340/// # Example
341///
342/// ```ignore
343/// use mcpkit_server::{ElicitationHandler, Context};
344/// use mcpkit_core::types::elicitation::{ElicitRequest, ElicitResult};
345/// use mcpkit_core::error::McpError;
346///
347/// struct MyServer;
348///
349/// impl ElicitationHandler for MyServer {
350///     async fn elicit(
351///         &self,
352///         request: ElicitRequest,
353///         ctx: &Context<'_>,
354///     ) -> Result<ElicitResult, McpError> {
355///         // The client will display the request to the user
356///         ctx.peer().elicit(request).await
357///     }
358/// }
359/// ```
360///
361/// # Use Cases
362///
363/// - Requesting user confirmation before destructive operations
364/// - Gathering user preferences or configuration
365/// - Prompting for credentials or sensitive information
366/// - Interactive wizards or multi-step forms
367pub trait ElicitationHandler: Send + Sync {
368    /// Request structured input from the user.
369    ///
370    /// This allows the server to gather information from the user through
371    /// the client interface. The client will present the request to the
372    /// user according to the specified schema and return their response.
373    ///
374    /// # Arguments
375    ///
376    /// * `request` - The elicitation request specifying the message and schema.
377    /// * `ctx` - The request context providing access to the peer connection.
378    ///
379    /// # Returns
380    ///
381    /// The result of the elicitation, including the user's action (accept,
382    /// decline, or cancel) and any provided content.
383    fn elicit(
384        &self,
385        request: ElicitRequest,
386        ctx: &Context<'_>,
387    ) -> impl Future<Output = Result<ElicitResult, McpError>> + Send;
388}
389
390// =============================================================================
391// Blanket implementations for Arc<T>
392//
393// These allow sharing a single handler instance across multiple registrations
394// without requiring Clone on the user's type. The macro-generated `into_server()`
395// method uses Arc internally to wire everything up automatically.
396// =============================================================================
397
398use std::sync::Arc;
399
400impl<T: ServerHandler> ServerHandler for Arc<T> {
401    fn server_info(&self) -> ServerInfo {
402        (**self).server_info()
403    }
404
405    fn capabilities(&self) -> ServerCapabilities {
406        (**self).capabilities()
407    }
408
409    fn instructions(&self) -> Option<String> {
410        (**self).instructions()
411    }
412
413    fn on_initialized(&self, ctx: &Context<'_>) -> impl Future<Output = ()> + Send {
414        (**self).on_initialized(ctx)
415    }
416
417    fn on_shutdown(&self) -> impl Future<Output = ()> + Send {
418        (**self).on_shutdown()
419    }
420}
421
422impl<T: ToolHandler> ToolHandler for Arc<T> {
423    fn list_tools(
424        &self,
425        ctx: &Context<'_>,
426    ) -> impl Future<Output = Result<Vec<Tool>, McpError>> + Send {
427        (**self).list_tools(ctx)
428    }
429
430    fn call_tool(
431        &self,
432        name: &str,
433        args: Value,
434        ctx: &Context<'_>,
435    ) -> impl Future<Output = Result<ToolOutput, McpError>> + Send {
436        (**self).call_tool(name, args, ctx)
437    }
438
439    fn on_tools_changed(&self) -> impl Future<Output = ()> + Send {
440        (**self).on_tools_changed()
441    }
442}
443
444impl<T: ResourceHandler> ResourceHandler for Arc<T> {
445    fn list_resources(
446        &self,
447        ctx: &Context<'_>,
448    ) -> impl Future<Output = Result<Vec<Resource>, McpError>> + Send {
449        (**self).list_resources(ctx)
450    }
451
452    fn read_resource(
453        &self,
454        uri: &str,
455        ctx: &Context<'_>,
456    ) -> impl Future<Output = Result<Vec<ResourceContents>, McpError>> + Send {
457        (**self).read_resource(uri, ctx)
458    }
459
460    fn subscribe(
461        &self,
462        uri: &str,
463        ctx: &Context<'_>,
464    ) -> impl Future<Output = Result<bool, McpError>> + Send {
465        (**self).subscribe(uri, ctx)
466    }
467
468    fn unsubscribe(
469        &self,
470        uri: &str,
471        ctx: &Context<'_>,
472    ) -> impl Future<Output = Result<bool, McpError>> + Send {
473        (**self).unsubscribe(uri, ctx)
474    }
475}
476
477impl<T: PromptHandler> PromptHandler for Arc<T> {
478    fn list_prompts(
479        &self,
480        ctx: &Context<'_>,
481    ) -> impl Future<Output = Result<Vec<Prompt>, McpError>> + Send {
482        (**self).list_prompts(ctx)
483    }
484
485    fn get_prompt(
486        &self,
487        name: &str,
488        args: Option<serde_json::Map<String, Value>>,
489        ctx: &Context<'_>,
490    ) -> impl Future<Output = Result<GetPromptResult, McpError>> + Send {
491        (**self).get_prompt(name, args, ctx)
492    }
493}
494
495impl<T: TaskHandler> TaskHandler for Arc<T> {
496    fn list_tasks(
497        &self,
498        ctx: &Context<'_>,
499    ) -> impl Future<Output = Result<Vec<Task>, McpError>> + Send {
500        (**self).list_tasks(ctx)
501    }
502
503    fn get_task(
504        &self,
505        id: &TaskId,
506        ctx: &Context<'_>,
507    ) -> impl Future<Output = Result<Option<Task>, McpError>> + Send {
508        (**self).get_task(id, ctx)
509    }
510
511    fn cancel_task(
512        &self,
513        id: &TaskId,
514        ctx: &Context<'_>,
515    ) -> impl Future<Output = Result<bool, McpError>> + Send {
516        (**self).cancel_task(id, ctx)
517    }
518}
519
520impl<T: CompletionHandler> CompletionHandler for Arc<T> {
521    fn complete_resource(
522        &self,
523        partial_uri: &str,
524        ctx: &Context<'_>,
525    ) -> impl Future<Output = Result<Vec<String>, McpError>> + Send {
526        (**self).complete_resource(partial_uri, ctx)
527    }
528
529    fn complete_prompt_arg(
530        &self,
531        prompt_name: &str,
532        arg_name: &str,
533        partial_value: &str,
534        ctx: &Context<'_>,
535    ) -> impl Future<Output = Result<Vec<String>, McpError>> + Send {
536        (**self).complete_prompt_arg(prompt_name, arg_name, partial_value, ctx)
537    }
538}
539
540impl<T: LoggingHandler> LoggingHandler for Arc<T> {
541    fn set_level(&self, level: LogLevel) -> impl Future<Output = Result<(), McpError>> + Send {
542        (**self).set_level(level)
543    }
544}
545
546impl<T: SamplingHandler> SamplingHandler for Arc<T> {
547    fn create_message(
548        &self,
549        request: CreateMessageRequest,
550        ctx: &Context<'_>,
551    ) -> impl Future<Output = Result<CreateMessageResult, McpError>> + Send {
552        (**self).create_message(request, ctx)
553    }
554}
555
556impl<T: ElicitationHandler> ElicitationHandler for Arc<T> {
557    fn elicit(
558        &self,
559        request: ElicitRequest,
560        ctx: &Context<'_>,
561    ) -> impl Future<Output = Result<ElicitResult, McpError>> + Send {
562        (**self).elicit(request, ctx)
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569
570    struct TestServer;
571
572    impl ServerHandler for TestServer {
573        fn server_info(&self) -> ServerInfo {
574            ServerInfo::new("test", "1.0.0")
575        }
576    }
577
578    #[test]
579    fn test_server_handler() {
580        let server = TestServer;
581        let info = server.server_info();
582        assert_eq!(info.name, "test");
583        assert_eq!(info.version, "1.0.0");
584    }
585
586    #[test]
587    fn test_log_level_ordering() {
588        assert!(LogLevel::Debug < LogLevel::Error);
589        assert!(LogLevel::Info < LogLevel::Warning);
590        assert!(LogLevel::Emergency > LogLevel::Alert);
591    }
592
593    #[test]
594    fn test_arc_server_handler() {
595        let server = Arc::new(TestServer);
596        let info = server.server_info();
597        assert_eq!(info.name, "test");
598        assert_eq!(info.version, "1.0.0");
599    }
600}