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