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 elicitation::{ElicitRequest, ElicitResult},
43 sampling::{CreateMessageRequest, CreateMessageResult},
44 GetPromptResult, Prompt, Resource, ResourceContents, Task, TaskId, Tool, ToolOutput,
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(&self, ctx: &Context<'_>) -> impl Future<Output = Result<Vec<Tool>, McpError>> + Send;
102
103 /// Call a tool with the given arguments.
104 ///
105 /// # Arguments
106 ///
107 /// * `name` - The name of the tool to call
108 /// * `args` - The arguments as a JSON value
109 /// * `ctx` - The request context
110 fn call_tool(
111 &self,
112 name: &str,
113 args: Value,
114 ctx: &Context<'_>,
115 ) -> impl Future<Output = Result<ToolOutput, McpError>> + Send;
116
117 /// Called when a tool's definition has changed.
118 ///
119 /// Override this to dynamically add/remove/update tools.
120 fn on_tools_changed(&self) -> impl Future<Output = ()> + Send {
121 async {}
122 }
123}
124
125/// Handler for resource-related operations.
126///
127/// Implement this trait to expose resources that AI assistants can read.
128pub trait ResourceHandler: Send + Sync {
129 /// List all available resources.
130 fn list_resources(
131 &self,
132 ctx: &Context<'_>,
133 ) -> impl Future<Output = Result<Vec<Resource>, McpError>> + Send;
134
135 /// Read a resource by URI.
136 fn read_resource(
137 &self,
138 uri: &str,
139 ctx: &Context<'_>,
140 ) -> impl Future<Output = Result<Vec<ResourceContents>, McpError>> + Send;
141
142 /// Subscribe to resource updates.
143 ///
144 /// Returns true if the subscription was successful.
145 fn subscribe(
146 &self,
147 _uri: &str,
148 _ctx: &Context<'_>,
149 ) -> impl Future<Output = Result<bool, McpError>> + Send {
150 async { Ok(false) }
151 }
152
153 /// Unsubscribe from resource updates.
154 fn unsubscribe(
155 &self,
156 _uri: &str,
157 _ctx: &Context<'_>,
158 ) -> impl Future<Output = Result<bool, McpError>> + Send {
159 async { Ok(false) }
160 }
161}
162
163/// Handler for prompt-related operations.
164///
165/// Implement this trait to expose prompts that AI assistants can use.
166pub trait PromptHandler: Send + Sync {
167 /// List all available prompts.
168 fn list_prompts(
169 &self,
170 ctx: &Context<'_>,
171 ) -> impl Future<Output = Result<Vec<Prompt>, McpError>> + Send;
172
173 /// Get a prompt with the given arguments.
174 fn get_prompt(
175 &self,
176 name: &str,
177 args: Option<serde_json::Map<String, Value>>,
178 ctx: &Context<'_>,
179 ) -> impl Future<Output = Result<GetPromptResult, McpError>> + Send;
180}
181
182/// Handler for task-related operations.
183///
184/// Implement this trait to support long-running operations that can be
185/// tracked, monitored, and cancelled. This is a key differentiator from
186/// rmcp which lacks task support.
187pub trait TaskHandler: Send + Sync {
188 /// List all tasks, optionally filtered by status.
189 fn list_tasks(&self, ctx: &Context<'_>) -> impl Future<Output = Result<Vec<Task>, McpError>> + Send;
190
191 /// Get the current state of a task.
192 fn get_task(
193 &self,
194 id: &TaskId,
195 ctx: &Context<'_>,
196 ) -> impl Future<Output = Result<Option<Task>, McpError>> + Send;
197
198 /// Cancel a running task.
199 fn cancel_task(
200 &self,
201 id: &TaskId,
202 ctx: &Context<'_>,
203 ) -> impl Future<Output = Result<bool, McpError>> + Send;
204}
205
206/// Handler for completion suggestions.
207///
208/// Implement this trait to provide autocomplete suggestions for
209/// resource URIs, prompt arguments, etc.
210pub trait CompletionHandler: Send + Sync {
211 /// Complete a partial resource URI.
212 fn complete_resource(
213 &self,
214 partial_uri: &str,
215 ctx: &Context<'_>,
216 ) -> impl Future<Output = Result<Vec<String>, McpError>> + Send;
217
218 /// Complete a partial prompt argument.
219 fn complete_prompt_arg(
220 &self,
221 prompt_name: &str,
222 arg_name: &str,
223 partial_value: &str,
224 ctx: &Context<'_>,
225 ) -> impl Future<Output = Result<Vec<String>, McpError>> + Send;
226}
227
228/// Handler for logging operations.
229///
230/// Implement this trait to handle log messages from the client.
231pub trait LoggingHandler: Send + Sync {
232 /// Set the current logging level.
233 fn set_level(&self, level: LogLevel) -> impl Future<Output = Result<(), McpError>> + Send;
234}
235
236/// Log levels for MCP logging.
237#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
238pub enum LogLevel {
239 /// Debug level - most verbose.
240 Debug,
241 /// Info level.
242 Info,
243 /// Notice level.
244 Notice,
245 /// Warning level.
246 Warning,
247 /// Error level.
248 Error,
249 /// Critical level.
250 Critical,
251 /// Alert level.
252 Alert,
253 /// Emergency level - most severe.
254 Emergency,
255}
256
257impl Default for LogLevel {
258 fn default() -> Self {
259 Self::Info
260 }
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 powerful 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#[cfg(test)]
391mod tests {
392 use super::*;
393
394 struct TestServer;
395
396 impl ServerHandler for TestServer {
397 fn server_info(&self) -> ServerInfo {
398 ServerInfo::new("test", "1.0.0")
399 }
400 }
401
402 #[test]
403 fn test_server_handler() {
404 let server = TestServer;
405 let info = server.server_info();
406 assert_eq!(info.name, "test");
407 assert_eq!(info.version, "1.0.0");
408 }
409
410 #[test]
411 fn test_log_level_ordering() {
412 assert!(LogLevel::Debug < LogLevel::Error);
413 assert!(LogLevel::Info < LogLevel::Warning);
414 assert!(LogLevel::Emergency > LogLevel::Alert);
415 }
416}