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::{PromptHandler, ResourceHandler, ServerHandler, TaskHandler, ToolHandler};
71use mcpkit_core::capability::ServerCapabilities;
72
73/// Marker type indicating no handler is registered for a capability.
74#[derive(Debug, Clone, Copy, Default)]
75pub struct NotRegistered;
76
77/// Marker type indicating a handler is registered for a capability.
78#[derive(Debug)]
79pub struct Registered<T>(pub T);
80
81/// Builder for constructing MCP servers with specific capabilities.
82///
83/// Uses the typestate pattern with 5 type parameters to track registered
84/// handlers at compile time:
85///
86/// - `H`: Base server handler (always required)
87/// - `Tools`: Tool handler state
88/// - `Resources`: Resource handler state
89/// - `Prompts`: Prompt handler state
90/// - `Tasks`: Task handler state
91///
92/// When a capability is not registered, its type parameter is `NotRegistered`.
93/// When registered, it becomes `Registered<T>` where `T` is the handler type.
94pub struct ServerBuilder<H, Tools, Resources, Prompts, Tasks> {
95 handler: H,
96 tools: Tools,
97 resources: Resources,
98 prompts: Prompts,
99 tasks: Tasks,
100 capabilities: ServerCapabilities,
101}
102
103// Initial builder with no handlers registered
104impl<H: ServerHandler>
105 ServerBuilder<H, NotRegistered, NotRegistered, NotRegistered, NotRegistered>
106{
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 const 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>(
158 self,
159 tools: TH,
160 ) -> ServerBuilder<H, Registered<TH>, R, P, K> {
161 ServerBuilder {
162 handler: self.handler,
163 tools: Registered(tools),
164 resources: self.resources,
165 prompts: self.prompts,
166 tasks: self.tasks,
167 capabilities: self.capabilities.with_tools(),
168 }
169 }
170}
171
172// Resource handler registration (only when resources are not yet registered)
173impl<H, T, P, K> ServerBuilder<H, T, NotRegistered, P, K>
174where
175 H: ServerHandler,
176{
177 /// Register a resource handler.
178 ///
179 /// This method is only available when no resource handler has been registered yet.
180 #[must_use]
181 pub fn with_resources<RH: ResourceHandler>(
182 self,
183 resources: RH,
184 ) -> ServerBuilder<H, T, Registered<RH>, P, K> {
185 ServerBuilder {
186 handler: self.handler,
187 tools: self.tools,
188 resources: Registered(resources),
189 prompts: self.prompts,
190 tasks: self.tasks,
191 capabilities: self.capabilities.with_resources(),
192 }
193 }
194}
195
196// Prompt handler registration (only when prompts are not yet registered)
197impl<H, T, R, K> ServerBuilder<H, T, R, NotRegistered, K>
198where
199 H: ServerHandler,
200{
201 /// Register a prompt handler.
202 ///
203 /// This method is only available when no prompt handler has been registered yet.
204 #[must_use]
205 pub fn with_prompts<PH: PromptHandler>(
206 self,
207 prompts: PH,
208 ) -> ServerBuilder<H, T, R, Registered<PH>, K> {
209 ServerBuilder {
210 handler: self.handler,
211 tools: self.tools,
212 resources: self.resources,
213 prompts: Registered(prompts),
214 tasks: self.tasks,
215 capabilities: self.capabilities.with_prompts(),
216 }
217 }
218}
219
220// Task handler registration (only when tasks are not yet registered)
221impl<H, T, R, P> ServerBuilder<H, T, R, P, NotRegistered>
222where
223 H: ServerHandler,
224{
225 /// Register a task handler.
226 ///
227 /// Tasks are long-running operations that can be tracked, monitored,
228 /// and cancelled.
229 ///
230 /// This method is only available when no task handler has been registered yet.
231 #[must_use]
232 pub fn with_tasks<KH: TaskHandler>(
233 self,
234 tasks: KH,
235 ) -> ServerBuilder<H, T, R, P, Registered<KH>> {
236 ServerBuilder {
237 handler: self.handler,
238 tools: self.tools,
239 resources: self.resources,
240 prompts: self.prompts,
241 tasks: Registered(tasks),
242 // Note: capabilities.with_tasks() would be added when supported
243 capabilities: self.capabilities,
244 }
245 }
246}
247
248// Build method - available for any combination of handlers
249impl<H, T, R, P, K> ServerBuilder<H, T, R, P, K>
250where
251 H: ServerHandler + Send + Sync + 'static,
252 T: Send + Sync + 'static,
253 R: Send + Sync + 'static,
254 P: Send + Sync + 'static,
255 K: Send + Sync + 'static,
256{
257 /// Build the server.
258 ///
259 /// Returns a `Server` configured with the registered handlers and capabilities.
260 #[must_use]
261 pub fn build(self) -> Server<H, T, R, P, K> {
262 Server {
263 handler: self.handler,
264 tools: self.tools,
265 resources: self.resources,
266 prompts: self.prompts,
267 tasks: self.tasks,
268 capabilities: self.capabilities,
269 }
270 }
271}
272
273/// A configured MCP server ready to serve requests.
274///
275/// The type parameters track which capabilities are available:
276/// - `H`: Base server handler
277/// - `T`: Tool handler (`NotRegistered` or `Registered<TH>`)
278/// - `R`: Resource handler
279/// - `P`: Prompt handler
280/// - `K`: Task handler
281pub struct Server<H, T, R, P, K> {
282 handler: H,
283 tools: T,
284 resources: R,
285 prompts: P,
286 tasks: K,
287 capabilities: ServerCapabilities,
288}
289
290impl<H, T, R, P, K> Server<H, T, R, P, K>
291where
292 H: ServerHandler,
293{
294 /// Get the server's capabilities.
295 #[must_use]
296 pub const fn capabilities(&self) -> &ServerCapabilities {
297 &self.capabilities
298 }
299
300 /// Get a reference to the base handler.
301 #[must_use]
302 pub const fn handler(&self) -> &H {
303 &self.handler
304 }
305
306 /// Get the server info from the base handler.
307 #[must_use]
308 pub fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
309 self.handler.server_info()
310 }
311}
312
313// Methods when tools are registered
314impl<H, TH, R, P, K> Server<H, Registered<TH>, R, P, K>
315where
316 H: ServerHandler,
317 TH: ToolHandler,
318{
319 /// Get a reference to the tool handler.
320 #[must_use]
321 pub const fn tool_handler(&self) -> &TH {
322 &self.tools.0
323 }
324}
325
326// Methods when resources are registered
327impl<H, T, RH, P, K> Server<H, T, Registered<RH>, P, K>
328where
329 H: ServerHandler,
330 RH: ResourceHandler,
331{
332 /// Get a reference to the resource handler.
333 #[must_use]
334 pub const fn resource_handler(&self) -> &RH {
335 &self.resources.0
336 }
337}
338
339// Methods when prompts are registered
340impl<H, T, R, PH, K> Server<H, T, R, Registered<PH>, K>
341where
342 H: ServerHandler,
343 PH: PromptHandler,
344{
345 /// Get a reference to the prompt handler.
346 #[must_use]
347 pub const fn prompt_handler(&self) -> &PH {
348 &self.prompts.0
349 }
350}
351
352// Methods when tasks are registered
353impl<H, T, R, P, KH> Server<H, T, R, P, Registered<KH>>
354where
355 H: ServerHandler,
356 KH: TaskHandler,
357{
358 /// Get a reference to the task handler.
359 #[must_use]
360 pub const fn task_handler(&self) -> &KH {
361 &self.tasks.0
362 }
363}
364
365/// Type alias for a fully-configured server with all handlers.
366pub type FullServer<H, TH, RH, PH, KH> =
367 Server<H, Registered<TH>, Registered<RH>, Registered<PH>, Registered<KH>>;
368
369/// Type alias for a minimal server with no optional handlers.
370pub type MinimalServer<H> = Server<H, NotRegistered, NotRegistered, NotRegistered, NotRegistered>;
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375 use crate::context::Context;
376 use crate::handler::ToolHandler;
377 use mcpkit_core::capability::ServerInfo;
378 use mcpkit_core::error::McpError;
379 use mcpkit_core::types::{Tool, ToolOutput};
380 use serde_json::Value;
381
382 struct TestHandler;
383
384 impl ServerHandler for TestHandler {
385 fn server_info(&self) -> ServerInfo {
386 ServerInfo::new("test", "1.0.0")
387 }
388
389 fn capabilities(&self) -> ServerCapabilities {
390 ServerCapabilities::default()
391 }
392 }
393
394 struct TestToolHandler;
395
396 impl ToolHandler for TestToolHandler {
397 async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
398 Ok(vec![])
399 }
400
401 async fn call_tool(
402 &self,
403 _name: &str,
404 _args: Value,
405 _ctx: &Context<'_>,
406 ) -> Result<ToolOutput, McpError> {
407 Ok(ToolOutput::text("test"))
408 }
409 }
410
411 #[test]
412 fn test_server_builder_minimal() {
413 let server = ServerBuilder::new(TestHandler).build();
414
415 assert_eq!(server.server_info().name, "test");
416 assert_eq!(server.server_info().version, "1.0.0");
417 }
418
419 #[test]
420 fn test_server_builder_with_tools() {
421 let server = ServerBuilder::new(TestHandler)
422 .with_tools(TestToolHandler)
423 .build();
424
425 assert!(server.capabilities().has_tools());
426 // This compiles because tools are registered
427 let _tool_handler: &TestToolHandler = server.tool_handler();
428 }
429
430 #[test]
431 fn test_typestate_prevents_double_registration() {
432 // This test verifies the design - double registration would be
433 // a compile error, not a runtime error
434 let _server = ServerBuilder::new(TestHandler)
435 .with_tools(TestToolHandler)
436 // .with_tools(TestToolHandler) // This would NOT compile!
437 .build();
438 }
439
440 #[test]
441 fn test_builder_order_independence() {
442 // Handlers can be registered in any order
443 let server1 = ServerBuilder::new(TestHandler)
444 .with_tools(TestToolHandler)
445 .build();
446
447 // Different order, same result
448 let _server2: Server<
449 TestHandler,
450 Registered<TestToolHandler>,
451 NotRegistered,
452 NotRegistered,
453 NotRegistered,
454 > = ServerBuilder::new(TestHandler)
455 .with_tools(TestToolHandler)
456 .build();
457
458 assert!(server1.capabilities().has_tools());
459 }
460}