1use std::collections::HashMap;
6use std::sync::Arc;
7
8use async_trait::async_trait;
9use serde_json::{Value, json};
10use tracing::debug;
11
12use crate::resource::{McpResource, resource_to_descriptor};
13
14use crate::{McpResult, SessionContext};
15use turul_mcp_protocol::{McpError, WithMeta};
16
17fn extract_limit_from_params(params: &Option<Value>) -> Option<usize> {
26 params.as_ref().and_then(|p| {
27 p.get("limit")
29 .or_else(|| p.get("_meta").and_then(|m| m.get("limit")))
30 .and_then(|v| v.as_u64())
31 .map(|n| n as usize)
32 })
33}
34
35#[async_trait]
37pub trait McpHandler: Send + Sync {
38 async fn handle(&self, params: Option<Value>) -> McpResult<Value>;
40
41 async fn handle_with_session(
43 &self,
44 params: Option<Value>,
45 _session: Option<SessionContext>,
46 ) -> McpResult<Value> {
47 self.handle(params).await
48 }
49
50 fn supported_methods(&self) -> Vec<String>;
52}
53
54pub struct PingHandler;
56
57#[async_trait]
58impl McpHandler for PingHandler {
59 async fn handle(&self, _params: Option<Value>) -> McpResult<Value> {
60 Ok(serde_json::json!({}))
61 }
62
63 fn supported_methods(&self) -> Vec<String> {
64 vec!["ping".to_string()]
65 }
66}
67
68pub struct CompletionHandler;
70
71#[async_trait]
72impl McpHandler for CompletionHandler {
73 async fn handle(&self, _params: Option<Value>) -> McpResult<Value> {
74 use turul_mcp_protocol::completion::{CompleteResult, CompletionResult};
75
76 let values = vec!["example1".to_string(), "example2".to_string()];
78
79 let completion_result = CompletionResult::new(values);
80 let response = CompleteResult::new(completion_result);
81 serde_json::to_value(response).map_err(McpError::from)
82 }
83
84 fn supported_methods(&self) -> Vec<String> {
85 vec!["completion/complete".to_string()]
86 }
87}
88
89pub struct PromptsListHandler {
91 prompts: HashMap<String, Arc<dyn McpPrompt>>,
92}
93
94impl Default for PromptsListHandler {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100impl PromptsListHandler {
101 pub fn new() -> Self {
102 Self {
103 prompts: HashMap::new(),
104 }
105 }
106
107 pub fn add_prompt<P: McpPrompt + 'static>(mut self, prompt: P) -> Self {
108 self.prompts
109 .insert(prompt.name().to_string(), Arc::new(prompt));
110 self
111 }
112
113 pub fn add_prompt_arc(mut self, prompt: Arc<dyn McpPrompt>) -> Self {
114 self.prompts.insert(prompt.name().to_string(), prompt);
115 self
116 }
117}
118
119#[async_trait]
120impl McpHandler for PromptsListHandler {
121 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
122 use turul_mcp_protocol::meta::{Cursor, PaginatedResponse};
124 use turul_mcp_protocol::prompts::{ListPromptsParams, ListPromptsResult, Prompt};
125
126 const DEFAULT_PAGE_SIZE: usize = 50;
129 const MAX_PAGE_SIZE: usize = 1000;
130 const MIN_PAGE_SIZE: usize = 1;
131
132 let page_size = match extract_limit_from_params(¶ms) {
133 Some(0) => {
134 return Err(McpError::InvalidParameters(
135 "limit must be at least 1 (zero would return empty pages forever)".to_string(),
136 ));
137 }
138 Some(n) => n.clamp(MIN_PAGE_SIZE, MAX_PAGE_SIZE),
139 None => DEFAULT_PAGE_SIZE,
140 };
141
142 let list_params = if let Some(params_value) = params {
144 serde_json::from_value::<ListPromptsParams>(params_value).map_err(|e| {
145 McpError::InvalidParameters(format!("Invalid parameters for prompts/list: {}", e))
146 })?
147 } else {
148 ListPromptsParams::new()
149 };
150
151 let cursor = list_params.cursor;
152
153 debug!(
154 "Listing prompts with cursor: {:?}, limit: {}",
155 cursor, page_size
156 );
157
158 let mut all_prompts: Vec<Prompt> = self
160 .prompts
161 .values()
162 .map(|p| {
163 let mut prompt = Prompt::new(p.name());
164 if let Some(desc) = p.description() {
165 prompt = prompt.with_description(desc);
166 }
167 if let Some(args) = p.arguments() {
169 prompt = prompt.with_arguments(args.clone());
170 }
171 prompt
172 })
173 .collect();
174
175 all_prompts.sort_by(|a, b| a.name.cmp(&b.name));
177
178 let start_index = if let Some(cursor) = &cursor {
180 let cursor_name = cursor.as_str();
182
183 all_prompts
185 .iter()
186 .position(|p| p.name.as_str() > cursor_name)
187 .unwrap_or(all_prompts.len())
188 } else {
189 0 };
191
192 let end_index = std::cmp::min(start_index + page_size, all_prompts.len());
194
195 let page_prompts: Vec<Prompt> = all_prompts[start_index..end_index].to_vec();
197
198 let has_more = end_index < all_prompts.len();
200
201 let next_cursor = if has_more {
203 page_prompts.last().map(|p| Cursor::new(&p.name))
205 } else {
206 None
207 };
208
209 debug!(
210 "Prompt pagination: start={}, end={}, page_size={}, has_more={}, next_cursor={:?}",
211 start_index,
212 end_index,
213 page_prompts.len(),
214 has_more,
215 next_cursor
216 );
217
218 let mut base_response = ListPromptsResult::new(page_prompts);
219
220 if let Some(ref cursor) = next_cursor {
222 base_response = base_response.with_next_cursor(cursor.clone());
223 }
224
225 let total = Some(all_prompts.len() as u64);
226
227 let next_cursor_clone = next_cursor.clone();
228 let mut paginated_response =
229 PaginatedResponse::with_pagination(base_response, next_cursor, total, has_more);
230
231 if let Some(request_meta) = list_params.meta {
233 let mut response_meta = paginated_response.meta().cloned().unwrap_or_else(|| {
235 turul_mcp_protocol::meta::Meta::with_pagination(next_cursor_clone, total, has_more)
236 });
237
238 for (key, value) in request_meta {
240 response_meta.extra.insert(key, value);
241 }
242
243 paginated_response = paginated_response.with_meta(response_meta);
244 }
245
246 serde_json::to_value(paginated_response).map_err(McpError::from)
247 }
248
249 fn supported_methods(&self) -> Vec<String> {
250 vec!["prompts/list".to_string()]
251 }
252}
253
254pub struct PromptsGetHandler {
256 prompts: HashMap<String, Arc<dyn McpPrompt>>,
257}
258
259impl Default for PromptsGetHandler {
260 fn default() -> Self {
261 Self::new()
262 }
263}
264
265impl PromptsGetHandler {
266 pub fn new() -> Self {
267 Self {
268 prompts: HashMap::new(),
269 }
270 }
271
272 pub fn add_prompt<P: McpPrompt + 'static>(mut self, prompt: P) -> Self {
273 self.prompts
274 .insert(prompt.name().to_string(), Arc::new(prompt));
275 self
276 }
277
278 pub fn add_prompt_arc(mut self, prompt: Arc<dyn McpPrompt>) -> Self {
279 self.prompts.insert(prompt.name().to_string(), prompt);
280 self
281 }
282}
283
284#[async_trait]
285impl McpHandler for PromptsGetHandler {
286 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
287 use std::collections::HashMap as StdHashMap;
288 use turul_mcp_protocol::prompts::{GetPromptParams, GetPromptResult};
289
290 let params = params.ok_or_else(|| McpError::missing_param("GetPromptParams"))?;
292 let get_params: GetPromptParams = serde_json::from_value(params)?;
293
294 debug!(
295 "Getting prompt: {} with arguments: {:?}",
296 get_params.name, get_params.arguments
297 );
298
299 let prompt = self.prompts.get(&get_params.name).ok_or_else(|| {
301 McpError::invalid_param_type("name", "existing prompt name", &get_params.name)
302 })?;
303
304 if let Some(prompt_arguments) = prompt.arguments() {
306 let empty_args = StdHashMap::new();
307 let provided_args = get_params.arguments.as_ref().unwrap_or(&empty_args);
308
309 for arg_def in prompt_arguments {
310 let is_required = arg_def.required.unwrap_or(false);
311 if is_required && !provided_args.contains_key(&arg_def.name) {
312 return Err(McpError::InvalidParameters(format!(
313 "Missing required argument '{}' for prompt '{}'",
314 arg_def.name, get_params.name
315 )));
316 }
317 }
318 }
319
320 let arguments = match get_params.arguments {
322 Some(args) => {
323 let mut value_args = StdHashMap::new();
324 for (key, value) in args {
325 value_args.insert(key, serde_json::Value::String(value));
326 }
327 value_args
328 }
329 None => StdHashMap::new(),
330 };
331
332 let messages = prompt.render(Some(arguments)).await?;
335
336 let mut response = GetPromptResult::new(messages);
338 if let Some(desc) = prompt.description() {
339 response = response.with_description(desc);
340 }
341
342 if let Some(meta) = get_params.meta {
344 response = response.with_meta(meta);
345 }
346
347 serde_json::to_value(response).map_err(McpError::from)
348 }
349
350 fn supported_methods(&self) -> Vec<String> {
351 vec!["prompts/get".to_string()]
352 }
353}
354
355pub type PromptsHandler = PromptsListHandler;
357
358pub use crate::prompt::McpPrompt;
360
361pub struct ResourcesListHandler {
363 resources: HashMap<String, Arc<dyn McpResource>>,
364}
365
366impl Default for ResourcesListHandler {
367 fn default() -> Self {
368 Self::new()
369 }
370}
371
372impl ResourcesListHandler {
373 pub fn new() -> Self {
374 Self {
375 resources: HashMap::new(),
376 }
377 }
378
379 pub fn add_resource<R: McpResource + 'static>(mut self, resource: R) -> Self {
380 self.resources
381 .insert(resource.uri().to_string(), Arc::new(resource));
382 self
383 }
384
385 pub fn add_resource_arc(mut self, resource: Arc<dyn McpResource>) -> Self {
386 self.resources.insert(resource.uri().to_string(), resource);
387 self
388 }
389}
390
391#[async_trait]
392impl McpHandler for ResourcesListHandler {
393 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
394 use turul_mcp_protocol::meta::{Cursor, PaginatedResponse};
395 use turul_mcp_protocol::resources::{ListResourcesParams, ListResourcesResult, Resource};
396
397 const DEFAULT_PAGE_SIZE: usize = 50;
400 const MAX_PAGE_SIZE: usize = 1000;
401 const MIN_PAGE_SIZE: usize = 1;
402
403 let page_size = match extract_limit_from_params(¶ms) {
404 Some(0) => {
405 return Err(McpError::InvalidParameters(
406 "limit must be at least 1 (zero would return empty pages forever)".to_string(),
407 ));
408 }
409 Some(n) => n.clamp(MIN_PAGE_SIZE, MAX_PAGE_SIZE),
410 None => DEFAULT_PAGE_SIZE,
411 };
412
413 let list_params = if let Some(params_value) = params {
415 serde_json::from_value::<ListResourcesParams>(params_value).map_err(|e| {
416 McpError::InvalidParameters(format!("Invalid parameters for resources/list: {}", e))
417 })?
418 } else {
419 ListResourcesParams::new()
420 };
421
422 let cursor = list_params.cursor;
423
424 debug!(
425 "Listing resources with cursor: {:?}, limit: {}",
426 cursor, page_size
427 );
428
429 let mut all_resources: Vec<Resource> = self
431 .resources
432 .values()
433 .map(|r| resource_to_descriptor(r.as_ref()))
434 .collect();
435
436 all_resources.sort_by(|a, b| a.uri.cmp(&b.uri));
438
439 let start_index = if let Some(cursor) = &cursor {
441 let cursor_uri = cursor.as_str();
443
444 all_resources
446 .iter()
447 .position(|r| r.uri.as_str() > cursor_uri)
448 .unwrap_or(all_resources.len())
449 } else {
450 0 };
452
453 let end_index = std::cmp::min(start_index + page_size, all_resources.len());
455
456 let page_resources: Vec<Resource> = all_resources[start_index..end_index].to_vec();
458
459 let has_more = end_index < all_resources.len();
461
462 let next_cursor = if has_more {
464 page_resources.last().map(|r| Cursor::new(&r.uri))
466 } else {
467 None
468 };
469
470 debug!(
471 "Resource pagination: start={}, end={}, page_size={}, has_more={}, next_cursor={:?}",
472 start_index,
473 end_index,
474 page_resources.len(),
475 has_more,
476 next_cursor
477 );
478
479 let mut base_response = ListResourcesResult::new(page_resources);
480
481 if let Some(ref cursor) = next_cursor {
483 base_response = base_response.with_next_cursor(cursor.clone());
484 }
485
486 let total = Some(all_resources.len() as u64);
487
488 let next_cursor_clone = next_cursor.clone();
489 let mut paginated_response =
490 PaginatedResponse::with_pagination(base_response, next_cursor, total, has_more);
491
492 if let Some(request_meta) = list_params.meta {
494 let mut response_meta = paginated_response.meta().cloned().unwrap_or_else(|| {
496 turul_mcp_protocol::meta::Meta::with_pagination(next_cursor_clone, total, has_more)
497 });
498
499 for (key, value) in request_meta {
501 response_meta.extra.insert(key, value);
502 }
503
504 paginated_response = paginated_response.with_meta(response_meta);
505 }
506
507 serde_json::to_value(paginated_response).map_err(McpError::from)
508 }
509
510 fn supported_methods(&self) -> Vec<String> {
511 vec!["resources/list".to_string()]
512 }
513}
514
515pub struct ResourcesReadHandler {
517 resources: HashMap<String, Arc<dyn McpResource>>,
518 uri_registry: Arc<crate::uri_template::UriTemplateRegistry>,
519 security_middleware: Option<Arc<crate::security::SecurityMiddleware>>,
520}
521
522impl Default for ResourcesReadHandler {
523 fn default() -> Self {
524 Self::new()
525 }
526}
527
528impl ResourcesReadHandler {
529 pub fn new() -> Self {
530 Self {
531 resources: HashMap::new(),
532 uri_registry: Arc::new(crate::uri_template::UriTemplateRegistry::new()),
533 security_middleware: Some(Arc::new(crate::security::SecurityMiddleware::default())),
534 }
535 }
536
537 pub fn with_security(mut self, middleware: Arc<crate::security::SecurityMiddleware>) -> Self {
539 self.security_middleware = Some(middleware);
540 self
541 }
542
543 pub fn without_security(mut self) -> Self {
545 self.security_middleware = None;
546 self
547 }
548
549 pub fn add_resource<R: McpResource + 'static>(mut self, resource: R) -> Self {
550 self.resources
551 .insert(resource.uri().to_string(), Arc::new(resource));
552 self
553 }
554
555 pub fn add_resource_arc(mut self, resource: Arc<dyn McpResource>) -> Self {
556 self.resources.insert(resource.uri().to_string(), resource);
557 self
558 }
559
560 pub fn add_template_resource<R: McpResource + 'static>(
562 mut self,
563 template: crate::uri_template::UriTemplate,
564 resource: R,
565 ) -> Self {
566 Arc::get_mut(&mut self.uri_registry)
568 .expect("URI registry should not be shared yet")
569 .register(template.clone());
570
571 let pattern = template.pattern().to_string();
573 self.resources.insert(pattern, Arc::new(resource));
574 self
575 }
576
577 pub fn add_template_resource_arc(
579 mut self,
580 template: crate::uri_template::UriTemplate,
581 resource: Arc<dyn McpResource>,
582 ) -> Self {
583 Arc::get_mut(&mut self.uri_registry)
585 .expect("URI registry should not be shared yet")
586 .register(template.clone());
587
588 let pattern = template.pattern().to_string();
590 self.resources.insert(pattern, resource);
591 self
592 }
593}
594
595#[async_trait]
596impl McpHandler for ResourcesReadHandler {
597 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
598 self.handle_with_session(params, None).await
600 }
601
602 async fn handle_with_session(
603 &self,
604 params: Option<Value>,
605 session: Option<SessionContext>,
606 ) -> McpResult<Value> {
607 use turul_mcp_protocol::resources::{ReadResourceParams, ReadResourceResult};
608
609 if let Some(security_middleware) = &self.security_middleware {
611 security_middleware.validate_request(
612 "resources/read",
613 params.as_ref(),
614 session.as_ref(),
615 )?;
616 }
617
618 let params = params.ok_or_else(|| McpError::missing_param("ReadResourceParams"))?;
620 let read_params: ReadResourceParams = serde_json::from_value(params)?;
621
622 debug!("Reading resource with URI: {}", read_params.uri);
623
624 if let Some(security_middleware) = &self.security_middleware {
626 let uri_params = serde_json::json!({"uri": read_params.uri});
628 security_middleware.validate_request(
629 "resources/read",
630 Some(&uri_params),
631 session.as_ref(),
632 )?;
633 }
634
635 if let Some(template) = self.uri_registry.find_matching(&read_params.uri) {
637 debug!("Found matching URI template: {}", template.pattern());
638
639 let template_vars = template.extract(&read_params.uri)?;
641 debug!("Extracted template variables: {:?}", template_vars);
642
643 let resource = self.resources.get(template.pattern()).ok_or_else(|| {
645 McpError::invalid_param_type(
646 "template",
647 "registered template pattern",
648 template.pattern(),
649 )
650 })?;
651
652 let mut enhanced_params = serde_json::to_value(&read_params)?;
654 if let Some(params_obj) = enhanced_params.as_object_mut() {
655 params_obj.insert(
656 "template_variables".to_string(),
657 serde_json::to_value(template_vars)?,
658 );
659 }
660
661 let contents = resource
662 .read(Some(enhanced_params), session.as_ref())
663 .await?;
664
665 if let Some(security_middleware) = &self.security_middleware {
667 for content in &contents {
668 match content {
669 turul_mcp_protocol::resources::ResourceContent::Text(text_content) => {
670 if let Some(mime_type) = &text_content.mime_type {
671 security_middleware
672 .resource_access_control()
673 .validate_mime_type(mime_type)?;
674 }
675 let size = text_content.text.len() as u64;
676 security_middleware
677 .resource_access_control()
678 .validate_size(size)?;
679 }
680 turul_mcp_protocol::resources::ResourceContent::Blob(blob_content) => {
681 if let Some(mime_type) = &blob_content.mime_type {
682 security_middleware
683 .resource_access_control()
684 .validate_mime_type(mime_type)?;
685 }
686 let size = blob_content.blob.len() as u64;
687 security_middleware
688 .resource_access_control()
689 .validate_size(size)?;
690 }
691 }
692 }
693 }
694
695 let response = ReadResourceResult::new(contents);
696 return serde_json::to_value(response).map_err(McpError::from);
697 }
698
699 let resource = self.resources.get(&read_params.uri).ok_or_else(|| {
701 McpError::invalid_param_type(
702 "uri",
703 "existing resource URI or template pattern",
704 &read_params.uri,
705 )
706 })?;
707
708 let params = Some(serde_json::to_value(&read_params)?);
710 let contents = resource.read(params, session.as_ref()).await?;
711
712 if let Some(security_middleware) = &self.security_middleware {
714 for content in &contents {
715 match content {
716 turul_mcp_protocol::resources::ResourceContent::Text(text_content) => {
717 if let Some(mime_type) = &text_content.mime_type {
718 security_middleware
719 .resource_access_control()
720 .validate_mime_type(mime_type)?;
721 }
722 let size = text_content.text.len() as u64;
723 security_middleware
724 .resource_access_control()
725 .validate_size(size)?;
726 }
727 turul_mcp_protocol::resources::ResourceContent::Blob(blob_content) => {
728 if let Some(mime_type) = &blob_content.mime_type {
729 security_middleware
730 .resource_access_control()
731 .validate_mime_type(mime_type)?;
732 }
733 let size = blob_content.blob.len() as u64;
734 security_middleware
735 .resource_access_control()
736 .validate_size(size)?;
737 }
738 }
739 }
740 }
741
742 let response = ReadResourceResult::new(contents);
743 serde_json::to_value(response).map_err(McpError::from)
744 }
745
746 fn supported_methods(&self) -> Vec<String> {
747 vec!["resources/read".to_string()]
748 }
749}
750
751pub type ResourcesHandler = ResourcesListHandler;
753
754pub struct LoggingHandler;
756
757#[async_trait]
758impl McpHandler for LoggingHandler {
759 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
760 use turul_mcp_protocol::logging::SetLevelParams;
762
763 if let Some(params) = params {
764 let set_level_params: SetLevelParams = serde_json::from_value(params)?;
765
766 tracing::warn!(
768 "LoggingHandler.handle() called without session context - cannot store level per-session"
769 );
770 tracing::info!("Would set log level to: {:?}", set_level_params.level);
771
772 serde_json::to_value(json!({})).map_err(McpError::from)
774 } else {
775 Err(McpError::missing_param("SetLevelParams"))
776 }
777 }
778
779 async fn handle_with_session(
780 &self,
781 params: Option<Value>,
782 session: Option<SessionContext>,
783 ) -> McpResult<Value> {
784 use turul_mcp_protocol::logging::SetLevelParams;
785
786 let params = params.ok_or_else(|| McpError::missing_param("params"))?;
788
789 let set_level_params: SetLevelParams = serde_json::from_value(params)?;
790
791 let session_ctx = session
793 .ok_or_else(|| McpError::configuration("Session required for logging/setLevel"))?;
794
795 if !(session_ctx.is_initialized)().await {
797 return Err(McpError::configuration(
798 "Session must be initialized before setting logging level",
799 ));
800 }
801
802 session_ctx.set_logging_level(set_level_params.level).await;
804
805 tracing::info!(
806 "đ¯ Set logging level for session {}: {:?}",
807 session_ctx.session_id,
808 set_level_params.level
809 );
810
811 let stored_level = session_ctx.get_logging_level().await;
813 if stored_level != set_level_params.level {
814 return Err(McpError::configuration(
815 "Failed to persist logging level in session storage",
816 ));
817 }
818
819 session_ctx
821 .notify_log(
822 turul_mcp_protocol::logging::LoggingLevel::Info,
823 serde_json::json!(format!(
824 "Logging level changed to: {:?}",
825 set_level_params.level
826 )),
827 None,
828 None,
829 )
830 .await;
831
832 Ok(json!({}))
834 }
835
836 fn supported_methods(&self) -> Vec<String> {
837 vec!["logging/setLevel".to_string()]
838 }
839}
840
841pub struct RootsHandler {
843 roots: Vec<turul_mcp_protocol::roots::Root>,
844}
845
846impl Default for RootsHandler {
847 fn default() -> Self {
848 Self::new()
849 }
850}
851
852impl RootsHandler {
853 pub fn new() -> Self {
854 Self { roots: Vec::new() }
855 }
856
857 pub fn add_root(mut self, root: turul_mcp_protocol::roots::Root) -> Self {
858 self.roots.push(root);
859 self
860 }
861}
862
863#[async_trait]
864impl McpHandler for RootsHandler {
865 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
866 use turul_mcp_protocol::meta::{Cursor, PaginatedResponse};
867 use turul_mcp_protocol::roots::ListRootsResult;
868
869 let cursor = params
871 .as_ref()
872 .and_then(|p| p.get("cursor"))
873 .and_then(|c| c.as_str())
874 .map(Cursor::from);
875
876 debug!("Listing roots with cursor: {:?}", cursor);
877
878 let mut all_roots = self.roots.clone();
880 all_roots.sort_by(|a, b| a.uri.cmp(&b.uri));
881
882 const DEFAULT_PAGE_SIZE: usize = 50; let page_size = DEFAULT_PAGE_SIZE;
885
886 let start_index = if let Some(cursor) = &cursor {
888 let cursor_uri = cursor.as_str();
890
891 all_roots
893 .iter()
894 .position(|r| r.uri.as_str() > cursor_uri)
895 .unwrap_or(all_roots.len())
896 } else {
897 0 };
899
900 let end_index = std::cmp::min(start_index + page_size, all_roots.len());
902
903 let page_roots = all_roots[start_index..end_index].to_vec();
905
906 let has_more = end_index < all_roots.len();
908
909 let next_cursor = if has_more {
911 page_roots.last().map(|r| Cursor::new(&r.uri))
913 } else {
914 None
915 };
916
917 debug!(
918 "Root pagination: start={}, end={}, page_size={}, has_more={}, next_cursor={:?}",
919 start_index,
920 end_index,
921 page_roots.len(),
922 has_more,
923 next_cursor
924 );
925
926 let base_response = ListRootsResult::new(page_roots);
927
928 let total = Some(all_roots.len() as u64);
931
932 let paginated_response =
933 PaginatedResponse::with_pagination(base_response, next_cursor, total, has_more);
934
935 serde_json::to_value(paginated_response).map_err(McpError::from)
936 }
937
938 fn supported_methods(&self) -> Vec<String> {
939 vec!["roots/list".to_string()]
940 }
941}
942
943pub struct SamplingHandler;
945
946#[async_trait]
947impl McpHandler for SamplingHandler {
948 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
949 use turul_mcp_protocol::meta::{ProgressResponse, ProgressToken};
950 use turul_mcp_protocol::sampling::{CreateMessageResult, SamplingMessage};
951
952 let progress_token = params
954 .as_ref()
955 .and_then(|p| p.get("progressToken"))
956 .and_then(|t| t.as_str())
957 .map(ProgressToken::from);
958
959 let message = SamplingMessage {
961 role: turul_mcp_protocol::sampling::Role::Assistant,
962 content: turul_mcp_protocol::prompts::ContentBlock::text(
963 "This is a sample message generated by the MCP server",
964 ),
965 };
966
967 let base_response = CreateMessageResult {
968 message,
969 model: "mock-model-v1".to_string(),
970 stop_reason: Some("stop".to_string()),
971 meta: None,
972 };
973
974 let progress_response = ProgressResponse::with_progress(
977 base_response,
978 progress_token.or_else(|| Some(ProgressToken::new("sampling-default"))),
979 1.0, Some(1),
981 Some(1),
982 );
983
984 serde_json::to_value(progress_response).map_err(McpError::from)
985 }
986
987 fn supported_methods(&self) -> Vec<String> {
988 vec!["sampling/createMessage".to_string()]
989 }
990}
991
992pub struct ProvidedSamplingHandler {
998 providers: HashMap<String, Arc<dyn crate::McpSampling>>,
999}
1000
1001impl ProvidedSamplingHandler {
1002 pub fn new(providers: HashMap<String, Arc<dyn crate::McpSampling>>) -> Self {
1003 Self { providers }
1004 }
1005}
1006
1007#[async_trait]
1008impl McpHandler for ProvidedSamplingHandler {
1009 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
1010 use turul_mcp_protocol::meta::{ProgressResponse, ProgressToken};
1011 use turul_mcp_protocol::sampling::{CreateMessageParams, CreateMessageRequest};
1012
1013 let progress_token = params
1015 .as_ref()
1016 .and_then(|p| p.get("progressToken"))
1017 .and_then(|t| t.as_str())
1018 .map(ProgressToken::from);
1019
1020 let message_params: CreateMessageParams =
1022 serde_json::from_value(params.ok_or_else(|| McpError::missing_param("params"))?)?;
1023
1024 let request = CreateMessageRequest {
1026 method: "sampling/createMessage".to_string(),
1027 params: message_params,
1028 };
1029
1030 let provider = self
1032 .providers
1033 .values()
1034 .next()
1035 .ok_or_else(|| McpError::configuration("No sampling provider available"))?;
1036
1037 provider.validate_request(&request).await?;
1039
1040 let result = provider.sample(request).await?;
1042
1043 let progress_response = ProgressResponse::with_progress(
1045 result,
1046 progress_token.or_else(|| Some(ProgressToken::new("sampling-provided"))),
1047 1.0, Some(1),
1049 Some(1),
1050 );
1051
1052 serde_json::to_value(progress_response).map_err(McpError::from)
1053 }
1054
1055 fn supported_methods(&self) -> Vec<String> {
1056 vec!["sampling/createMessage".to_string()]
1057 }
1058}
1059
1060pub struct ResourceTemplatesHandler {
1062 templates: Vec<(crate::uri_template::UriTemplate, Arc<dyn McpResource>)>,
1063}
1064
1065impl Default for ResourceTemplatesHandler {
1066 fn default() -> Self {
1067 Self::new()
1068 }
1069}
1070
1071impl ResourceTemplatesHandler {
1072 pub fn new() -> Self {
1073 Self {
1074 templates: Vec::new(),
1075 }
1076 }
1077
1078 pub fn with_templates(
1079 mut self,
1080 templates: Vec<(crate::uri_template::UriTemplate, Arc<dyn McpResource>)>,
1081 ) -> Self {
1082 self.templates = templates;
1083 self
1084 }
1085}
1086
1087#[async_trait]
1088impl McpHandler for ResourceTemplatesHandler {
1089 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
1090 use turul_mcp_protocol::meta::Cursor;
1091
1092 const DEFAULT_PAGE_SIZE: usize = 50;
1095 const MAX_PAGE_SIZE: usize = 1000;
1096 const MIN_PAGE_SIZE: usize = 1;
1097
1098 let page_size = match extract_limit_from_params(¶ms) {
1099 Some(0) => {
1100 return Err(McpError::InvalidParameters(
1101 "limit must be at least 1 (zero would return empty pages forever)".to_string(),
1102 ));
1103 }
1104 Some(n) => n.clamp(MIN_PAGE_SIZE, MAX_PAGE_SIZE),
1105 None => DEFAULT_PAGE_SIZE,
1106 };
1107
1108 use turul_mcp_protocol::resources::ListResourceTemplatesParams;
1110 let list_params = if let Some(params_value) = params {
1111 serde_json::from_value::<ListResourceTemplatesParams>(params_value).map_err(|e| {
1112 McpError::InvalidParameters(format!(
1113 "Invalid parameters for resources/templates/list: {}",
1114 e
1115 ))
1116 })?
1117 } else {
1118 ListResourceTemplatesParams::new()
1119 };
1120
1121 let cursor = list_params.cursor;
1122 debug!(
1123 "Listing resource templates with cursor: {:?}, limit: {}",
1124 cursor, page_size
1125 );
1126
1127 tracing::info!(
1128 "Resource templates list requested - {} templates registered",
1129 self.templates.len()
1130 );
1131
1132 use turul_mcp_protocol::resources::{ListResourceTemplatesResult, ResourceTemplate};
1133
1134 let mut all_templates: Vec<ResourceTemplate> = self
1136 .templates
1137 .iter()
1138 .map(|(uri_template, resource)| {
1139 let template_name = resource.name();
1140 let mut template = ResourceTemplate::new(template_name, uri_template.pattern());
1141 if let Some(desc) = resource.description() {
1142 template = template.with_description(desc);
1143 }
1144 if let Some(mime_type) = resource.mime_type() {
1146 template = template.with_mime_type(mime_type);
1147 }
1148 template
1149 })
1150 .collect();
1151
1152 all_templates.sort_by(|a, b| a.uri_template.cmp(&b.uri_template));
1154
1155 let start_index = if let Some(cursor) = &cursor {
1157 let cursor_template = cursor.as_str();
1159
1160 all_templates
1162 .iter()
1163 .position(|t| t.uri_template.as_str() > cursor_template)
1164 .unwrap_or(all_templates.len())
1165 } else {
1166 0 };
1168
1169 let end_index = std::cmp::min(start_index + page_size, all_templates.len());
1171
1172 let page_templates = all_templates[start_index..end_index].to_vec();
1174
1175 let total = Some(all_templates.len() as u64);
1177 let has_more = end_index < all_templates.len();
1178 let next_cursor = if has_more {
1179 page_templates.last().map(|t| Cursor::new(&t.uri_template))
1181 } else {
1182 None };
1184
1185 debug!(
1186 "Resource template pagination: page_size={}, has_more={}, next_cursor={:?}",
1187 page_templates.len(),
1188 has_more,
1189 next_cursor
1190 );
1191
1192 let mut base_response = ListResourceTemplatesResult::new(page_templates);
1193
1194 if let Some(ref cursor) = next_cursor {
1196 base_response = base_response.with_next_cursor(cursor.clone());
1197 }
1198
1199 use turul_mcp_protocol::meta::PaginatedResponse;
1200 let next_cursor_clone = next_cursor.clone();
1201 let mut paginated_response =
1202 PaginatedResponse::with_pagination(base_response, next_cursor, total, has_more);
1203
1204 if let Some(request_meta) = list_params.meta {
1206 let mut response_meta = paginated_response.meta().cloned().unwrap_or_else(|| {
1208 turul_mcp_protocol::meta::Meta::with_pagination(next_cursor_clone, total, has_more)
1209 });
1210
1211 for (key, value) in request_meta {
1213 response_meta.extra.insert(key, value);
1214 }
1215
1216 paginated_response = paginated_response.with_meta(response_meta);
1217 }
1218
1219 serde_json::to_value(paginated_response).map_err(McpError::from)
1220 }
1221
1222 fn supported_methods(&self) -> Vec<String> {
1223 vec!["resources/templates/list".to_string()]
1224 }
1225}
1226
1227#[async_trait]
1232pub trait ElicitationProvider: Send + Sync {
1233 async fn elicit(
1235 &self,
1236 request: &turul_mcp_protocol_2025_06_18::elicitation::ElicitCreateRequest,
1237 ) -> McpResult<turul_mcp_protocol_2025_06_18::elicitation::ElicitResult>;
1238
1239 fn can_handle(
1241 &self,
1242 _request: &turul_mcp_protocol_2025_06_18::elicitation::ElicitCreateRequest,
1243 ) -> bool {
1244 true
1246 }
1247}
1248
1249pub struct MockElicitationProvider;
1251
1252#[async_trait]
1253impl ElicitationProvider for MockElicitationProvider {
1254 async fn elicit(
1255 &self,
1256 request: &turul_mcp_protocol_2025_06_18::elicitation::ElicitCreateRequest,
1257 ) -> McpResult<turul_mcp_protocol_2025_06_18::elicitation::ElicitResult> {
1258 use turul_mcp_protocol::elicitation::ElicitResult;
1259
1260 let mut mock_data = std::collections::HashMap::new();
1262 mock_data.insert("mock_response".to_string(), serde_json::json!(true));
1263 mock_data.insert(
1264 "message".to_string(),
1265 serde_json::json!(&request.params.message),
1266 );
1267 mock_data.insert(
1268 "note".to_string(),
1269 serde_json::json!("This is a mock elicitation response for testing"),
1270 );
1271
1272 match request.params.message.as_str() {
1274 msg if msg.contains("cancel") => Ok(ElicitResult::cancel()),
1275 msg if msg.contains("decline") => Ok(ElicitResult::decline()),
1276 _ => Ok(ElicitResult::accept(mock_data)),
1277 }
1278 }
1279
1280 fn can_handle(
1281 &self,
1282 _request: &turul_mcp_protocol_2025_06_18::elicitation::ElicitCreateRequest,
1283 ) -> bool {
1284 true }
1286}
1287
1288pub struct ElicitationHandler {
1290 provider: Arc<dyn ElicitationProvider>,
1291}
1292
1293impl ElicitationHandler {
1294 pub fn new(provider: Arc<dyn ElicitationProvider>) -> Self {
1295 Self { provider }
1296 }
1297
1298 pub fn with_mock_provider() -> Self {
1299 Self::new(Arc::new(MockElicitationProvider))
1300 }
1301}
1302
1303#[async_trait]
1304impl McpHandler for ElicitationHandler {
1305 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
1306 use turul_mcp_protocol::elicitation::ElicitCreateParams;
1307
1308 if let Some(params) = params {
1309 let request_params: ElicitCreateParams = serde_json::from_value(params)?;
1310
1311 tracing::info!("Processing elicitation request: {}", request_params.message);
1312
1313 use turul_mcp_protocol::elicitation::ElicitCreateRequest;
1315 let create_request = ElicitCreateRequest {
1316 method: "elicitation/create".to_string(),
1317 params: request_params.clone(),
1318 };
1319
1320 if !self.provider.can_handle(&create_request) {
1322 let error_response =
1323 turul_mcp_protocol_2025_06_18::elicitation::ElicitResult::cancel();
1324 return serde_json::to_value(error_response).map_err(McpError::from);
1325 }
1326
1327 let result = self.provider.elicit(&create_request).await?;
1329
1330 serde_json::to_value(result).map_err(McpError::from)
1331 } else {
1332 Err(McpError::missing_param("ElicitCreateParams"))
1333 }
1334 }
1335
1336 fn supported_methods(&self) -> Vec<String> {
1337 vec!["elicitation/create".to_string()]
1338 }
1339}
1340
1341use crate::session::SessionManager;
1342
1343pub struct NotificationsHandler;
1345
1346#[async_trait]
1347impl McpHandler for NotificationsHandler {
1348 async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
1349 tracing::info!("Received notification: {:?}", params);
1351 Ok(Value::Null)
1352 }
1353
1354 fn supported_methods(&self) -> Vec<String> {
1355 vec![
1356 "notifications/message".to_string(),
1357 "notifications/progress".to_string(),
1358 "notifications/resources/listChanged".to_string(),
1359 "notifications/resources/updated".to_string(),
1360 "notifications/tools/listChanged".to_string(),
1361 "notifications/prompts/listChanged".to_string(),
1362 "notifications/roots/listChanged".to_string(),
1363 ]
1364 }
1365}
1366
1367pub struct InitializedNotificationHandler {
1369 session_manager: Arc<SessionManager>,
1370}
1371
1372impl InitializedNotificationHandler {
1373 pub fn new(session_manager: Arc<SessionManager>) -> Self {
1374 Self { session_manager }
1375 }
1376}
1377
1378#[async_trait]
1379impl McpHandler for InitializedNotificationHandler {
1380 async fn handle(&self, _params: Option<Value>) -> McpResult<Value> {
1381 tracing::warn!("notifications/initialized received without session context");
1383 Ok(Value::Null)
1384 }
1385
1386 async fn handle_with_session(
1387 &self,
1388 params: Option<Value>,
1389 session: Option<SessionContext>,
1390 ) -> McpResult<Value> {
1391 tracing::info!("đ¨ Received notifications/initialized: {:?}", params);
1392
1393 if let Some(session_ctx) = session {
1394 tracing::info!(
1395 "đ Processing notifications/initialized for session: {}",
1396 session_ctx.session_id
1397 );
1398
1399 if self
1401 .session_manager
1402 .is_session_initialized(&session_ctx.session_id)
1403 .await
1404 {
1405 tracing::info!(
1406 "âšī¸ Session {} already initialized, ignoring duplicate notifications/initialized",
1407 session_ctx.session_id
1408 );
1409 return Ok(Value::Null);
1410 }
1411
1412 let client_info_value = self
1414 .session_manager
1415 .get_session_state(&session_ctx.session_id, "client_info")
1416 .await;
1417 let capabilities_value = self
1418 .session_manager
1419 .get_session_state(&session_ctx.session_id, "client_capabilities")
1420 .await;
1421 let negotiated_version_value = self
1422 .session_manager
1423 .get_session_state(&session_ctx.session_id, "negotiated_version")
1424 .await;
1425
1426 if let (
1427 Some(client_info_value),
1428 Some(capabilities_value),
1429 Some(negotiated_version_value),
1430 ) = (
1431 client_info_value,
1432 capabilities_value,
1433 negotiated_version_value,
1434 ) {
1435 use turul_mcp_protocol::{ClientCapabilities, Implementation, McpVersion};
1437
1438 if let (Ok(client_info), Ok(client_capabilities), Ok(negotiated_version)) = (
1439 serde_json::from_value::<Implementation>(client_info_value),
1440 serde_json::from_value::<ClientCapabilities>(capabilities_value),
1441 serde_json::from_value::<McpVersion>(negotiated_version_value),
1442 ) {
1443 if let Err(e) = self
1445 .session_manager
1446 .initialize_session_with_version(
1447 &session_ctx.session_id,
1448 client_info,
1449 client_capabilities,
1450 negotiated_version,
1451 )
1452 .await
1453 {
1454 tracing::error!(
1455 "â Failed to initialize session {}: {}",
1456 session_ctx.session_id,
1457 e
1458 );
1459 return Err(turul_mcp_protocol::McpError::configuration(&format!(
1460 "Failed to initialize session: {}",
1461 e
1462 )));
1463 }
1464
1465 tracing::info!(
1466 "â
Session {} successfully initialized after receiving notifications/initialized",
1467 session_ctx.session_id
1468 );
1469 } else {
1470 tracing::error!(
1471 "â Failed to deserialize stored client info/capabilities/version for session {}",
1472 session_ctx.session_id
1473 );
1474 return Err(turul_mcp_protocol::McpError::configuration(
1475 "Failed to deserialize stored client info",
1476 ));
1477 }
1478 } else {
1479 tracing::error!(
1480 "â Missing stored client info/capabilities/version for session {}",
1481 session_ctx.session_id
1482 );
1483 return Err(turul_mcp_protocol::McpError::configuration(
1484 "Missing stored client info - session must call initialize first",
1485 ));
1486 }
1487 } else {
1488 tracing::warn!("â ī¸ notifications/initialized received without session context");
1489 }
1490
1491 Ok(Value::Null)
1492 }
1493
1494 fn supported_methods(&self) -> Vec<String> {
1495 vec!["notifications/initialized".to_string()]
1496 }
1497}