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}