Skip to main content

component_shape_mcp/
server.rs

1use super::*;
2
3/// A clonable collection of MCP tool definitions and in-process handlers.
4///
5/// Resources, prompts, server identity, and transport lifecycle remain owned
6/// by [`McpServer`].
7#[derive(Clone, Default)]
8pub struct McpToolRegistry {
9    tools: BTreeMap<String, Arc<dyn ToolExecutor>>,
10}
11
12impl McpToolRegistry {
13    /// Create an empty tool registry.
14    #[must_use]
15    pub fn new() -> Self {
16        Self::default()
17    }
18
19    /// Register a synchronous MCP tool handler.
20    ///
21    /// # Errors
22    ///
23    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
24    /// same name is already registered.
25    pub fn add_tool<Call>(
26        &mut self,
27        definition: ToolDefinition,
28        call: Call,
29    ) -> Result<(), McpToolError>
30    where
31        Call: Fn(McpToolCall) -> ToolCallResult + Send + Sync + 'static,
32    {
33        let name = definition.name.to_string();
34        validate_tool_definition(&definition)?;
35        if self.tools.contains_key(&name) {
36            return Err(McpToolError::duplicate_tool(name));
37        }
38
39        self.tools.insert(
40            name,
41            Arc::new(RegisteredTool {
42                definition,
43                call: Arc::new(move |arguments| Box::pin(std::future::ready(call(arguments)))),
44            }),
45        );
46        Ok(())
47    }
48
49    /// Register a synchronous typed MCP tool handler.
50    ///
51    /// # Errors
52    ///
53    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
54    /// same name is already registered.
55    pub fn add_typed_tool<Input, Call>(
56        &mut self,
57        definition: McpTypedTool<Input>,
58        call: Call,
59    ) -> Result<(), McpToolError>
60    where
61        Input: McpToolInput,
62        Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
63    {
64        self.add_tool(definition.into_definition(), move |tool_call| {
65            let input = match Input::from_tool_call(tool_call) {
66                Ok(input) => input,
67                Err(error) => return tool_error_result_for(error),
68            };
69            call(input)
70        })
71    }
72
73    /// Register an async MCP tool handler.
74    ///
75    /// # Errors
76    ///
77    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
78    /// same name is already registered.
79    pub fn add_tool_async<Call, Fut>(
80        &mut self,
81        definition: ToolDefinition,
82        call: Call,
83    ) -> Result<(), McpToolError>
84    where
85        Call: Fn(McpToolCall) -> Fut + Send + Sync + 'static,
86        Fut: Future<Output = ToolCallResult> + Send + 'static,
87    {
88        let name = definition.name.to_string();
89        validate_tool_definition(&definition)?;
90        if self.tools.contains_key(&name) {
91            return Err(McpToolError::duplicate_tool(name));
92        }
93
94        self.tools.insert(
95            name,
96            Arc::new(RegisteredTool {
97                definition,
98                call: Arc::new(move |arguments| Box::pin(call(arguments))),
99            }),
100        );
101        Ok(())
102    }
103
104    /// Register an async typed MCP tool handler.
105    ///
106    /// # Errors
107    ///
108    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
109    /// same name is already registered.
110    pub fn add_typed_tool_async<Input, Call, Fut>(
111        &mut self,
112        definition: McpTypedTool<Input>,
113        call: Call,
114    ) -> Result<(), McpToolError>
115    where
116        Input: McpToolInput,
117        Call: Fn(Input) -> Fut + Send + Sync + 'static,
118        Fut: Future<Output = ToolCallResult> + Send + 'static,
119    {
120        self.add_tool_async(definition.into_definition(), move |tool_call| {
121            let input = Input::from_tool_call(tool_call);
122            let future = match input {
123                Ok(input) => call(input),
124                Err(error) => {
125                    return Box::pin(std::future::ready(tool_error_result_for(error)))
126                        as ToolFuture;
127                },
128            };
129            Box::pin(future) as ToolFuture
130        })
131    }
132
133    /// Return registered MCP tool definitions.
134    #[must_use]
135    pub fn list_tools(&self) -> Vec<ToolDefinition> {
136        self.tools
137            .values()
138            .map(|executor| executor.definition())
139            .collect()
140    }
141
142    /// Whether a tool name is already registered.
143    #[must_use]
144    pub fn contains_tool(&self, name: &str) -> bool {
145        self.tools.contains_key(name)
146    }
147
148    /// Number of registered tools.
149    #[must_use]
150    pub fn tool_count(&self) -> usize {
151        self.tools.len()
152    }
153
154    /// Calls a registered tool and converts validation or handler failures into
155    /// a protocol-level tool result.
156    #[must_use]
157    pub fn call_tool(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
158        let call = match McpToolCall::from_value(arguments) {
159            Ok(call) => call,
160            Err(error) => return tool_error_result_for(error),
161        };
162        let (definition, call) = match self.resolve_tool(name, call) {
163            Ok(resolved) => resolved,
164            Err(error) => return tool_error_result_for(error),
165        };
166        validate_tool_call_result(
167            name,
168            definition.output_schema.as_deref(),
169            block_on_tool_future(call),
170        )
171    }
172
173    /// Asynchronously calls a registered tool and converts validation or
174    /// handler failures into a protocol-level tool result.
175    pub async fn call_tool_async(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
176        let call = match McpToolCall::from_value(arguments) {
177            Ok(call) => call,
178            Err(error) => return tool_error_result_for(error),
179        };
180        let (definition, call) = match self.resolve_tool(name, call) {
181            Ok(resolved) => resolved,
182            Err(error) => return tool_error_result_for(error),
183        };
184        validate_tool_call_result(name, definition.output_schema.as_deref(), call.await)
185    }
186
187    fn definition(&self, name: &str) -> Option<ToolDefinition> {
188        self.tools.get(name).map(|executor| executor.definition())
189    }
190
191    fn resolve_tool(
192        &self,
193        name: &str,
194        call: McpToolCall,
195    ) -> Result<(ToolDefinition, ToolFuture), McpToolError> {
196        match self.tools.get(name) {
197            Some(executor) => Ok((executor.definition(), executor.call(call))),
198            None => Err(McpToolError::UnknownTool {
199                name: name.to_string(),
200            }),
201        }
202    }
203}
204
205/// In-process MCP server that owns registered tools, resources, and prompts.
206#[derive(Clone)]
207pub struct McpServer {
208    pub(crate) server_name: Cow<'static, str>,
209    pub(crate) server_version: Cow<'static, str>,
210    tools: McpToolRegistry,
211    resources: BTreeMap<String, Arc<dyn ResourceReader>>,
212    resource_templates: Vec<ResourceTemplate>,
213    prompts: BTreeMap<String, Arc<dyn PromptExecutor>>,
214}
215
216impl McpServer {
217    /// Create a dynamic MCP tool server with the advertised metadata.
218    pub fn new(
219        server_name: impl Into<Cow<'static, str>>,
220        server_version: impl Into<Cow<'static, str>>,
221    ) -> Self {
222        Self {
223            server_name: server_name.into(),
224            server_version: server_version.into(),
225            tools: McpToolRegistry::new(),
226            resources: BTreeMap::new(),
227            resource_templates: Vec::new(),
228            prompts: BTreeMap::new(),
229        }
230    }
231
232    /// Start building a dynamic MCP tool server with generated registrars.
233    pub fn builder(
234        server_name: impl Into<Cow<'static, str>>,
235        server_version: impl Into<Cow<'static, str>>,
236    ) -> McpServerBuilder {
237        McpServerBuilder::new(server_name, server_version)
238    }
239
240    /// Create a dynamic MCP server from an existing tool registry.
241    #[must_use]
242    pub fn from_tool_registry(
243        server_name: impl Into<Cow<'static, str>>,
244        server_version: impl Into<Cow<'static, str>>,
245        tools: McpToolRegistry,
246    ) -> Self {
247        Self {
248            tools,
249            ..Self::new(server_name, server_version)
250        }
251    }
252
253    /// Borrow the shared tool registry.
254    #[must_use]
255    pub fn tool_registry(&self) -> &McpToolRegistry {
256        &self.tools
257    }
258
259    /// Mutably borrow the shared tool registry.
260    pub fn tool_registry_mut(&mut self) -> &mut McpToolRegistry {
261        &mut self.tools
262    }
263
264    /// Consume the server and return its tool registry.
265    #[must_use]
266    pub fn into_tool_registry(self) -> McpToolRegistry {
267        self.tools
268    }
269
270    /// Register a synchronous MCP tool handler.
271    ///
272    /// # Errors
273    ///
274    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
275    /// same name is already registered.
276    pub fn add_tool<Call>(
277        &mut self,
278        definition: ToolDefinition,
279        call: Call,
280    ) -> Result<(), McpToolError>
281    where
282        Call: Fn(McpToolCall) -> ToolCallResult + Send + Sync + 'static,
283    {
284        self.tools.add_tool(definition, call)
285    }
286
287    /// Register a synchronous typed MCP tool handler.
288    ///
289    /// # Errors
290    ///
291    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
292    /// same name is already registered.
293    pub fn add_typed_tool<Input, Call>(
294        &mut self,
295        definition: McpTypedTool<Input>,
296        call: Call,
297    ) -> Result<(), McpToolError>
298    where
299        Input: McpToolInput,
300        Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
301    {
302        self.tools.add_typed_tool(definition, call)
303    }
304
305    /// Register an async MCP tool handler.
306    ///
307    /// # Errors
308    ///
309    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
310    /// same name is already registered.
311    pub fn add_tool_async<Call, Fut>(
312        &mut self,
313        definition: ToolDefinition,
314        call: Call,
315    ) -> Result<(), McpToolError>
316    where
317        Call: Fn(McpToolCall) -> Fut + Send + Sync + 'static,
318        Fut: Future<Output = ToolCallResult> + Send + 'static,
319    {
320        self.tools.add_tool_async(definition, call)
321    }
322
323    /// Register an async typed MCP tool handler.
324    ///
325    /// # Errors
326    ///
327    /// Returns [`McpToolError`] when `definition` is invalid or a tool with the
328    /// same name is already registered.
329    pub fn add_typed_tool_async<Input, Call, Fut>(
330        &mut self,
331        definition: McpTypedTool<Input>,
332        call: Call,
333    ) -> Result<(), McpToolError>
334    where
335        Input: McpToolInput,
336        Call: Fn(Input) -> Fut + Send + Sync + 'static,
337        Fut: Future<Output = ToolCallResult> + Send + 'static,
338    {
339        self.tools.add_typed_tool_async(definition, call)
340    }
341
342    /// Return registered MCP tool definitions.
343    #[must_use]
344    pub fn list_tools(&self) -> Vec<ToolDefinition> {
345        self.tools.list_tools()
346    }
347
348    /// Whether a tool name is already registered.
349    #[must_use]
350    pub fn contains_tool(&self, name: &str) -> bool {
351        self.tools.contains_tool(name)
352    }
353
354    /// Number of registered tools.
355    #[must_use]
356    pub fn tool_count(&self) -> usize {
357        self.tools.tool_count()
358    }
359
360    /// Register a static MCP resource reader.
361    ///
362    /// # Errors
363    ///
364    /// Returns [`McpToolError`] when `definition` is invalid or a resource with
365    /// the same URI is already registered.
366    pub fn add_resource<Read>(
367        &mut self,
368        definition: ResourceDefinition,
369        read: Read,
370    ) -> Result<(), McpToolError>
371    where
372        Read: Fn() -> ReadResourceResult + Send + Sync + 'static,
373    {
374        self.add_resource_async(definition, move || {
375            let result = read();
376            std::future::ready(Ok(result))
377        })
378    }
379
380    /// Register an async MCP resource reader.
381    ///
382    /// # Errors
383    ///
384    /// Returns [`McpToolError`] when `definition` is invalid or a resource with
385    /// the same URI is already registered.
386    pub fn add_resource_async<Read, Fut>(
387        &mut self,
388        definition: ResourceDefinition,
389        read: Read,
390    ) -> Result<(), McpToolError>
391    where
392        Read: Fn() -> Fut + Send + Sync + 'static,
393        Fut: Future<Output = Result<ReadResourceResult, ErrorData>> + Send + 'static,
394    {
395        let uri = definition.uri.clone();
396        validate_resource_definition(&definition)?;
397        if self.resources.contains_key(&uri) {
398            return Err(McpToolError::duplicate_resource(uri));
399        }
400
401        self.resources.insert(
402            uri,
403            Arc::new(RegisteredResource {
404                definition,
405                read: Arc::new(move || Box::pin(read())),
406            }),
407        );
408        Ok(())
409    }
410
411    /// Register an MCP resource template advertised by `resources/templates/list`.
412    ///
413    /// # Errors
414    ///
415    /// Returns [`McpToolError`] when `definition` is invalid.
416    pub fn add_resource_template(
417        &mut self,
418        definition: ResourceTemplateDefinition,
419    ) -> Result<(), McpToolError> {
420        validate_resource_template(&definition)?;
421        self.resource_templates.push(definition);
422        Ok(())
423    }
424
425    /// Return registered MCP resource definitions.
426    pub fn list_resources(&self) -> Vec<ResourceDefinition> {
427        self.resources
428            .values()
429            .map(|resource| resource.definition())
430            .collect()
431    }
432
433    /// Return registered MCP resource template definitions.
434    pub fn list_resource_templates(&self) -> Vec<ResourceTemplateDefinition> {
435        self.resource_templates.clone()
436    }
437
438    /// Whether a resource URI is already registered.
439    pub fn contains_resource(&self, uri: &str) -> bool {
440        self.resources.contains_key(uri)
441    }
442
443    /// Number of registered concrete resources.
444    pub fn resource_count(&self) -> usize {
445        self.resources.len()
446    }
447
448    /// Register a static MCP prompt.
449    ///
450    /// # Errors
451    ///
452    /// Returns [`McpToolError`] when `definition` is invalid or a prompt with
453    /// the same name is already registered.
454    pub fn add_prompt<Get>(
455        &mut self,
456        definition: PromptDefinition,
457        get: Get,
458    ) -> Result<(), McpToolError>
459    where
460        Get: Fn(Option<JsonObject>) -> GetPromptResult + Send + Sync + 'static,
461    {
462        self.add_prompt_async(definition, move |arguments| {
463            let result = get(arguments);
464            std::future::ready(Ok(result))
465        })
466    }
467
468    /// Register an async MCP prompt.
469    ///
470    /// # Errors
471    ///
472    /// Returns [`McpToolError`] when `definition` is invalid or a prompt with
473    /// the same name is already registered.
474    pub fn add_prompt_async<Get, Fut>(
475        &mut self,
476        definition: PromptDefinition,
477        get: Get,
478    ) -> Result<(), McpToolError>
479    where
480        Get: Fn(Option<JsonObject>) -> Fut + Send + Sync + 'static,
481        Fut: Future<Output = Result<GetPromptResult, ErrorData>> + Send + 'static,
482    {
483        let name = definition.name.clone();
484        validate_prompt_definition(&definition)?;
485        if self.prompts.contains_key(&name) {
486            return Err(McpToolError::duplicate_prompt(name));
487        }
488
489        self.prompts.insert(
490            name,
491            Arc::new(RegisteredPrompt {
492                definition,
493                get: Arc::new(move |arguments| Box::pin(get(arguments))),
494            }),
495        );
496        Ok(())
497    }
498
499    /// Return registered MCP prompt definitions.
500    pub fn list_prompts(&self) -> Vec<PromptDefinition> {
501        self.prompts
502            .values()
503            .map(|prompt| prompt.definition())
504            .collect()
505    }
506
507    /// Whether a prompt name is already registered.
508    pub fn contains_prompt(&self, name: &str) -> bool {
509        self.prompts.contains_key(name)
510    }
511
512    /// Number of registered prompts.
513    pub fn prompt_count(&self) -> usize {
514        self.prompts.len()
515    }
516
517    /// Calls a registered tool and converts validation or handler failures into
518    /// a protocol-level tool result.
519    #[must_use]
520    pub fn call_tool(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
521        self.tools.call_tool(name, arguments)
522    }
523
524    /// Asynchronously calls a registered tool and converts validation or
525    /// handler failures into a protocol-level tool result.
526    pub async fn call_tool_async(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
527        self.tools.call_tool_async(name, arguments).await
528    }
529
530    /// Serve this server over stdin/stdout using the MCP stdio transport.
531    ///
532    /// # Errors
533    ///
534    /// Returns an error when stdio serving fails or the service task fails.
535    pub async fn serve_stdio(self) -> ServeStdioResult {
536        let service = self.serve(stdio()).await?;
537        service.waiting().await?;
538        Ok(())
539    }
540
541    /// Serve this server over stdin/stdout on a new Tokio runtime.
542    ///
543    /// # Errors
544    ///
545    /// Returns an error when the Tokio runtime cannot be created, stdio serving
546    /// fails, or the service task fails.
547    pub fn serve_stdio_blocking(self) -> ServeStdioResult {
548        let runtime = tokio::runtime::Builder::new_multi_thread()
549            .enable_all()
550            .build()?;
551        runtime.block_on(self.serve_stdio())
552    }
553
554    fn result_meta(&self) -> MetaObject {
555        let mut meta = MetaObject::default();
556        meta.0.insert(
557            "io.modelcontextprotocol/serverInfo".to_string(),
558            json!({
559                "name": self.server_name,
560                "version": self.server_version,
561            }),
562        );
563        meta
564    }
565
566    fn prepare_cacheable_result(
567        &self,
568        protocol_version: Option<&ProtocolVersion>,
569        ttl_ms: &mut Option<u64>,
570        cache_scope: &mut Option<CacheScope>,
571        meta: &mut Option<MetaObject>,
572    ) {
573        if protocol_version.is_some_and(|version| version >= &ProtocolVersion::V_2026_07_28) {
574            *ttl_ms = Some(0);
575            *cache_scope = Some(CacheScope::Private);
576        }
577        *meta = Some(self.result_meta());
578    }
579}
580
581/// Builder for composing generated MCP server registrars.
582#[derive(Clone)]
583pub struct McpServerBuilder {
584    server: Result<McpServer, McpToolError>,
585}
586
587impl McpServerBuilder {
588    /// Create a builder with advertised server metadata.
589    pub fn new(
590        server_name: impl Into<Cow<'static, str>>,
591        server_version: impl Into<Cow<'static, str>>,
592    ) -> Self {
593        Self {
594            server: Ok(McpServer::new(server_name, server_version)),
595        }
596    }
597
598    /// Runs a registrar against the server being built.
599    pub fn register<Register>(mut self, register: Register) -> Self
600    where
601        Register: FnOnce(&mut McpServer) -> Result<(), McpToolError>,
602    {
603        if let Ok(server) = self.server.as_mut()
604            && let Err(error) = register(server)
605        {
606            self.server = Err(error);
607        }
608        self
609    }
610
611    /// Add a synchronous MCP tool to the server being built.
612    pub fn tool<Call>(self, definition: ToolDefinition, call: Call) -> Self
613    where
614        Call: Fn(McpToolCall) -> ToolCallResult + Send + Sync + 'static,
615    {
616        self.register(move |server| server.add_tool(definition, call))
617    }
618
619    /// Add a synchronous typed MCP tool to the server being built.
620    pub fn typed_tool<Input, Call>(self, definition: McpTypedTool<Input>, call: Call) -> Self
621    where
622        Input: McpToolInput,
623        Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
624    {
625        self.register(move |server| server.add_typed_tool(definition, call))
626    }
627
628    /// Add an async MCP tool to the server being built.
629    pub fn tool_async<Call, Fut>(self, definition: ToolDefinition, call: Call) -> Self
630    where
631        Call: Fn(McpToolCall) -> Fut + Send + Sync + 'static,
632        Fut: Future<Output = ToolCallResult> + Send + 'static,
633    {
634        self.register(move |server| server.add_tool_async(definition, call))
635    }
636
637    /// Add an async typed MCP tool to the server being built.
638    pub fn typed_tool_async<Input, Call, Fut>(
639        self,
640        definition: McpTypedTool<Input>,
641        call: Call,
642    ) -> Self
643    where
644        Input: McpToolInput,
645        Call: Fn(Input) -> Fut + Send + Sync + 'static,
646        Fut: Future<Output = ToolCallResult> + Send + 'static,
647    {
648        self.register(move |server| server.add_typed_tool_async(definition, call))
649    }
650
651    /// Add a static MCP resource to the server being built.
652    pub fn resource<Read>(self, definition: ResourceDefinition, read: Read) -> Self
653    where
654        Read: Fn() -> ReadResourceResult + Send + Sync + 'static,
655    {
656        self.register(move |server| server.add_resource(definition, read))
657    }
658
659    /// Add an async MCP resource to the server being built.
660    pub fn resource_async<Read, Fut>(self, definition: ResourceDefinition, read: Read) -> Self
661    where
662        Read: Fn() -> Fut + Send + Sync + 'static,
663        Fut: Future<Output = Result<ReadResourceResult, ErrorData>> + Send + 'static,
664    {
665        self.register(move |server| server.add_resource_async(definition, read))
666    }
667
668    /// Add an MCP resource template to the server being built.
669    pub fn resource_template(self, definition: ResourceTemplateDefinition) -> Self {
670        self.register(move |server| server.add_resource_template(definition))
671    }
672
673    /// Add a static MCP prompt to the server being built.
674    pub fn prompt<Get>(self, definition: PromptDefinition, get: Get) -> Self
675    where
676        Get: Fn(Option<JsonObject>) -> GetPromptResult + Send + Sync + 'static,
677    {
678        self.register(move |server| server.add_prompt(definition, get))
679    }
680
681    /// Add an async MCP prompt to the server being built.
682    pub fn prompt_async<Get, Fut>(self, definition: PromptDefinition, get: Get) -> Self
683    where
684        Get: Fn(Option<JsonObject>) -> Fut + Send + Sync + 'static,
685        Fut: Future<Output = Result<GetPromptResult, ErrorData>> + Send + 'static,
686    {
687        self.register(move |server| server.add_prompt_async(definition, get))
688    }
689
690    /// Finish the builder and return the composed server.
691    ///
692    /// # Errors
693    ///
694    /// Returns the first [`McpToolError`] produced by a builder registrar.
695    pub fn build(self) -> Result<McpServer, McpToolError> {
696        self.server
697    }
698
699    /// Build and serve this server over stdin/stdout using the MCP stdio transport.
700    ///
701    /// # Errors
702    ///
703    /// Returns an error when a builder registrar fails, stdio serving fails, or
704    /// the service task fails.
705    pub async fn serve_stdio(self) -> ServeStdioResult {
706        self.build()?.serve_stdio().await
707    }
708
709    /// Build and serve this server over stdin/stdout on a new Tokio runtime.
710    ///
711    /// # Errors
712    ///
713    /// Returns an error when a builder registrar fails, the Tokio runtime cannot
714    /// be created, stdio serving fails, or the service task fails.
715    pub fn serve_stdio_blocking(self) -> ServeStdioResult {
716        self.build()?.serve_stdio_blocking()
717    }
718}
719
720impl ServerHandler for McpServer {
721    fn get_info(&self) -> ServerConfig {
722        let mut capabilities = ServerCapabilities::builder().enable_tools().build();
723        capabilities.resources = (!self.resources.is_empty()
724            || !self.resource_templates.is_empty())
725        .then(Default::default);
726        capabilities.prompts = (!self.prompts.is_empty()).then(Default::default);
727        ServerConfig::new(capabilities)
728            .with_protocol_version(ProtocolVersion::V_2026_07_28)
729            .with_server_info(Implementation::new(
730                self.server_name.clone(),
731                self.server_version.clone(),
732            ))
733    }
734
735    fn list_tools(
736        &self,
737        _request: Option<PaginatedRequestParams>,
738        context: RequestContext<RoleServer>,
739    ) -> impl Future<Output = Result<ListToolsResult, ErrorData>> + MaybeSendFuture + '_ {
740        let mut result = ListToolsResult::with_all_items(self.list_tools());
741        self.prepare_cacheable_result(
742            context.protocol_version().as_ref(),
743            &mut result.ttl_ms,
744            &mut result.cache_scope,
745            &mut result.meta,
746        );
747        std::future::ready(Ok(result))
748    }
749
750    fn get_tool(&self, name: &str) -> Option<Tool> {
751        self.tools.definition(name)
752    }
753
754    fn call_tool(
755        &self,
756        request: CallToolRequestParams,
757        _context: RequestContext<RoleServer>,
758    ) -> impl Future<Output = Result<CallToolResponse, ErrorData>> + MaybeSendFuture + '_ {
759        let name = request.name.to_string();
760        let call = McpToolCall::new(request.arguments.unwrap_or_default());
761        let call = match self.tools.resolve_tool(&name, call) {
762            Ok((definition, call)) => {
763                let output_schema = definition.output_schema;
764                let name = name.clone();
765                Box::pin(async move {
766                    validate_tool_call_result(&name, output_schema.as_deref(), call.await)
767                }) as ToolFuture
768            },
769            Err(error) => {
770                let result = tool_error_result_for(error);
771                Box::pin(std::future::ready(result))
772            },
773        };
774        let result_meta = self.result_meta();
775        async move {
776            let mut result = call.await;
777            result.meta = Some(result_meta);
778            Ok(result.into())
779        }
780    }
781
782    fn list_resources(
783        &self,
784        _request: Option<PaginatedRequestParams>,
785        context: RequestContext<RoleServer>,
786    ) -> impl Future<Output = Result<ListResourcesResult, ErrorData>> + MaybeSendFuture + '_ {
787        let mut result = ListResourcesResult::with_all_items(self.list_resources());
788        self.prepare_cacheable_result(
789            context.protocol_version().as_ref(),
790            &mut result.ttl_ms,
791            &mut result.cache_scope,
792            &mut result.meta,
793        );
794        std::future::ready(Ok(result))
795    }
796
797    fn list_resource_templates(
798        &self,
799        _request: Option<PaginatedRequestParams>,
800        context: RequestContext<RoleServer>,
801    ) -> impl Future<Output = Result<ListResourceTemplatesResult, ErrorData>> + MaybeSendFuture + '_
802    {
803        let mut result =
804            ListResourceTemplatesResult::with_all_items(self.list_resource_templates());
805        self.prepare_cacheable_result(
806            context.protocol_version().as_ref(),
807            &mut result.ttl_ms,
808            &mut result.cache_scope,
809            &mut result.meta,
810        );
811        std::future::ready(Ok(result))
812    }
813
814    fn read_resource(
815        &self,
816        request: ReadResourceRequestParams,
817        context: RequestContext<RoleServer>,
818    ) -> impl Future<Output = Result<ReadResourceResponse, ErrorData>> + MaybeSendFuture + '_ {
819        let uri = request.uri;
820        let read = self.resources.get(&uri).map(|resource| resource.read());
821        let protocol_version = context.protocol_version();
822        let result_meta = self.result_meta();
823        async move {
824            match read {
825                Some(read) => {
826                    let mut result = read.await?;
827                    if protocol_version
828                        .as_ref()
829                        .is_some_and(|version| version >= &ProtocolVersion::V_2026_07_28)
830                    {
831                        result.ttl_ms = Some(0);
832                        result.cache_scope = Some(CacheScope::Private);
833                    }
834                    result.meta = Some(result_meta);
835                    Ok(result.into())
836                },
837                None => Err(ErrorData::resource_not_found(
838                    format!("resource `{uri}` not found"),
839                    Some(McpToolError::unknown_resource(uri).to_structured_value()),
840                )),
841            }
842        }
843    }
844
845    fn list_prompts(
846        &self,
847        _request: Option<PaginatedRequestParams>,
848        context: RequestContext<RoleServer>,
849    ) -> impl Future<Output = Result<ListPromptsResult, ErrorData>> + MaybeSendFuture + '_ {
850        let mut result = ListPromptsResult::with_all_items(self.list_prompts());
851        self.prepare_cacheable_result(
852            context.protocol_version().as_ref(),
853            &mut result.ttl_ms,
854            &mut result.cache_scope,
855            &mut result.meta,
856        );
857        std::future::ready(Ok(result))
858    }
859
860    fn get_prompt(
861        &self,
862        request: GetPromptRequestParams,
863        _context: RequestContext<RoleServer>,
864    ) -> impl Future<Output = Result<GetPromptResponse, ErrorData>> + MaybeSendFuture + '_ {
865        let name = request.name;
866        let get = self
867            .prompts
868            .get(&name)
869            .map(|prompt| prompt.get(request.arguments));
870        let result_meta = self.result_meta();
871        async move {
872            match get {
873                Some(get) => {
874                    let mut result = get.await?;
875                    result.meta = Some(result_meta);
876                    Ok(result.into())
877                },
878                None => Err(ErrorData::invalid_params(
879                    format!("prompt `{name}` not found"),
880                    Some(McpToolError::unknown_prompt(name).to_structured_value()),
881                )),
882            }
883        }
884    }
885}
886
887trait ToolExecutor: Send + Sync {
888    fn definition(&self) -> ToolDefinition;
889    fn call(&self, call: McpToolCall) -> ToolFuture;
890}
891
892struct RegisteredTool {
893    definition: ToolDefinition,
894    call: Arc<dyn Fn(McpToolCall) -> ToolFuture + Send + Sync>,
895}
896
897impl ToolExecutor for RegisteredTool {
898    fn definition(&self) -> ToolDefinition {
899        self.definition.clone()
900    }
901
902    fn call(&self, call: McpToolCall) -> ToolFuture {
903        (self.call)(call)
904    }
905}
906
907trait ResourceReader: Send + Sync {
908    fn definition(&self) -> ResourceDefinition;
909    fn read(&self) -> ResourceFuture;
910}
911
912struct RegisteredResource {
913    definition: ResourceDefinition,
914    read: Arc<dyn Fn() -> ResourceFuture + Send + Sync>,
915}
916
917impl ResourceReader for RegisteredResource {
918    fn definition(&self) -> ResourceDefinition {
919        self.definition.clone()
920    }
921
922    fn read(&self) -> ResourceFuture {
923        (self.read)()
924    }
925}
926
927trait PromptExecutor: Send + Sync {
928    fn definition(&self) -> PromptDefinition;
929    fn get(&self, arguments: Option<JsonObject>) -> PromptFuture;
930}
931
932struct RegisteredPrompt {
933    definition: PromptDefinition,
934    get: Arc<dyn Fn(Option<JsonObject>) -> PromptFuture + Send + Sync>,
935}
936
937impl PromptExecutor for RegisteredPrompt {
938    fn definition(&self) -> PromptDefinition {
939        self.definition.clone()
940    }
941
942    fn get(&self, arguments: Option<JsonObject>) -> PromptFuture {
943        (self.get)(arguments)
944    }
945}
946
947fn validate_tool_call_result(
948    tool_name: &str,
949    output_schema: Option<&JsonObject>,
950    result: ToolCallResult,
951) -> ToolCallResult {
952    let Some(output_schema) = output_schema else {
953        return result;
954    };
955    if result.is_error == Some(true) {
956        return result;
957    }
958
959    let Some(structured_content) = result.structured_content.as_ref() else {
960        return tool_error_result_for(McpToolError::invalid_tool_output(
961            tool_name,
962            "tool declares output_schema but returned no structured_content",
963        ));
964    };
965
966    let output_schema = Value::Object(output_schema.clone());
967    if let Err(error) = validate_value_against_closed_schema(
968        "structured_content",
969        &output_schema,
970        structured_content,
971    ) {
972        return tool_error_result_for(McpToolError::invalid_tool_output(
973            tool_name,
974            error.to_string(),
975        ));
976    }
977
978    result
979}
980
981fn block_on_tool_future(future: ToolFuture) -> ToolCallResult {
982    let join = std::thread::spawn(move || {
983        let runtime = tokio::runtime::Builder::new_current_thread()
984            .enable_all()
985            .build()?;
986        Ok::<_, Box<dyn std::error::Error + Send + Sync>>(runtime.block_on(future))
987    })
988    .join();
989
990    match join {
991        Ok(Ok(result)) => result,
992        Ok(Err(error)) => tool_error_result_for(McpToolError::handler(format!(
993            "failed to run async tool handler: {error}"
994        ))),
995        Err(_) => {
996            tool_error_result_for(McpToolError::handler("async tool handler runtime panicked"))
997        },
998    }
999}