mcpkit_server/
builder.rs

1//! Fluent server builder for MCP servers.
2//!
3//! The builder uses the typestate pattern to track registered capabilities
4//! at the type level, ensuring compile-time verification of server configuration.
5//!
6//! # Type Parameters
7//!
8//! - `H`: The base server handler
9//! - `Tools`: Tool handler state (`()` = not registered, `TH: ToolHandler` = registered)
10//! - `Resources`: Resource handler state
11//! - `Prompts`: Prompt handler state
12//! - `Tasks`: Task handler state
13//!
14//! # Example
15//!
16//! ```rust
17//! use mcpkit_server::{ServerBuilder, ServerHandler};
18//! use mcpkit_core::capability::{ServerInfo, ServerCapabilities};
19//!
20//! struct MyHandler;
21//!
22//! impl ServerHandler for MyHandler {
23//!     fn server_info(&self) -> ServerInfo {
24//!         ServerInfo::new("my-server", "1.0.0")
25//!     }
26//! }
27//!
28//! let server = ServerBuilder::new(MyHandler).build();
29//! assert_eq!(server.server_info().name, "my-server");
30//! ```
31//!
32//! # Type-Level Capability Tracking
33//!
34//! The builder tracks which handlers have been registered at the type level.
35//! This means you can't accidentally call a method that requires a handler
36//! that hasn't been registered - the compiler will catch it.
37//!
38//! ```rust
39//! use mcpkit_server::{ServerBuilder, ServerHandler, ToolHandler, Context};
40//! use mcpkit_core::capability::{ServerInfo, ServerCapabilities};
41//! use mcpkit_core::types::{Tool, ToolOutput};
42//! use mcpkit_core::error::McpError;
43//! use serde_json::Value;
44//!
45//! struct MyHandler;
46//! impl ServerHandler for MyHandler {
47//!     fn server_info(&self) -> ServerInfo {
48//!         ServerInfo::new("test", "1.0.0")
49//!     }
50//! }
51//!
52//! struct MyToolHandler;
53//! impl ToolHandler for MyToolHandler {
54//!     async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
55//!         Ok(vec![])
56//!     }
57//!     async fn call_tool(&self, _name: &str, _args: Value, _ctx: &Context<'_>) -> Result<ToolOutput, McpError> {
58//!         Ok(ToolOutput::text("done"))
59//!     }
60//! }
61//!
62//! // Tools are registered - this compiles
63//! let server = ServerBuilder::new(MyHandler)
64//!     .with_tools(MyToolHandler)
65//!     .build();
66//!
67//! assert!(server.capabilities().has_tools());
68//! ```
69
70use crate::handler::{
71    PromptHandler, ResourceHandler, ServerHandler, TaskHandler, ToolHandler,
72};
73use mcpkit_core::capability::ServerCapabilities;
74
75/// Marker type indicating no handler is registered for a capability.
76#[derive(Debug, Clone, Copy, Default)]
77pub struct NotRegistered;
78
79/// Marker type indicating a handler is registered for a capability.
80#[derive(Debug)]
81pub struct Registered<T>(pub T);
82
83/// Builder for constructing MCP servers with specific capabilities.
84///
85/// Uses the typestate pattern with 5 type parameters to track registered
86/// handlers at compile time:
87///
88/// - `H`: Base server handler (always required)
89/// - `Tools`: Tool handler state
90/// - `Resources`: Resource handler state
91/// - `Prompts`: Prompt handler state
92/// - `Tasks`: Task handler state
93///
94/// When a capability is not registered, its type parameter is `NotRegistered`.
95/// When registered, it becomes `Registered<T>` where `T` is the handler type.
96pub struct ServerBuilder<H, Tools, Resources, Prompts, Tasks> {
97    handler: H,
98    tools: Tools,
99    resources: Resources,
100    prompts: Prompts,
101    tasks: Tasks,
102    capabilities: ServerCapabilities,
103}
104
105// Initial builder with no handlers registered
106impl<H: ServerHandler> ServerBuilder<H, NotRegistered, NotRegistered, NotRegistered, NotRegistered> {
107    /// Create a new server builder with the given base handler.
108    ///
109    /// The base handler must implement `ServerHandler` and provides
110    /// the core server identity and configuration.
111    #[must_use]
112    pub fn new(handler: H) -> Self {
113        let capabilities = handler.capabilities();
114        Self {
115            handler,
116            tools: NotRegistered,
117            resources: NotRegistered,
118            prompts: NotRegistered,
119            tasks: NotRegistered,
120            capabilities,
121        }
122    }
123}
124
125// Methods available regardless of which handlers are registered
126impl<H, T, R, P, K> ServerBuilder<H, T, R, P, K>
127where
128    H: ServerHandler,
129{
130    /// Override the capabilities advertised by this server.
131    ///
132    /// By default, capabilities are derived from the base handler.
133    /// Use this to customize or extend those capabilities.
134    #[must_use]
135    pub fn capabilities(mut self, caps: ServerCapabilities) -> Self {
136        self.capabilities = caps;
137        self
138    }
139
140    /// Get a reference to the current capabilities.
141    #[must_use]
142    pub fn get_capabilities(&self) -> &ServerCapabilities {
143        &self.capabilities
144    }
145}
146
147// Tool handler registration (only when tools are not yet registered)
148impl<H, R, P, K> ServerBuilder<H, NotRegistered, R, P, K>
149where
150    H: ServerHandler,
151{
152    /// Register a tool handler.
153    ///
154    /// This method is only available when no tool handler has been registered yet.
155    /// Attempting to register tools twice will result in a compile error.
156    #[must_use]
157    pub fn with_tools<TH: ToolHandler>(self, tools: TH) -> ServerBuilder<H, Registered<TH>, R, P, K> {
158        ServerBuilder {
159            handler: self.handler,
160            tools: Registered(tools),
161            resources: self.resources,
162            prompts: self.prompts,
163            tasks: self.tasks,
164            capabilities: self.capabilities.with_tools(),
165        }
166    }
167}
168
169// Resource handler registration (only when resources are not yet registered)
170impl<H, T, P, K> ServerBuilder<H, T, NotRegistered, P, K>
171where
172    H: ServerHandler,
173{
174    /// Register a resource handler.
175    ///
176    /// This method is only available when no resource handler has been registered yet.
177    #[must_use]
178    pub fn with_resources<RH: ResourceHandler>(self, resources: RH) -> ServerBuilder<H, T, Registered<RH>, P, K> {
179        ServerBuilder {
180            handler: self.handler,
181            tools: self.tools,
182            resources: Registered(resources),
183            prompts: self.prompts,
184            tasks: self.tasks,
185            capabilities: self.capabilities.with_resources(),
186        }
187    }
188}
189
190// Prompt handler registration (only when prompts are not yet registered)
191impl<H, T, R, K> ServerBuilder<H, T, R, NotRegistered, K>
192where
193    H: ServerHandler,
194{
195    /// Register a prompt handler.
196    ///
197    /// This method is only available when no prompt handler has been registered yet.
198    #[must_use]
199    pub fn with_prompts<PH: PromptHandler>(self, prompts: PH) -> ServerBuilder<H, T, R, Registered<PH>, K> {
200        ServerBuilder {
201            handler: self.handler,
202            tools: self.tools,
203            resources: self.resources,
204            prompts: Registered(prompts),
205            tasks: self.tasks,
206            capabilities: self.capabilities.with_prompts(),
207        }
208    }
209}
210
211// Task handler registration (only when tasks are not yet registered)
212impl<H, T, R, P> ServerBuilder<H, T, R, P, NotRegistered>
213where
214    H: ServerHandler,
215{
216    /// Register a task handler.
217    ///
218    /// Tasks are long-running operations that can be tracked, monitored,
219    /// and cancelled. This is a key differentiator from rmcp which lacks
220    /// task support.
221    ///
222    /// This method is only available when no task handler has been registered yet.
223    #[must_use]
224    pub fn with_tasks<KH: TaskHandler>(self, tasks: KH) -> ServerBuilder<H, T, R, P, Registered<KH>> {
225        ServerBuilder {
226            handler: self.handler,
227            tools: self.tools,
228            resources: self.resources,
229            prompts: self.prompts,
230            tasks: Registered(tasks),
231            // Note: capabilities.with_tasks() would be added when supported
232            capabilities: self.capabilities,
233        }
234    }
235}
236
237// Build method - available for any combination of handlers
238impl<H, T, R, P, K> ServerBuilder<H, T, R, P, K>
239where
240    H: ServerHandler + Send + Sync + 'static,
241    T: Send + Sync + 'static,
242    R: Send + Sync + 'static,
243    P: Send + Sync + 'static,
244    K: Send + Sync + 'static,
245{
246    /// Build the server.
247    ///
248    /// Returns a `Server` configured with the registered handlers and capabilities.
249    #[must_use]
250    pub fn build(self) -> Server<H, T, R, P, K> {
251        Server {
252            handler: self.handler,
253            tools: self.tools,
254            resources: self.resources,
255            prompts: self.prompts,
256            tasks: self.tasks,
257            capabilities: self.capabilities,
258        }
259    }
260}
261
262/// A configured MCP server ready to serve requests.
263///
264/// The type parameters track which capabilities are available:
265/// - `H`: Base server handler
266/// - `T`: Tool handler (`NotRegistered` or `Registered<TH>`)
267/// - `R`: Resource handler
268/// - `P`: Prompt handler
269/// - `K`: Task handler
270pub struct Server<H, T, R, P, K> {
271    handler: H,
272    tools: T,
273    resources: R,
274    prompts: P,
275    tasks: K,
276    capabilities: ServerCapabilities,
277}
278
279impl<H, T, R, P, K> Server<H, T, R, P, K>
280where
281    H: ServerHandler,
282{
283    /// Get the server's capabilities.
284    #[must_use]
285    pub fn capabilities(&self) -> &ServerCapabilities {
286        &self.capabilities
287    }
288
289    /// Get a reference to the base handler.
290    #[must_use]
291    pub fn handler(&self) -> &H {
292        &self.handler
293    }
294
295    /// Get the server info from the base handler.
296    #[must_use]
297    pub fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
298        self.handler.server_info()
299    }
300}
301
302// Methods when tools are registered
303impl<H, TH, R, P, K> Server<H, Registered<TH>, R, P, K>
304where
305    H: ServerHandler,
306    TH: ToolHandler,
307{
308    /// Get a reference to the tool handler.
309    #[must_use]
310    pub fn tool_handler(&self) -> &TH {
311        &self.tools.0
312    }
313}
314
315// Methods when resources are registered
316impl<H, T, RH, P, K> Server<H, T, Registered<RH>, P, K>
317where
318    H: ServerHandler,
319    RH: ResourceHandler,
320{
321    /// Get a reference to the resource handler.
322    #[must_use]
323    pub fn resource_handler(&self) -> &RH {
324        &self.resources.0
325    }
326}
327
328// Methods when prompts are registered
329impl<H, T, R, PH, K> Server<H, T, R, Registered<PH>, K>
330where
331    H: ServerHandler,
332    PH: PromptHandler,
333{
334    /// Get a reference to the prompt handler.
335    #[must_use]
336    pub fn prompt_handler(&self) -> &PH {
337        &self.prompts.0
338    }
339}
340
341// Methods when tasks are registered
342impl<H, T, R, P, KH> Server<H, T, R, P, Registered<KH>>
343where
344    H: ServerHandler,
345    KH: TaskHandler,
346{
347    /// Get a reference to the task handler.
348    #[must_use]
349    pub fn task_handler(&self) -> &KH {
350        &self.tasks.0
351    }
352}
353
354/// Type alias for a fully-configured server with all handlers.
355pub type FullServer<H, TH, RH, PH, KH> = Server<H, Registered<TH>, Registered<RH>, Registered<PH>, Registered<KH>>;
356
357/// Type alias for a minimal server with no optional handlers.
358pub type MinimalServer<H> = Server<H, NotRegistered, NotRegistered, NotRegistered, NotRegistered>;
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363    use mcpkit_core::capability::ServerInfo;
364    use crate::handler::ToolHandler;
365    use crate::context::Context;
366    use mcpkit_core::error::McpError;
367    use mcpkit_core::types::{Tool, ToolOutput};
368    use serde_json::Value;
369
370    struct TestHandler;
371
372    impl ServerHandler for TestHandler {
373        fn server_info(&self) -> ServerInfo {
374            ServerInfo::new("test", "1.0.0")
375        }
376
377        fn capabilities(&self) -> ServerCapabilities {
378            ServerCapabilities::default()
379        }
380    }
381
382    struct TestToolHandler;
383
384    impl ToolHandler for TestToolHandler {
385        async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
386            Ok(vec![])
387        }
388
389        async fn call_tool(&self, _name: &str, _args: Value, _ctx: &Context<'_>) -> Result<ToolOutput, McpError> {
390            Ok(ToolOutput::text("test"))
391        }
392    }
393
394    #[test]
395    fn test_server_builder_minimal() {
396        let server = ServerBuilder::new(TestHandler).build();
397
398        assert_eq!(server.server_info().name, "test");
399        assert_eq!(server.server_info().version, "1.0.0");
400    }
401
402    #[test]
403    fn test_server_builder_with_tools() {
404        let server = ServerBuilder::new(TestHandler)
405            .with_tools(TestToolHandler)
406            .build();
407
408        assert!(server.capabilities().has_tools());
409        // This compiles because tools are registered
410        let _tool_handler: &TestToolHandler = server.tool_handler();
411    }
412
413    #[test]
414    fn test_typestate_prevents_double_registration() {
415        // This test verifies the design - double registration would be
416        // a compile error, not a runtime error
417        let _server = ServerBuilder::new(TestHandler)
418            .with_tools(TestToolHandler)
419            // .with_tools(TestToolHandler) // This would NOT compile!
420            .build();
421    }
422
423    #[test]
424    fn test_builder_order_independence() {
425        // Handlers can be registered in any order
426        let server1 = ServerBuilder::new(TestHandler)
427            .with_tools(TestToolHandler)
428            .build();
429
430        // Different order, same result
431        let _server2: Server<TestHandler, Registered<TestToolHandler>, NotRegistered, NotRegistered, NotRegistered> =
432            ServerBuilder::new(TestHandler)
433                .with_tools(TestToolHandler)
434                .build();
435
436        assert!(server1.capabilities().has_tools());
437    }
438}