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}