Skip to main content

mcp_kit/server/
builder.rs

1use std::sync::Arc;
2
3use crate::types::{
4    prompt::Prompt,
5    resource::{Resource, ResourceTemplate},
6    tool::Tool,
7    ServerInfo,
8};
9
10use crate::server::{
11    core::McpServer,
12    handler::{
13        CompletionHandler, PromptHandler, PromptHandlerFn, ResourceHandler, ResourceHandlerFn,
14        ToolHandler, ToolHandlerFn,
15    },
16    router::Router,
17};
18
19#[cfg(feature = "auth")]
20use crate::auth::DynAuthProvider;
21
22/// Builder for `McpServer` — the main entry point for configuring your server.
23pub struct McpServerBuilder {
24    name: String,
25    version: String,
26    instructions: Option<String>,
27    router: Router,
28    #[cfg(feature = "auth")]
29    auth_provider: Option<DynAuthProvider>,
30    #[cfg(feature = "auth")]
31    require_auth: bool,
32}
33
34impl McpServerBuilder {
35    pub fn new() -> Self {
36        Self {
37            name: "mcp-server".to_owned(),
38            version: "0.1.0".to_owned(),
39            instructions: None,
40            router: Router::new(),
41            #[cfg(feature = "auth")]
42            auth_provider: None,
43            #[cfg(feature = "auth")]
44            require_auth: true,
45        }
46    }
47
48    /// Set the server name (shown to clients during handshake)
49    pub fn name(mut self, name: impl Into<String>) -> Self {
50        self.name = name.into();
51        self
52    }
53
54    /// Set the server version
55    pub fn version(mut self, version: impl Into<String>) -> Self {
56        self.version = version.into();
57        self
58    }
59
60    /// Human-readable instructions for how to use this server
61    pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
62        self.instructions = Some(instructions.into());
63        self
64    }
65
66    // ─── Auth configuration ───────────────────────────────────────────────────
67
68    /// Require authentication on all requests using the given provider.
69    ///
70    /// Requests with no or invalid credentials receive HTTP 401 on SSE/HTTP
71    /// transports. Stdio transport is unaffected (it relies on process-level
72    /// access control).
73    ///
74    /// # Example
75    /// ```rust,no_run
76    /// use mcp_kit::prelude::*;
77    /// use mcp_kit::auth::BearerTokenProvider;
78    /// use std::sync::Arc;
79    ///
80    /// McpServer::builder()
81    ///     .name("my-server")
82    ///     .version("1.0")
83    ///     .auth(Arc::new(BearerTokenProvider::new(["secret"])))
84    ///     .build();
85    /// ```
86    #[cfg(feature = "auth")]
87    pub fn auth(mut self, provider: DynAuthProvider) -> Self {
88        self.auth_provider = Some(provider);
89        self.require_auth = true;
90        self
91    }
92
93    /// Accept an auth provider but allow unauthenticated requests through.
94    ///
95    /// Authenticated requests have an identity available via `Auth`; unauthenticated
96    /// requests have no identity and may reach handlers with `None`.
97    #[cfg(feature = "auth")]
98    pub fn optional_auth(mut self, provider: DynAuthProvider) -> Self {
99        self.auth_provider = Some(provider);
100        self.require_auth = false;
101        self
102    }
103
104    // ─── Tool registration ────────────────────────────────────────────────────
105
106    /// Register a tool with an explicit `Tool` descriptor and a handler function.
107    pub fn tool<M>(mut self, tool: Tool, handler: impl ToolHandler<M>) -> Self {
108        self.router.add_tool(tool, handler.into_handler_fn());
109        self
110    }
111
112    /// Register a tool using a pre-built `ToolDef` (from the `#[tool]` macro).
113    pub fn tool_def(mut self, def: ToolDef) -> Self {
114        self.router.add_tool(def.tool, def.handler);
115        self
116    }
117
118    /// Convenience: register a no-parameter tool.
119    pub fn tool_fn<M>(
120        mut self,
121        name: impl Into<String>,
122        description: impl Into<String>,
123        handler: impl ToolHandler<M>,
124    ) -> Self {
125        self.router.add_tool(
126            Tool::no_params(name, description),
127            handler.into_handler_fn(),
128        );
129        self
130    }
131
132    // ─── Resource registration ────────────────────────────────────────────────
133
134    /// Register a static resource (exact URI match).
135    pub fn resource<M>(mut self, resource: Resource, handler: impl ResourceHandler<M>) -> Self {
136        self.router
137            .add_resource(resource, handler.into_handler_fn());
138        self
139    }
140
141    /// Register a URI-template resource (e.g. `"file://{path}"`).
142    pub fn resource_template<M>(
143        mut self,
144        template: ResourceTemplate,
145        handler: impl ResourceHandler<M>,
146    ) -> Self {
147        self.router
148            .add_resource_template(template, handler.into_handler_fn());
149        self
150    }
151
152    /// Register a resource using a pre-built `ResourceDef` (from the `#[resource]` macro).
153    pub fn resource_def(mut self, def: ResourceDef) -> Self {
154        match def {
155            ResourceDef::Static { resource, handler } => {
156                self.router.add_resource(resource, handler);
157            }
158            ResourceDef::Template { template, handler } => {
159                self.router.add_resource_template(template, handler);
160            }
161        }
162        self
163    }
164
165    // ─── Prompt registration ──────────────────────────────────────────────────
166
167    /// Register a prompt template.
168    pub fn prompt<M>(mut self, prompt: Prompt, handler: impl PromptHandler<M>) -> Self {
169        self.router.add_prompt(prompt, handler.into_handler_fn());
170        self
171    }
172
173    /// Register a prompt using a pre-built `PromptDef` (from the `#[prompt]` macro).
174    pub fn prompt_def(mut self, def: PromptDef) -> Self {
175        self.router.add_prompt(def.prompt, def.handler);
176        self
177    }
178
179    // ─── Completion registration ──────────────────────────────────────────────
180
181    /// Register a global completion handler for auto-completing prompt/resource arguments.
182    ///
183    /// This handler is called for any `completion/complete` request that doesn't have
184    /// a more specific handler (prompt-specific or resource-specific).
185    ///
186    /// # Example
187    /// ```rust,no_run
188    /// use mcp_kit::prelude::*;
189    /// use mcp_kit::types::messages::{CompleteRequest, CompleteResult};
190    ///
191    /// McpServer::builder()
192    ///     .name("my-server")
193    ///     .completion(|req: CompleteRequest| async move {
194    ///         // Auto-complete based on argument name
195    ///         let values = match req.argument.name.as_str() {
196    ///             "language" => vec!["rust", "python", "javascript"],
197    ///             _ => vec![],
198    ///         };
199    ///         Ok(CompleteResult::new(values))
200    ///     })
201    ///     .build();
202    /// ```
203    pub fn completion<M>(mut self, handler: impl CompletionHandler<M>) -> Self {
204        self.router
205            .set_completion_handler(handler.into_handler_fn());
206        self
207    }
208
209    /// Register a completion handler for a specific resource URI pattern.
210    ///
211    /// The pattern can be an exact URI or a template like `"file://{path}"`.
212    pub fn resource_completion<M>(
213        mut self,
214        uri_pattern: impl Into<String>,
215        handler: impl CompletionHandler<M>,
216    ) -> Self {
217        self.router
218            .add_resource_completion(uri_pattern.into(), handler.into_handler_fn());
219        self
220    }
221
222    /// Register a prompt with an associated completion handler.
223    ///
224    /// The completion handler provides auto-complete suggestions for the prompt's arguments.
225    pub fn prompt_with_completion<M1, M2>(
226        mut self,
227        prompt: Prompt,
228        handler: impl PromptHandler<M1>,
229        completion: impl CompletionHandler<M2>,
230    ) -> Self {
231        self.router.add_prompt_with_completion(
232            prompt,
233            handler.into_handler_fn(),
234            completion.into_handler_fn(),
235        );
236        self
237    }
238
239    // ─── Build ────────────────────────────────────────────────────────────────
240
241    pub fn build(self) -> McpServer {
242        McpServer {
243            info: ServerInfo::new(self.name, self.version),
244            instructions: self.instructions,
245            router: Arc::new(self.router),
246            #[cfg(feature = "auth")]
247            auth_provider: self.auth_provider,
248            #[cfg(feature = "auth")]
249            require_auth: self.require_auth,
250        }
251    }
252}
253
254impl Default for McpServerBuilder {
255    fn default() -> Self {
256        Self::new()
257    }
258}
259
260// ─── ToolDef ─────────────────────────────────────────────────────────────────
261
262/// A fully-described tool produced by the `#[tool]` proc macro.
263pub struct ToolDef {
264    pub tool: Tool,
265    pub handler: ToolHandlerFn,
266}
267
268impl ToolDef {
269    pub fn new(tool: Tool, handler: ToolHandlerFn) -> Self {
270        Self { tool, handler }
271    }
272}
273
274// ─── ResourceDef ─────────────────────────────────────────────────────────────
275
276/// A fully-described resource produced by the `#[resource]` proc macro.
277pub enum ResourceDef {
278    Static {
279        resource: Resource,
280        handler: ResourceHandlerFn,
281    },
282    Template {
283        template: ResourceTemplate,
284        handler: ResourceHandlerFn,
285    },
286}
287
288impl ResourceDef {
289    pub fn new_static(resource: Resource, handler: ResourceHandlerFn) -> Self {
290        Self::Static { resource, handler }
291    }
292
293    pub fn new_template(template: ResourceTemplate, handler: ResourceHandlerFn) -> Self {
294        Self::Template { template, handler }
295    }
296}
297
298// ─── PromptDef ───────────────────────────────────────────────────────────────
299
300/// A fully-described prompt produced by the `#[prompt]` proc macro.
301pub struct PromptDef {
302    pub prompt: Prompt,
303    pub handler: PromptHandlerFn,
304}
305
306impl PromptDef {
307    pub fn new(prompt: Prompt, handler: PromptHandlerFn) -> Self {
308        Self { prompt, handler }
309    }
310}