turul_mcp_server/handlers/
mod.rs

1//! MCP Handler System
2//!
3//! This module provides a standardized handler system for MCP endpoints.
4
5use 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
17//pub mod response;
18//pub use response::*;
19
20/// Extract limit parameter from raw params (supports both params.limit and params._meta.limit)
21///
22/// MCP spec allows arbitrary extension fields via `[key: string]: unknown` in both
23/// params root and _meta. This helper extracts limit from either location before
24/// parsing to typed params.
25fn extract_limit_from_params(params: &Option<Value>) -> Option<usize> {
26    params.as_ref().and_then(|p| {
27        // Try direct params.limit first
28        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/// Generic MCP handler trait
36#[async_trait]
37pub trait McpHandler: Send + Sync {
38    /// Handle an MCP request
39    async fn handle(&self, params: Option<Value>) -> McpResult<Value>;
40
41    /// Handle an MCP request with session context (default implementation calls handle)
42    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    /// Get the methods this handler supports
51    fn supported_methods(&self) -> Vec<String>;
52}
53
54/// Ping handler for ping endpoint
55pub 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
68/// Completion handler for completion/complete endpoint
69pub 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        // Default implementation - can be overridden by users
77        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
89/// Prompts list handler for prompts/list endpoint only
90pub 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        // Handle prompts/list with pagination support
123        use turul_mcp_protocol::meta::{Cursor, PaginatedResponse};
124        use turul_mcp_protocol::prompts::{ListPromptsParams, ListPromptsResult, Prompt};
125
126        // Extract limit from raw params before parsing to typed params (MCP extension field)
127        // Clamp to 1000 for DoS protection, reject zero
128        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(&params) {
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        // Parse typed parameters with proper error handling (MCP compliance)
143        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        // Convert all prompts and sort by name for stable ordering
159        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                // Include arguments from the prompt object
168                if let Some(args) = p.arguments() {
169                    prompt = prompt.with_arguments(args.clone());
170                }
171                prompt
172            })
173            .collect();
174
175        // Sort by name to ensure stable pagination ordering (MCP 2025-06-18 requirement)
176        all_prompts.sort_by(|a, b| a.name.cmp(&b.name));
177
178        // Find starting index based on cursor
179        let start_index = if let Some(cursor) = &cursor {
180            // Cursor contains the last name from previous page
181            let cursor_name = cursor.as_str();
182
183            // Find the position after the cursor name
184            all_prompts
185                .iter()
186                .position(|p| p.name.as_str() > cursor_name)
187                .unwrap_or(all_prompts.len())
188        } else {
189            0 // No cursor = start from beginning
190        };
191
192        // Calculate end index for this page
193        let end_index = std::cmp::min(start_index + page_size, all_prompts.len());
194
195        // Extract page of prompts
196        let page_prompts: Vec<Prompt> = all_prompts[start_index..end_index].to_vec();
197
198        // Determine if there are more prompts after this page
199        let has_more = end_index < all_prompts.len();
200
201        // Generate next cursor if there are more prompts
202        let next_cursor = if has_more {
203            // Cursor should be the name of the last item in current page
204            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        // Set top-level nextCursor field on the result before wrapping
221        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        // Propagate optional _meta from request to response (MCP 2025-06-18 compliance)
232        if let Some(request_meta) = list_params.meta {
233            // Get existing meta from PaginatedResponse or use pagination defaults
234            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            // Merge request's _meta fields into extra without clobbering pagination
239            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
254/// Prompts get handler for prompts/get endpoint only
255pub 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        // Parse get prompt parameters
291        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        // Find the prompt by name
300        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        // Validate required arguments against prompt definition (MCP 2025-06-18 compliance)
305        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        // Convert arguments from HashMap<String, String> to HashMap<String, Value> if needed
321        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        // Generate prompt messages using the prompt implementation
333        // Note: MCP 2025-06-18 spec enforces only 'user' and 'assistant' roles via Role enum - no 'system' role
334        let messages = prompt.render(Some(arguments)).await?;
335
336        // Create response with messages
337        let mut response = GetPromptResult::new(messages);
338        if let Some(desc) = prompt.description() {
339            response = response.with_description(desc);
340        }
341
342        // Propagate optional _meta from request to response (MCP 2025-06-18 compliance)
343        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
355/// Legacy handler for backward compatibility - use PromptsListHandler instead
356pub type PromptsHandler = PromptsListHandler;
357
358/// Import the proper McpPrompt trait from the prompt module
359pub use crate::prompt::McpPrompt;
360
361/// Resources list handler for resources/list endpoint only
362pub 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        // Extract limit from raw params before parsing to typed params (MCP extension field)
398        // Clamp to 1000 for DoS protection, reject zero
399        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(&params) {
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        // Parse typed parameters with proper error handling (MCP compliance)
414        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        // Convert all resources to descriptors and sort by URI for stable ordering
430        let mut all_resources: Vec<Resource> = self
431            .resources
432            .values()
433            .map(|r| resource_to_descriptor(r.as_ref()))
434            .collect();
435
436        // Sort by URI to ensure stable pagination ordering (MCP 2025-06-18 requirement)
437        all_resources.sort_by(|a, b| a.uri.cmp(&b.uri));
438
439        // Find starting index based on cursor
440        let start_index = if let Some(cursor) = &cursor {
441            // Cursor contains the last URI from previous page
442            let cursor_uri = cursor.as_str();
443
444            // Find the position after the cursor URI
445            all_resources
446                .iter()
447                .position(|r| r.uri.as_str() > cursor_uri)
448                .unwrap_or(all_resources.len())
449        } else {
450            0 // No cursor = start from beginning
451        };
452
453        // Calculate end index for this page
454        let end_index = std::cmp::min(start_index + page_size, all_resources.len());
455
456        // Extract page of resources
457        let page_resources: Vec<Resource> = all_resources[start_index..end_index].to_vec();
458
459        // Determine if there are more resources after this page
460        let has_more = end_index < all_resources.len();
461
462        // Generate next cursor if there are more resources
463        let next_cursor = if has_more {
464            // Cursor should be the URI of the last item in current page
465            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        // Set top-level nextCursor field on the result before wrapping
482        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        // Propagate optional _meta from request to response (MCP 2025-06-18 compliance)
493        if let Some(request_meta) = list_params.meta {
494            // Get existing meta from PaginatedResponse or use pagination defaults
495            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            // Merge request's _meta fields into extra without clobbering pagination
500            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
515/// Resources read handler for resources/read endpoint only
516pub 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    /// Create handler with custom security middleware
538    pub fn with_security(mut self, middleware: Arc<crate::security::SecurityMiddleware>) -> Self {
539        self.security_middleware = Some(middleware);
540        self
541    }
542
543    /// Create handler without security middleware (for testing or trusted environments)
544    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    /// Add a dynamic resource with URI template support
561    pub fn add_template_resource<R: McpResource + 'static>(
562        mut self,
563        template: crate::uri_template::UriTemplate,
564        resource: R,
565    ) -> Self {
566        // Register the template in the registry
567        Arc::get_mut(&mut self.uri_registry)
568            .expect("URI registry should not be shared yet")
569            .register(template.clone());
570
571        // Store the resource using the template pattern as key
572        let pattern = template.pattern().to_string();
573        self.resources.insert(pattern, Arc::new(resource));
574        self
575    }
576
577    /// Add a dynamic resource with URI template support (Arc version)
578    pub fn add_template_resource_arc(
579        mut self,
580        template: crate::uri_template::UriTemplate,
581        resource: Arc<dyn McpResource>,
582    ) -> Self {
583        // Register the template in the registry
584        Arc::get_mut(&mut self.uri_registry)
585            .expect("URI registry should not be shared yet")
586            .register(template.clone());
587
588        // Store the resource using the template pattern as key
589        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        // Delegate to handle_with_session with no session
599        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        // Security validation
610        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        // Parse read resource parameters
619        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        // Additional security validation for the specific URI
625        if let Some(security_middleware) = &self.security_middleware {
626            // Re-validate the URI after parsing (defense in depth)
627            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        // First try to match against URI templates
636        if let Some(template) = self.uri_registry.find_matching(&read_params.uri) {
637            debug!("Found matching URI template: {}", template.pattern());
638
639            // Extract variables from the URI
640            let template_vars = template.extract(&read_params.uri)?;
641            debug!("Extracted template variables: {:?}", template_vars);
642
643            // Find the resource by template pattern
644            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            // Create enhanced params with template variables
653            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            // Validate content before returning
666            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        // Fall back to exact URI matching
700        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        // Call the resource's read method with original params
709        let params = Some(serde_json::to_value(&read_params)?);
710        let contents = resource.read(params, session.as_ref()).await?;
711
712        // Validate content before returning
713        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
751/// Legacy handler for backward compatibility - use ResourcesListHandler instead
752pub type ResourcesHandler = ResourcesListHandler;
753
754/// Logging handler for logging/setLevel endpoint
755pub struct LoggingHandler;
756
757#[async_trait]
758impl McpHandler for LoggingHandler {
759    async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
760        // Fallback for when no session is provided
761        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            // Without session context, just log the request but can't store per-session
767            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            // MCP logging/setLevel doesn't return data, just success
773            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        // Parse params - returns InvalidParams (-32602) if fails
787        let params = params.ok_or_else(|| McpError::missing_param("params"))?;
788
789        let set_level_params: SetLevelParams = serde_json::from_value(params)?;
790
791        // Require session - returns configuration error if missing
792        let session_ctx = session
793            .ok_or_else(|| McpError::configuration("Session required for logging/setLevel"))?;
794
795        // Check initialization - returns configuration error if not initialized
796        if !(session_ctx.is_initialized)().await {
797            return Err(McpError::configuration(
798                "Session must be initialized before setting logging level",
799            ));
800        }
801
802        // Set the level
803        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        // Verify persistence - returns configuration error if fails
812        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        // Send confirmation notification
820        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        // Success returns empty object per MCP spec
833        Ok(json!({}))
834    }
835
836    fn supported_methods(&self) -> Vec<String> {
837        vec!["logging/setLevel".to_string()]
838    }
839}
840
841/// Roots handler for roots/list endpoint
842pub 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        // Parse cursor from params if provided
870        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        // Sort roots by URI for stable ordering
879        let mut all_roots = self.roots.clone();
880        all_roots.sort_by(|a, b| a.uri.cmp(&b.uri));
881
882        // Implement cursor-based pagination
883        const DEFAULT_PAGE_SIZE: usize = 50; // MCP suggested default
884        let page_size = DEFAULT_PAGE_SIZE;
885
886        // Find starting index based on cursor
887        let start_index = if let Some(cursor) = &cursor {
888            // Cursor contains the last URI from previous page
889            let cursor_uri = cursor.as_str();
890
891            // Find the position after the cursor URI
892            all_roots
893                .iter()
894                .position(|r| r.uri.as_str() > cursor_uri)
895                .unwrap_or(all_roots.len())
896        } else {
897            0 // No cursor = start from beginning
898        };
899
900        // Calculate end index for this page
901        let end_index = std::cmp::min(start_index + page_size, all_roots.len());
902
903        // Extract page of roots
904        let page_roots = all_roots[start_index..end_index].to_vec();
905
906        // Determine if there are more roots after this page
907        let has_more = end_index < all_roots.len();
908
909        // Generate next cursor if there are more roots
910        let next_cursor = if has_more {
911            // Cursor should be the URI of the last item in current page
912            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        // Note: ListRootsResult doesn't have next_cursor field - roots may not be paginatable per MCP spec
929
930        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
943/// Sampling handler for sampling/createMessage endpoint (default/mock implementation)
944pub 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        // Parse progress token if provided
953        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        // Default implementation - return a simple message
960        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        // Add progress metadata for message generation operations
975        // In a real implementation, progress would track token generation steps
976        let progress_response = ProgressResponse::with_progress(
977            base_response,
978            progress_token.or_else(|| Some(ProgressToken::new("sampling-default"))),
979            1.0, // Complete since we're returning immediately
980            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
992/// Sampling handler that dispatches to registered sampling providers
993///
994/// This handler validates requests and dispatches to actual McpSampling
995/// implementations registered via .sampling_provider(). It replaces the
996/// default SamplingHandler when providers are configured.
997pub 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        // Extract progress token if provided
1014        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        // Parse params into CreateMessageParams
1021        let message_params: CreateMessageParams =
1022            serde_json::from_value(params.ok_or_else(|| McpError::missing_param("params"))?)?;
1023
1024        // Construct full CreateMessageRequest for the provider
1025        let request = CreateMessageRequest {
1026            method: "sampling/createMessage".to_string(),
1027            params: message_params,
1028        };
1029
1030        // Select provider - use first for now (can enhance with can_handle/priority later)
1031        let provider = self
1032            .providers
1033            .values()
1034            .next()
1035            .ok_or_else(|| McpError::configuration("No sampling provider available"))?;
1036
1037        // Validate request - THIS is where maxTokens=0 gets caught!
1038        provider.validate_request(&request).await?;
1039
1040        // Generate message using the provider
1041        let result = provider.sample(request).await?;
1042
1043        // Wrap in progress response for consistency with default handler
1044        let progress_response = ProgressResponse::with_progress(
1045            result,
1046            progress_token.or_else(|| Some(ProgressToken::new("sampling-provided"))),
1047            1.0, // Complete since we're returning immediately
1048            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
1060/// Resource templates handler for resources/templates/list endpoint
1061pub 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        // Extract limit from raw params before parsing to typed params (MCP extension field)
1093        // Clamp to 1000 for DoS protection, reject zero
1094        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(&params) {
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        // Parse typed parameters with proper error handling (MCP compliance)
1109        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        // Convert registered templates to ResourceTemplate objects and sort by template for stable ordering
1135        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                // Add MIME type if the resource provides it
1145                if let Some(mime_type) = resource.mime_type() {
1146                    template = template.with_mime_type(mime_type);
1147                }
1148                template
1149            })
1150            .collect();
1151
1152        // Sort by uri_template to ensure stable pagination ordering (MCP 2025-06-18 requirement)
1153        all_templates.sort_by(|a, b| a.uri_template.cmp(&b.uri_template));
1154
1155        // Find starting index based on cursor
1156        let start_index = if let Some(cursor) = &cursor {
1157            // Cursor contains the last uri_template from previous page
1158            let cursor_template = cursor.as_str();
1159
1160            // Find the position after the cursor template
1161            all_templates
1162                .iter()
1163                .position(|t| t.uri_template.as_str() > cursor_template)
1164                .unwrap_or(all_templates.len())
1165        } else {
1166            0 // No cursor = start from beginning
1167        };
1168
1169        // Calculate end index for this page
1170        let end_index = std::cmp::min(start_index + page_size, all_templates.len());
1171
1172        // Extract the page
1173        let page_templates = all_templates[start_index..end_index].to_vec();
1174
1175        // Calculate pagination metadata
1176        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            // Next cursor is the last template name in this page
1180            page_templates.last().map(|t| Cursor::new(&t.uri_template))
1181        } else {
1182            None // No more pages
1183        };
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        // Set top-level nextCursor field on the result before wrapping
1195        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        // Propagate optional _meta from request to response (MCP 2025-06-18 compliance)
1205        if let Some(request_meta) = list_params.meta {
1206            // Get existing meta from PaginatedResponse or use pagination defaults
1207            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            // Merge request's _meta fields into extra without clobbering pagination
1212            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/// Trait for custom elicitation UI implementations
1228///
1229/// This trait enables server implementations to provide custom user interfaces
1230/// for collecting structured input from users via JSON Schema-defined forms.
1231#[async_trait]
1232pub trait ElicitationProvider: Send + Sync {
1233    /// Present an elicitation request to the user and return their response
1234    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    /// Check if this provider can handle a specific elicitation schema
1240    fn can_handle(
1241        &self,
1242        _request: &turul_mcp_protocol_2025_06_18::elicitation::ElicitCreateRequest,
1243    ) -> bool {
1244        // Default implementation accepts all requests
1245        true
1246    }
1247}
1248
1249/// Default console-based elicitation provider for demonstration
1250pub 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        // Mock implementation based on message content
1261        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        // Simple mock logic based on message content
1273        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 // Mock provider handles all requests
1285    }
1286}
1287
1288/// Elicitation handler for elicitation/create endpoint
1289pub 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            // Create full request object from parameters
1314            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            // Check if provider can handle this request
1321            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            // Delegate to the elicitation provider
1328            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
1343/// Generic notifications handler for most notification endpoints
1344pub struct NotificationsHandler;
1345
1346#[async_trait]
1347impl McpHandler for NotificationsHandler {
1348    async fn handle(&self, params: Option<Value>) -> McpResult<Value> {
1349        // Notifications are typically one-way, so we just log them
1350        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
1367/// Special handler for notifications/initialized that manages session lifecycle
1368pub 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        // This should not be called directly without session context
1382        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            // Check if session is already initialized
1400            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            // Get client info from session state (it should have been stored during the initialize request)
1413            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                // Deserialize the stored values
1436                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                    // Mark session as initialized now that we received the notification
1444                    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}