1use super::*;
2
3#[derive(Clone, Default)]
12pub struct McpToolRegistry {
13 tools: BTreeMap<String, Arc<dyn ToolExecutor>>,
14}
15
16impl McpToolRegistry {
17 #[must_use]
19 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn add_tool<Call>(
30 &mut self,
31 definition: ToolDefinition,
32 call: Call,
33 ) -> Result<(), McpToolError>
34 where
35 Call: Fn(McpToolCall) -> ToolCallResult + Send + Sync + 'static,
36 {
37 let name = definition.name.to_string();
38 let output_validator =
39 definitions::validate_tool_definition_with_output(&definition)?.map(Arc::new);
40 if self.tools.contains_key(&name) {
41 return Err(McpToolError::duplicate_tool(name));
42 }
43
44 self.tools.insert(
45 name,
46 Arc::new(RegisteredTool {
47 definition,
48 output_validator,
49 call: Arc::new(move |arguments| Box::pin(std::future::ready(call(arguments)))),
50 }),
51 );
52 Ok(())
53 }
54
55 pub fn add_typed_tool<Input, Call>(
62 &mut self,
63 definition: McpTypedTool<Input>,
64 call: Call,
65 ) -> Result<(), McpToolError>
66 where
67 Input: McpToolInput,
68 Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
69 {
70 self.add_tool(definition.into_definition(), move |tool_call| {
71 let input = match Input::from_tool_call(tool_call) {
72 Ok(input) => input,
73 Err(error) => return tool_error_result_for(error),
74 };
75 call(input)
76 })
77 }
78
79 pub fn add_koruma_tool<Input, Call>(
115 &mut self,
116 definition: McpTypedTool<Input>,
117 call: Call,
118 ) -> Result<(), McpToolError>
119 where
120 Input: McpToolInput + koruma_core::ValidateExt,
121 Input::Error: koruma_core::ValidationIssues,
122 Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
123 {
124 self.add_typed_tool(definition, move |input| match validate_koruma(&input) {
125 Ok(()) => call(input),
126 Err(error) => tool_error_result_for(error),
127 })
128 }
129
130 pub fn add_tool_async<Call, Fut>(
137 &mut self,
138 definition: ToolDefinition,
139 call: Call,
140 ) -> Result<(), McpToolError>
141 where
142 Call: Fn(McpToolCall) -> Fut + Send + Sync + 'static,
143 Fut: Future<Output = ToolCallResult> + Send + 'static,
144 {
145 let name = definition.name.to_string();
146 let output_validator =
147 definitions::validate_tool_definition_with_output(&definition)?.map(Arc::new);
148 if self.tools.contains_key(&name) {
149 return Err(McpToolError::duplicate_tool(name));
150 }
151
152 self.tools.insert(
153 name,
154 Arc::new(RegisteredTool {
155 definition,
156 output_validator,
157 call: Arc::new(move |arguments| Box::pin(call(arguments))),
158 }),
159 );
160 Ok(())
161 }
162
163 pub fn add_typed_tool_async<Input, Call, Fut>(
170 &mut self,
171 definition: McpTypedTool<Input>,
172 call: Call,
173 ) -> Result<(), McpToolError>
174 where
175 Input: McpToolInput,
176 Call: Fn(Input) -> Fut + Send + Sync + 'static,
177 Fut: Future<Output = ToolCallResult> + Send + 'static,
178 {
179 self.add_tool_async(definition.into_definition(), move |tool_call| {
180 let input = Input::from_tool_call(tool_call);
181 let future = match input {
182 Ok(input) => call(input),
183 Err(error) => {
184 return Box::pin(std::future::ready(tool_error_result_for(error)))
185 as ToolFuture;
186 },
187 };
188 Box::pin(future) as ToolFuture
189 })
190 }
191
192 pub fn add_koruma_tool_async<Input, Call, Fut>(
201 &mut self,
202 definition: McpTypedTool<Input>,
203 call: Call,
204 ) -> Result<(), McpToolError>
205 where
206 Input: McpToolInput + koruma_core::ValidateExt,
207 Input::Error: koruma_core::ValidationIssues,
208 Call: Fn(Input) -> Fut + Send + Sync + 'static,
209 Fut: Future<Output = ToolCallResult> + Send + 'static,
210 {
211 self.add_typed_tool_async(definition, move |input| match validate_koruma(&input) {
212 Ok(()) => Box::pin(call(input)) as ToolFuture,
213 Err(error) => Box::pin(std::future::ready(tool_error_result_for(error))) as ToolFuture,
214 })
215 }
216
217 #[must_use]
219 pub fn list_tools(&self) -> Vec<ToolDefinition> {
220 self.tools
221 .values()
222 .map(|executor| executor.definition())
223 .collect()
224 }
225
226 #[must_use]
228 pub fn contains_tool(&self, name: &str) -> bool {
229 self.tools.contains_key(name)
230 }
231
232 #[must_use]
234 pub fn tool_count(&self) -> usize {
235 self.tools.len()
236 }
237
238 #[must_use]
241 pub fn call_tool(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
242 let call = match McpToolCall::from_value(arguments) {
243 Ok(call) => call,
244 Err(error) => return tool_error_result_for(error),
245 };
246 let call = match self.resolve_tool(name, call) {
247 Ok(resolved) => resolved,
248 Err(error) => return tool_error_result_for(error),
249 };
250 block_on_tool_future(call)
251 }
252
253 pub async fn call_tool_async(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
256 let call = match McpToolCall::from_value(arguments) {
257 Ok(call) => call,
258 Err(error) => return tool_error_result_for(error),
259 };
260 let call = match self.resolve_tool(name, call) {
261 Ok(resolved) => resolved,
262 Err(error) => return tool_error_result_for(error),
263 };
264 call.await
265 }
266
267 fn definition(&self, name: &str) -> Option<ToolDefinition> {
268 self.tools.get(name).map(|executor| executor.definition())
269 }
270
271 fn resolve_tool(&self, name: &str, call: McpToolCall) -> Result<ToolFuture, McpToolError> {
272 match self.tools.get(name) {
273 Some(executor) => Ok(executor.call(call)),
274 None => Err(McpToolError::UnknownTool {
275 name: name.to_string(),
276 }),
277 }
278 }
279}
280
281#[derive(Clone)]
283pub struct McpServer {
284 pub(crate) server_name: Cow<'static, str>,
285 pub(crate) server_version: Cow<'static, str>,
286 tools: McpToolRegistry,
287 resources: BTreeMap<String, Arc<dyn ResourceReader>>,
288 resource_templates: Vec<ResourceTemplate>,
289 prompts: BTreeMap<String, Arc<dyn PromptExecutor>>,
290}
291
292impl McpServer {
293 pub fn new(
295 server_name: impl Into<Cow<'static, str>>,
296 server_version: impl Into<Cow<'static, str>>,
297 ) -> Self {
298 Self {
299 server_name: server_name.into(),
300 server_version: server_version.into(),
301 tools: McpToolRegistry::new(),
302 resources: BTreeMap::new(),
303 resource_templates: Vec::new(),
304 prompts: BTreeMap::new(),
305 }
306 }
307
308 pub fn builder(
310 server_name: impl Into<Cow<'static, str>>,
311 server_version: impl Into<Cow<'static, str>>,
312 ) -> McpServerBuilder {
313 McpServerBuilder::new(server_name, server_version)
314 }
315
316 #[must_use]
318 pub fn from_tool_registry(
319 server_name: impl Into<Cow<'static, str>>,
320 server_version: impl Into<Cow<'static, str>>,
321 tools: McpToolRegistry,
322 ) -> Self {
323 Self {
324 tools,
325 ..Self::new(server_name, server_version)
326 }
327 }
328
329 #[must_use]
331 pub fn tool_registry(&self) -> &McpToolRegistry {
332 &self.tools
333 }
334
335 pub fn tool_registry_mut(&mut self) -> &mut McpToolRegistry {
337 &mut self.tools
338 }
339
340 #[must_use]
342 pub fn into_tool_registry(self) -> McpToolRegistry {
343 self.tools
344 }
345
346 pub fn add_tool<Call>(
353 &mut self,
354 definition: ToolDefinition,
355 call: Call,
356 ) -> Result<(), McpToolError>
357 where
358 Call: Fn(McpToolCall) -> ToolCallResult + Send + Sync + 'static,
359 {
360 self.tools.add_tool(definition, call)
361 }
362
363 pub fn add_typed_tool<Input, Call>(
370 &mut self,
371 definition: McpTypedTool<Input>,
372 call: Call,
373 ) -> Result<(), McpToolError>
374 where
375 Input: McpToolInput,
376 Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
377 {
378 self.tools.add_typed_tool(definition, call)
379 }
380
381 pub fn add_koruma_tool<Input, Call>(
387 &mut self,
388 definition: McpTypedTool<Input>,
389 call: Call,
390 ) -> Result<(), McpToolError>
391 where
392 Input: McpToolInput + koruma_core::ValidateExt,
393 Input::Error: koruma_core::ValidationIssues,
394 Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
395 {
396 self.tools.add_koruma_tool(definition, call)
397 }
398
399 pub fn add_tool_async<Call, Fut>(
406 &mut self,
407 definition: ToolDefinition,
408 call: Call,
409 ) -> Result<(), McpToolError>
410 where
411 Call: Fn(McpToolCall) -> Fut + Send + Sync + 'static,
412 Fut: Future<Output = ToolCallResult> + Send + 'static,
413 {
414 self.tools.add_tool_async(definition, call)
415 }
416
417 pub fn add_typed_tool_async<Input, Call, Fut>(
424 &mut self,
425 definition: McpTypedTool<Input>,
426 call: Call,
427 ) -> Result<(), McpToolError>
428 where
429 Input: McpToolInput,
430 Call: Fn(Input) -> Fut + Send + Sync + 'static,
431 Fut: Future<Output = ToolCallResult> + Send + 'static,
432 {
433 self.tools.add_typed_tool_async(definition, call)
434 }
435
436 pub fn add_koruma_tool_async<Input, Call, Fut>(
442 &mut self,
443 definition: McpTypedTool<Input>,
444 call: Call,
445 ) -> Result<(), McpToolError>
446 where
447 Input: McpToolInput + koruma_core::ValidateExt,
448 Input::Error: koruma_core::ValidationIssues,
449 Call: Fn(Input) -> Fut + Send + Sync + 'static,
450 Fut: Future<Output = ToolCallResult> + Send + 'static,
451 {
452 self.tools.add_koruma_tool_async(definition, call)
453 }
454
455 #[must_use]
457 pub fn list_tools(&self) -> Vec<ToolDefinition> {
458 self.tools.list_tools()
459 }
460
461 #[must_use]
463 pub fn contains_tool(&self, name: &str) -> bool {
464 self.tools.contains_tool(name)
465 }
466
467 #[must_use]
469 pub fn tool_count(&self) -> usize {
470 self.tools.tool_count()
471 }
472
473 pub fn add_resource<Read>(
480 &mut self,
481 definition: ResourceDefinition,
482 read: Read,
483 ) -> Result<(), McpToolError>
484 where
485 Read: Fn() -> ReadResourceResult + Send + Sync + 'static,
486 {
487 self.add_resource_async(definition, move || {
488 let result = read();
489 std::future::ready(Ok(result))
490 })
491 }
492
493 pub fn add_resource_async<Read, Fut>(
500 &mut self,
501 definition: ResourceDefinition,
502 read: Read,
503 ) -> Result<(), McpToolError>
504 where
505 Read: Fn() -> Fut + Send + Sync + 'static,
506 Fut: Future<Output = Result<ReadResourceResult, ErrorData>> + Send + 'static,
507 {
508 let uri = definition.uri.clone();
509 validate_resource_definition(&definition)?;
510 if self.resources.contains_key(&uri) {
511 return Err(McpToolError::duplicate_resource(uri));
512 }
513
514 self.resources.insert(
515 uri,
516 Arc::new(RegisteredResource {
517 definition,
518 read: Arc::new(move || Box::pin(read())),
519 }),
520 );
521 Ok(())
522 }
523
524 pub fn add_resource_template(
530 &mut self,
531 definition: ResourceTemplateDefinition,
532 ) -> Result<(), McpToolError> {
533 validate_resource_template(&definition)?;
534 self.resource_templates.push(definition);
535 Ok(())
536 }
537
538 pub fn list_resources(&self) -> Vec<ResourceDefinition> {
540 self.resources
541 .values()
542 .map(|resource| resource.definition())
543 .collect()
544 }
545
546 pub fn list_resource_templates(&self) -> Vec<ResourceTemplateDefinition> {
548 self.resource_templates.clone()
549 }
550
551 pub fn contains_resource(&self, uri: &str) -> bool {
553 self.resources.contains_key(uri)
554 }
555
556 pub fn resource_count(&self) -> usize {
558 self.resources.len()
559 }
560
561 pub fn add_prompt<Get>(
568 &mut self,
569 definition: PromptDefinition,
570 get: Get,
571 ) -> Result<(), McpToolError>
572 where
573 Get: Fn(Option<JsonObject>) -> GetPromptResult + Send + Sync + 'static,
574 {
575 self.add_prompt_async(definition, move |arguments| {
576 let result = get(arguments);
577 std::future::ready(Ok(result))
578 })
579 }
580
581 pub fn add_prompt_async<Get, Fut>(
588 &mut self,
589 definition: PromptDefinition,
590 get: Get,
591 ) -> Result<(), McpToolError>
592 where
593 Get: Fn(Option<JsonObject>) -> Fut + Send + Sync + 'static,
594 Fut: Future<Output = Result<GetPromptResult, ErrorData>> + Send + 'static,
595 {
596 let name = definition.name.clone();
597 validate_prompt_definition(&definition)?;
598 if self.prompts.contains_key(&name) {
599 return Err(McpToolError::duplicate_prompt(name));
600 }
601
602 self.prompts.insert(
603 name,
604 Arc::new(RegisteredPrompt {
605 definition,
606 get: Arc::new(move |arguments| Box::pin(get(arguments))),
607 }),
608 );
609 Ok(())
610 }
611
612 pub fn list_prompts(&self) -> Vec<PromptDefinition> {
614 self.prompts
615 .values()
616 .map(|prompt| prompt.definition())
617 .collect()
618 }
619
620 pub fn contains_prompt(&self, name: &str) -> bool {
622 self.prompts.contains_key(name)
623 }
624
625 pub fn prompt_count(&self) -> usize {
627 self.prompts.len()
628 }
629
630 #[must_use]
633 pub fn call_tool(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
634 self.tools.call_tool(name, arguments)
635 }
636
637 pub async fn call_tool_async(&self, name: &str, arguments: Option<Value>) -> ToolCallResult {
640 self.tools.call_tool_async(name, arguments).await
641 }
642
643 pub async fn serve_stdio(self) -> ServeStdioResult {
649 let service = self.serve(stdio()).await?;
650 service.waiting().await?;
651 Ok(())
652 }
653
654 pub fn serve_stdio_blocking(self) -> ServeStdioResult {
661 let runtime = tokio::runtime::Builder::new_multi_thread()
662 .enable_all()
663 .build()?;
664 runtime.block_on(self.serve_stdio())
665 }
666
667 fn result_meta(&self) -> MetaObject {
668 let mut meta = MetaObject::default();
669 meta.0.insert(
670 "io.modelcontextprotocol/serverInfo".to_string(),
671 json!({
672 "name": self.server_name,
673 "version": self.server_version,
674 }),
675 );
676 meta
677 }
678
679 fn prepare_cacheable_result(
680 &self,
681 protocol_version: Option<&ProtocolVersion>,
682 ttl_ms: &mut Option<u64>,
683 cache_scope: &mut Option<CacheScope>,
684 meta: &mut Option<MetaObject>,
685 ) {
686 if protocol_version.is_some_and(|version| version >= &ProtocolVersion::V_2026_07_28) {
687 *ttl_ms = Some(0);
688 *cache_scope = Some(CacheScope::Private);
689 }
690 *meta = Some(self.result_meta());
691 }
692}
693
694#[derive(Clone)]
696pub struct McpServerBuilder {
697 server: Result<McpServer, McpToolError>,
698}
699
700impl McpServerBuilder {
701 pub fn new(
703 server_name: impl Into<Cow<'static, str>>,
704 server_version: impl Into<Cow<'static, str>>,
705 ) -> Self {
706 Self {
707 server: Ok(McpServer::new(server_name, server_version)),
708 }
709 }
710
711 pub fn register<Register>(mut self, register: Register) -> Self
713 where
714 Register: FnOnce(&mut McpServer) -> Result<(), McpToolError>,
715 {
716 if let Ok(server) = self.server.as_mut()
717 && let Err(error) = register(server)
718 {
719 self.server = Err(error);
720 }
721 self
722 }
723
724 pub fn tool<Call>(self, definition: ToolDefinition, call: Call) -> Self
726 where
727 Call: Fn(McpToolCall) -> ToolCallResult + Send + Sync + 'static,
728 {
729 self.register(move |server| server.add_tool(definition, call))
730 }
731
732 pub fn typed_tool<Input, Call>(self, definition: McpTypedTool<Input>, call: Call) -> Self
734 where
735 Input: McpToolInput,
736 Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
737 {
738 self.register(move |server| server.add_typed_tool(definition, call))
739 }
740
741 pub fn koruma_tool<Input, Call>(self, definition: McpTypedTool<Input>, call: Call) -> Self
743 where
744 Input: McpToolInput + koruma_core::ValidateExt,
745 Input::Error: koruma_core::ValidationIssues,
746 Call: Fn(Input) -> ToolCallResult + Send + Sync + 'static,
747 {
748 self.register(move |server| server.add_koruma_tool(definition, call))
749 }
750
751 pub fn tool_async<Call, Fut>(self, definition: ToolDefinition, call: Call) -> Self
753 where
754 Call: Fn(McpToolCall) -> Fut + Send + Sync + 'static,
755 Fut: Future<Output = ToolCallResult> + Send + 'static,
756 {
757 self.register(move |server| server.add_tool_async(definition, call))
758 }
759
760 pub fn typed_tool_async<Input, Call, Fut>(
762 self,
763 definition: McpTypedTool<Input>,
764 call: Call,
765 ) -> Self
766 where
767 Input: McpToolInput,
768 Call: Fn(Input) -> Fut + Send + Sync + 'static,
769 Fut: Future<Output = ToolCallResult> + Send + 'static,
770 {
771 self.register(move |server| server.add_typed_tool_async(definition, call))
772 }
773
774 pub fn koruma_tool_async<Input, Call, Fut>(
776 self,
777 definition: McpTypedTool<Input>,
778 call: Call,
779 ) -> Self
780 where
781 Input: McpToolInput + koruma_core::ValidateExt,
782 Input::Error: koruma_core::ValidationIssues,
783 Call: Fn(Input) -> Fut + Send + Sync + 'static,
784 Fut: Future<Output = ToolCallResult> + Send + 'static,
785 {
786 self.register(move |server| server.add_koruma_tool_async(definition, call))
787 }
788
789 pub fn resource<Read>(self, definition: ResourceDefinition, read: Read) -> Self
791 where
792 Read: Fn() -> ReadResourceResult + Send + Sync + 'static,
793 {
794 self.register(move |server| server.add_resource(definition, read))
795 }
796
797 pub fn resource_async<Read, Fut>(self, definition: ResourceDefinition, read: Read) -> Self
799 where
800 Read: Fn() -> Fut + Send + Sync + 'static,
801 Fut: Future<Output = Result<ReadResourceResult, ErrorData>> + Send + 'static,
802 {
803 self.register(move |server| server.add_resource_async(definition, read))
804 }
805
806 pub fn resource_template(self, definition: ResourceTemplateDefinition) -> Self {
808 self.register(move |server| server.add_resource_template(definition))
809 }
810
811 pub fn prompt<Get>(self, definition: PromptDefinition, get: Get) -> Self
813 where
814 Get: Fn(Option<JsonObject>) -> GetPromptResult + Send + Sync + 'static,
815 {
816 self.register(move |server| server.add_prompt(definition, get))
817 }
818
819 pub fn prompt_async<Get, Fut>(self, definition: PromptDefinition, get: Get) -> Self
821 where
822 Get: Fn(Option<JsonObject>) -> Fut + Send + Sync + 'static,
823 Fut: Future<Output = Result<GetPromptResult, ErrorData>> + Send + 'static,
824 {
825 self.register(move |server| server.add_prompt_async(definition, get))
826 }
827
828 pub fn build(self) -> Result<McpServer, McpToolError> {
834 self.server
835 }
836
837 pub async fn serve_stdio(self) -> ServeStdioResult {
844 self.build()?.serve_stdio().await
845 }
846
847 pub fn serve_stdio_blocking(self) -> ServeStdioResult {
854 self.build()?.serve_stdio_blocking()
855 }
856}
857
858impl ServerHandler for McpServer {
859 fn get_info(&self) -> ServerConfig {
860 let mut capabilities = ServerCapabilities::builder().enable_tools().build();
861 capabilities.resources = (!self.resources.is_empty()
862 || !self.resource_templates.is_empty())
863 .then(Default::default);
864 capabilities.prompts = (!self.prompts.is_empty()).then(Default::default);
865 ServerConfig::new(capabilities)
866 .with_protocol_version(ProtocolVersion::V_2026_07_28)
867 .with_server_info(Implementation::new(
868 self.server_name.clone(),
869 self.server_version.clone(),
870 ))
871 }
872
873 fn list_tools(
874 &self,
875 _request: Option<PaginatedRequestParams>,
876 context: RequestContext<RoleServer>,
877 ) -> impl Future<Output = Result<ListToolsResult, ErrorData>> + MaybeSendFuture + '_ {
878 let mut result = ListToolsResult::with_all_items(self.list_tools());
879 self.prepare_cacheable_result(
880 context.protocol_version().as_ref(),
881 &mut result.ttl_ms,
882 &mut result.cache_scope,
883 &mut result.meta,
884 );
885 std::future::ready(Ok(result))
886 }
887
888 fn get_tool(&self, name: &str) -> Option<Tool> {
889 self.tools.definition(name)
890 }
891
892 fn call_tool(
893 &self,
894 request: CallToolRequestParams,
895 _context: RequestContext<RoleServer>,
896 ) -> impl Future<Output = Result<CallToolResponse, ErrorData>> + MaybeSendFuture + '_ {
897 let name = request.name.to_string();
898 let call = McpToolCall::new(request.arguments.unwrap_or_default());
899 let call = match self.tools.resolve_tool(&name, call) {
900 Ok(call) => call,
901 Err(error) => {
902 let result = tool_error_result_for(error);
903 Box::pin(std::future::ready(result))
904 },
905 };
906 let result_meta = self.result_meta();
907 async move {
908 let mut result = call.await;
909 result.meta = Some(result_meta);
910 Ok(result.into())
911 }
912 }
913
914 fn list_resources(
915 &self,
916 _request: Option<PaginatedRequestParams>,
917 context: RequestContext<RoleServer>,
918 ) -> impl Future<Output = Result<ListResourcesResult, ErrorData>> + MaybeSendFuture + '_ {
919 let mut result = ListResourcesResult::with_all_items(self.list_resources());
920 self.prepare_cacheable_result(
921 context.protocol_version().as_ref(),
922 &mut result.ttl_ms,
923 &mut result.cache_scope,
924 &mut result.meta,
925 );
926 std::future::ready(Ok(result))
927 }
928
929 fn list_resource_templates(
930 &self,
931 _request: Option<PaginatedRequestParams>,
932 context: RequestContext<RoleServer>,
933 ) -> impl Future<Output = Result<ListResourceTemplatesResult, ErrorData>> + MaybeSendFuture + '_
934 {
935 let mut result =
936 ListResourceTemplatesResult::with_all_items(self.list_resource_templates());
937 self.prepare_cacheable_result(
938 context.protocol_version().as_ref(),
939 &mut result.ttl_ms,
940 &mut result.cache_scope,
941 &mut result.meta,
942 );
943 std::future::ready(Ok(result))
944 }
945
946 fn read_resource(
947 &self,
948 request: ReadResourceRequestParams,
949 context: RequestContext<RoleServer>,
950 ) -> impl Future<Output = Result<ReadResourceResponse, ErrorData>> + MaybeSendFuture + '_ {
951 let uri = request.uri;
952 let read = self.resources.get(&uri).map(|resource| resource.read());
953 let protocol_version = context.protocol_version();
954 let result_meta = self.result_meta();
955 async move {
956 match read {
957 Some(read) => {
958 let mut result = read.await?;
959 if protocol_version
960 .as_ref()
961 .is_some_and(|version| version >= &ProtocolVersion::V_2026_07_28)
962 {
963 result.ttl_ms = Some(0);
964 result.cache_scope = Some(CacheScope::Private);
965 }
966 result.meta = Some(result_meta);
967 Ok(result.into())
968 },
969 None => Err(ErrorData::resource_not_found(
970 format!("resource `{uri}` not found"),
971 Some(McpToolError::unknown_resource(uri).to_structured_value()),
972 )),
973 }
974 }
975 }
976
977 fn list_prompts(
978 &self,
979 _request: Option<PaginatedRequestParams>,
980 context: RequestContext<RoleServer>,
981 ) -> impl Future<Output = Result<ListPromptsResult, ErrorData>> + MaybeSendFuture + '_ {
982 let mut result = ListPromptsResult::with_all_items(self.list_prompts());
983 self.prepare_cacheable_result(
984 context.protocol_version().as_ref(),
985 &mut result.ttl_ms,
986 &mut result.cache_scope,
987 &mut result.meta,
988 );
989 std::future::ready(Ok(result))
990 }
991
992 fn get_prompt(
993 &self,
994 request: GetPromptRequestParams,
995 _context: RequestContext<RoleServer>,
996 ) -> impl Future<Output = Result<GetPromptResponse, ErrorData>> + MaybeSendFuture + '_ {
997 let name = request.name;
998 let get = self
999 .prompts
1000 .get(&name)
1001 .map(|prompt| prompt.get(request.arguments));
1002 let result_meta = self.result_meta();
1003 async move {
1004 match get {
1005 Some(get) => {
1006 let mut result = get.await?;
1007 result.meta = Some(result_meta);
1008 Ok(result.into())
1009 },
1010 None => Err(ErrorData::invalid_params(
1011 format!("prompt `{name}` not found"),
1012 Some(McpToolError::unknown_prompt(name).to_structured_value()),
1013 )),
1014 }
1015 }
1016 }
1017}
1018
1019trait ToolExecutor: Send + Sync {
1020 fn definition(&self) -> ToolDefinition;
1021 fn call(&self, call: McpToolCall) -> ToolFuture;
1022}
1023
1024struct RegisteredTool {
1025 definition: ToolDefinition,
1026 output_validator: Option<Arc<jsonschema::Validator>>,
1027 call: Arc<dyn Fn(McpToolCall) -> ToolFuture + Send + Sync>,
1028}
1029
1030impl ToolExecutor for RegisteredTool {
1031 fn definition(&self) -> ToolDefinition {
1032 self.definition.clone()
1033 }
1034
1035 fn call(&self, call: McpToolCall) -> ToolFuture {
1036 let future = (self.call)(call);
1037 let Some(validator) = self.output_validator.clone() else {
1038 return future;
1039 };
1040 let name = self.definition.name.clone();
1041 Box::pin(async move { validate_tool_call_result(&name, &validator, future.await) })
1042 }
1043}
1044
1045trait ResourceReader: Send + Sync {
1046 fn definition(&self) -> ResourceDefinition;
1047 fn read(&self) -> ResourceFuture;
1048}
1049
1050struct RegisteredResource {
1051 definition: ResourceDefinition,
1052 read: Arc<dyn Fn() -> ResourceFuture + Send + Sync>,
1053}
1054
1055impl ResourceReader for RegisteredResource {
1056 fn definition(&self) -> ResourceDefinition {
1057 self.definition.clone()
1058 }
1059
1060 fn read(&self) -> ResourceFuture {
1061 (self.read)()
1062 }
1063}
1064
1065trait PromptExecutor: Send + Sync {
1066 fn definition(&self) -> PromptDefinition;
1067 fn get(&self, arguments: Option<JsonObject>) -> PromptFuture;
1068}
1069
1070struct RegisteredPrompt {
1071 definition: PromptDefinition,
1072 get: Arc<dyn Fn(Option<JsonObject>) -> PromptFuture + Send + Sync>,
1073}
1074
1075impl PromptExecutor for RegisteredPrompt {
1076 fn definition(&self) -> PromptDefinition {
1077 self.definition.clone()
1078 }
1079
1080 fn get(&self, arguments: Option<JsonObject>) -> PromptFuture {
1081 (self.get)(arguments)
1082 }
1083}
1084
1085fn validate_tool_call_result(
1086 tool_name: &str,
1087 output_validator: &jsonschema::Validator,
1088 result: ToolCallResult,
1089) -> ToolCallResult {
1090 if result.is_error == Some(true) {
1091 return result;
1092 }
1093
1094 let Some(structured_content) = result.structured_content.as_ref() else {
1095 return tool_error_result_for(McpToolError::invalid_tool_output(
1096 tool_name,
1097 "tool declares output_schema but returned no structured_content",
1098 ));
1099 };
1100
1101 if let Err(error) = output_validator.validate(structured_content) {
1102 return tool_error_result_for(McpToolError::invalid_tool_output(
1103 tool_name,
1104 format!(
1105 "structured_content{}: {}",
1106 error.instance_path(),
1107 error.masked()
1108 ),
1109 ));
1110 }
1111
1112 result
1113}
1114
1115fn block_on_tool_future(future: ToolFuture) -> ToolCallResult {
1116 let join = std::thread::spawn(move || {
1117 let runtime = tokio::runtime::Builder::new_current_thread()
1118 .enable_all()
1119 .build()?;
1120 Ok::<_, Box<dyn std::error::Error + Send + Sync>>(runtime.block_on(future))
1121 })
1122 .join();
1123
1124 match join {
1125 Ok(Ok(result)) => result,
1126 Ok(Err(error)) => tool_error_result_for(McpToolError::handler(format!(
1127 "failed to run async tool handler: {error}"
1128 ))),
1129 Err(_) => {
1130 tool_error_result_for(McpToolError::handler("async tool handler runtime panicked"))
1131 },
1132 }
1133}