mcp_tools/servers/
code_analysis.rs

1//! Code Analysis MCP Server
2//!
3//! Provides comprehensive code analysis via MCP protocol including:
4//! - Syntax tree parsing with tree-sitter
5//! - Complexity analysis and metrics
6//! - Code quality assessment
7//! - Security vulnerability detection
8//! - Dependency analysis
9
10use async_trait::async_trait;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use tracing::{debug, info, warn};
14use tree_sitter::{Node, Parser, Query, QueryCursor, Tree};
15
16use crate::common::{
17    BaseServer, McpContent, McpServerBase, McpTool, McpToolRequest, McpToolResponse,
18    ServerCapabilities, ServerConfig,
19};
20use crate::{McpToolsError, Result};
21
22/// Code Analysis MCP Server
23pub struct CodeAnalysisServer {
24    base: BaseServer,
25}
26
27/// Complexity analysis results
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ComplexityAnalysis {
30    pub cyclomatic_complexity: u32,
31    pub cognitive_complexity: u32,
32    pub lines_of_code: u32,
33    pub functions: u32,
34    pub classes: u32,
35    pub nested_depth: u32,
36}
37
38/// Code quality metrics
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct QualityMetrics {
41    pub maintainability_index: f64,
42    pub documentation_ratio: f64,
43    pub test_coverage: f64,
44    pub code_duplication: f64,
45    pub technical_debt: f64,
46}
47
48/// Security analysis results
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct SecurityAnalysis {
51    pub vulnerabilities: Vec<SecurityVulnerability>,
52    pub risk_score: u32,
53    pub security_hotspots: Vec<String>,
54}
55
56/// Security vulnerability
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct SecurityVulnerability {
59    pub severity: String,
60    pub category: String,
61    pub description: String,
62    pub line: u32,
63    pub recommendation: String,
64}
65
66/// Dependency analysis results
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct DependencyAnalysis {
69    pub imports: Vec<String>,
70    pub external_dependencies: Vec<String>,
71    pub internal_dependencies: Vec<String>,
72    pub circular_dependencies: Vec<String>,
73    pub unused_imports: Vec<String>,
74}
75
76/// Complete code analysis results
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct CodeAnalysisResult {
79    pub language: String,
80    pub file_path: String,
81    pub complexity: ComplexityAnalysis,
82    pub quality: QualityMetrics,
83    pub security: SecurityAnalysis,
84    pub dependencies: DependencyAnalysis,
85    pub suggestions: Vec<String>,
86}
87
88impl CodeAnalysisServer {
89    pub async fn new(config: ServerConfig) -> Result<Self> {
90        let base = BaseServer::new(config).await?;
91        Ok(Self { base })
92    }
93
94    /// Detect programming language from file path and content
95    fn detect_language(&self, file_path: &str, content: &str) -> String {
96        // First try to detect by file extension
97        if let Some(extension) = std::path::Path::new(file_path).extension() {
98            match extension.to_str() {
99                Some("rs") => return "rust".to_string(),
100                Some("py") => return "python".to_string(),
101                Some("js") => return "javascript".to_string(),
102                Some("ts") => return "typescript".to_string(),
103                Some("go") => return "go".to_string(),
104                Some("java") => return "java".to_string(),
105                Some("c") => return "c".to_string(),
106                Some("cpp") | Some("cc") | Some("cxx") => return "cpp".to_string(),
107                _ => {}
108            }
109        }
110
111        // Fallback to content-based detection
112        if content.contains("fn main()") || content.contains("use std::") {
113            "rust".to_string()
114        } else if content.contains("def ") || content.contains("import ") {
115            "python".to_string()
116        } else if content.contains("function ") || content.contains("const ") {
117            "javascript".to_string()
118        } else if content.contains("package main") || content.contains("func ") {
119            "go".to_string()
120        } else if content.contains("public class") || content.contains("import java") {
121            "java".to_string()
122        } else {
123            "unknown".to_string()
124        }
125    }
126
127    /// Analyze code complexity
128    async fn analyze_complexity(
129        &self,
130        file_path: &str,
131        content: &str,
132    ) -> Result<ComplexityAnalysis> {
133        let language = self.detect_language(file_path, content);
134
135        // Create parser for the detected language
136        let mut parser = Parser::new();
137        let tree_sitter_language = match language.as_str() {
138            "rust" => tree_sitter_rust::language(),
139            "python" => tree_sitter_python::language(),
140            "javascript" => tree_sitter_javascript::language(),
141            "typescript" => tree_sitter_typescript::language_typescript(),
142            "go" => tree_sitter_go::language(),
143            "java" => tree_sitter_java::language(),
144            "c" => tree_sitter_c::language(),
145            "cpp" => tree_sitter_cpp::language(),
146            _ => {
147                // Return basic metrics for unsupported languages
148                return Ok(ComplexityAnalysis {
149                    cyclomatic_complexity: 1,
150                    cognitive_complexity: 1,
151                    lines_of_code: content.lines().count() as u32,
152                    functions: 0,
153                    classes: 0,
154                    nested_depth: 0,
155                });
156            }
157        };
158
159        parser
160            .set_language(tree_sitter_language)
161            .map_err(|e| McpToolsError::Server(format!("Failed to set language: {}", e)))?;
162
163        // Parse the code
164        let tree = parser
165            .parse(content, None)
166            .ok_or_else(|| McpToolsError::Server("Failed to parse code".to_string()))?;
167
168        // Analyze the syntax tree
169        let root_node = tree.root_node();
170        let mut complexity = ComplexityAnalysis {
171            cyclomatic_complexity: 1, // Base complexity
172            cognitive_complexity: 0,
173            lines_of_code: content.lines().count() as u32,
174            functions: 0,
175            classes: 0,
176            nested_depth: 0,
177        };
178
179        self.traverse_node(&root_node, &mut complexity, 0);
180
181        Ok(complexity)
182    }
183
184    /// Traverse syntax tree node and calculate complexity
185    fn traverse_node(&self, node: &Node, complexity: &mut ComplexityAnalysis, depth: u32) {
186        complexity.nested_depth = complexity.nested_depth.max(depth);
187
188        match node.kind() {
189            // Function definitions
190            "function_item"
191            | "function_declaration"
192            | "function_definition"
193            | "method_declaration" => {
194                complexity.functions += 1;
195                complexity.cyclomatic_complexity += 1;
196            }
197            // Class definitions
198            "struct_item" | "impl_item" | "class_declaration" | "class_specifier" => {
199                complexity.classes += 1;
200            }
201            // Control flow statements that increase complexity
202            "if_expression" | "if_statement" | "match_expression" | "while_statement"
203            | "for_statement" | "loop_expression" | "try_statement" => {
204                complexity.cyclomatic_complexity += 1;
205                complexity.cognitive_complexity += 1;
206            }
207            // Logical operators
208            "binary_expression" => {
209                if let Some(operator) = node.child_by_field_name("operator") {
210                    if matches!(operator.kind(), "&&" | "||" | "and" | "or") {
211                        complexity.cyclomatic_complexity += 1;
212                    }
213                }
214            }
215            _ => {}
216        }
217
218        // Recursively traverse child nodes
219        for i in 0..node.child_count() {
220            if let Some(child) = node.child(i) {
221                self.traverse_node(&child, complexity, depth + 1);
222            }
223        }
224    }
225
226    /// Analyze code quality metrics
227    async fn analyze_quality(&self, content: &str) -> QualityMetrics {
228        let lines = content.lines().collect::<Vec<_>>();
229        let total_lines = lines.len() as f64;
230
231        // Count comment lines for documentation ratio
232        let comment_lines = lines
233            .iter()
234            .filter(|line| {
235                let trimmed = line.trim();
236                trimmed.starts_with("//")
237                    || trimmed.starts_with("#")
238                    || trimmed.starts_with("/*")
239                    || trimmed.starts_with("*")
240                    || trimmed.starts_with("\"\"\"")
241                    || trimmed.starts_with("'''")
242            })
243            .count() as f64;
244
245        let documentation_ratio = if total_lines > 0.0 {
246            (comment_lines / total_lines) * 100.0
247        } else {
248            0.0
249        };
250
251        // Basic maintainability index calculation (simplified)
252        let maintainability_index = 100.0 - (documentation_ratio * 0.1).max(0.0).min(100.0);
253
254        QualityMetrics {
255            maintainability_index,
256            documentation_ratio,
257            test_coverage: 0.0,    // Would need test file analysis
258            code_duplication: 0.0, // Would need more sophisticated analysis
259            technical_debt: 0.0,   // Would need more sophisticated analysis
260        }
261    }
262
263    /// Analyze security vulnerabilities (simplified)
264    async fn analyze_security(&self, content: &str, language: &str) -> SecurityAnalysis {
265        let mut vulnerabilities = Vec::new();
266        let lines: Vec<&str> = content.lines().collect();
267
268        // Basic security pattern detection
269        for (line_num, line) in lines.iter().enumerate() {
270            let line_lower = line.to_lowercase();
271
272            // SQL injection patterns
273            if line_lower.contains("select")
274                && line_lower.contains("where")
275                && line_lower.contains("+")
276            {
277                vulnerabilities.push(SecurityVulnerability {
278                    severity: "High".to_string(),
279                    category: "SQL Injection".to_string(),
280                    description: "Potential SQL injection vulnerability".to_string(),
281                    line: (line_num + 1) as u32,
282                    recommendation: "Use parameterized queries".to_string(),
283                });
284            }
285
286            // Hardcoded credentials
287            if line_lower.contains("password")
288                && (line_lower.contains("=") || line_lower.contains(":"))
289            {
290                vulnerabilities.push(SecurityVulnerability {
291                    severity: "Medium".to_string(),
292                    category: "Hardcoded Credentials".to_string(),
293                    description: "Potential hardcoded password".to_string(),
294                    line: (line_num + 1) as u32,
295                    recommendation: "Use environment variables or secure storage".to_string(),
296                });
297            }
298
299            // Unsafe operations (language-specific)
300            match language {
301                "rust" => {
302                    if line.contains("unsafe") {
303                        vulnerabilities.push(SecurityVulnerability {
304                            severity: "Medium".to_string(),
305                            category: "Unsafe Code".to_string(),
306                            description: "Unsafe Rust code block".to_string(),
307                            line: (line_num + 1) as u32,
308                            recommendation: "Review unsafe code for memory safety".to_string(),
309                        });
310                    }
311                }
312                "c" | "cpp" => {
313                    if line.contains("strcpy") || line.contains("sprintf") {
314                        vulnerabilities.push(SecurityVulnerability {
315                            severity: "High".to_string(),
316                            category: "Buffer Overflow".to_string(),
317                            description: "Unsafe string function".to_string(),
318                            line: (line_num + 1) as u32,
319                            recommendation: "Use safe string functions like strncpy or snprintf"
320                                .to_string(),
321                        });
322                    }
323                }
324                _ => {}
325            }
326        }
327
328        let risk_score = vulnerabilities
329            .iter()
330            .map(|v| match v.severity.as_str() {
331                "High" => 30,
332                "Medium" => 15,
333                "Low" => 5,
334                _ => 0,
335            })
336            .sum::<u32>()
337            .min(100);
338
339        SecurityAnalysis {
340            vulnerabilities,
341            risk_score,
342            security_hotspots: vec![], // Would need more sophisticated analysis
343        }
344    }
345
346    /// Analyze dependencies (simplified)
347    async fn analyze_dependencies(&self, content: &str, language: &str) -> DependencyAnalysis {
348        let lines: Vec<&str> = content.lines().collect();
349        let mut imports = Vec::new();
350        let mut external_dependencies = Vec::new();
351        let mut internal_dependencies = Vec::new();
352
353        for line in lines {
354            let trimmed = line.trim();
355
356            match language {
357                "rust" => {
358                    if trimmed.starts_with("use ") {
359                        let import = trimmed
360                            .strip_prefix("use ")
361                            .unwrap_or("")
362                            .split(';')
363                            .next()
364                            .unwrap_or("")
365                            .trim();
366                        imports.push(import.to_string());
367
368                        if import.starts_with("std::") || import.starts_with("core::") {
369                            // Standard library
370                        } else if import.starts_with("crate::")
371                            || import.starts_with("super::")
372                            || import.starts_with("self::")
373                        {
374                            internal_dependencies.push(import.to_string());
375                        } else {
376                            external_dependencies.push(import.to_string());
377                        }
378                    }
379                }
380                "python" => {
381                    if trimmed.starts_with("import ") || trimmed.starts_with("from ") {
382                        imports.push(trimmed.to_string());
383                        // Would need more sophisticated analysis for internal vs external
384                    }
385                }
386                "javascript" | "typescript" => {
387                    if trimmed.starts_with("import ") || trimmed.contains("require(") {
388                        imports.push(trimmed.to_string());
389                        // Would need more sophisticated analysis for internal vs external
390                    }
391                }
392                _ => {}
393            }
394        }
395
396        DependencyAnalysis {
397            imports,
398            external_dependencies,
399            internal_dependencies,
400            circular_dependencies: Vec::new(), // Would need graph analysis
401            unused_imports: Vec::new(),        // Would need usage analysis
402        }
403    }
404}
405
406#[async_trait]
407impl McpServerBase for CodeAnalysisServer {
408    async fn get_capabilities(&self) -> Result<ServerCapabilities> {
409        let mut capabilities = self.base.get_capabilities().await?;
410
411        // Add Code Analysis-specific tools
412        let analysis_tools = vec![
413            McpTool {
414                name: "analyze_code".to_string(),
415                description: "Perform comprehensive code analysis including complexity, quality, security, and dependencies".to_string(),
416                input_schema: serde_json::json!({
417                    "type": "object",
418                    "properties": {
419                        "file_path": {
420                            "type": "string",
421                            "description": "Path to the code file to analyze"
422                        },
423                        "content": {
424                            "type": "string",
425                            "description": "Code content to analyze (alternative to file_path)"
426                        },
427                        "language": {
428                            "type": "string",
429                            "description": "Programming language (optional, auto-detected if not provided)"
430                        },
431                        "analysis_type": {
432                            "type": "string",
433                            "description": "Type of analysis: 'complexity', 'quality', 'security', 'dependencies', or 'comprehensive'",
434                            "enum": ["complexity", "quality", "security", "dependencies", "comprehensive"]
435                        }
436                    },
437                    "required": ["file_path"],
438                    "oneOf": [
439                        {"required": ["file_path"]},
440                        {"required": ["content"]}
441                    ]
442                }),
443                category: "code-analysis".to_string(),
444                requires_permission: false,
445                permissions: vec![],
446            },
447            McpTool {
448                name: "detect_language".to_string(),
449                description: "Detect programming language from file path or content".to_string(),
450                input_schema: serde_json::json!({
451                    "type": "object",
452                    "properties": {
453                        "file_path": {
454                            "type": "string",
455                            "description": "Path to the file"
456                        },
457                        "content": {
458                            "type": "string",
459                            "description": "Code content to analyze"
460                        }
461                    },
462                    "oneOf": [
463                        {"required": ["file_path"]},
464                        {"required": ["content"]}
465                    ]
466                }),
467                category: "code-analysis".to_string(),
468                requires_permission: false,
469                permissions: vec![],
470            },
471            McpTool {
472                name: "complexity_analysis".to_string(),
473                description: "Analyze code complexity metrics including cyclomatic complexity and nesting depth".to_string(),
474                input_schema: serde_json::json!({
475                    "type": "object",
476                    "properties": {
477                        "file_path": {
478                            "type": "string",
479                            "description": "Path to the code file"
480                        },
481                        "content": {
482                            "type": "string",
483                            "description": "Code content to analyze"
484                        },
485                        "language": {
486                            "type": "string",
487                            "description": "Programming language (optional)"
488                        }
489                    },
490                    "oneOf": [
491                        {"required": ["file_path"]},
492                        {"required": ["content"]}
493                    ]
494                }),
495                category: "code-analysis".to_string(),
496                requires_permission: false,
497                permissions: vec![],
498            },
499            McpTool {
500                name: "security_analysis".to_string(),
501                description: "Analyze code for security vulnerabilities and risks".to_string(),
502                input_schema: serde_json::json!({
503                    "type": "object",
504                    "properties": {
505                        "file_path": {
506                            "type": "string",
507                            "description": "Path to the code file"
508                        },
509                        "content": {
510                            "type": "string",
511                            "description": "Code content to analyze"
512                        },
513                        "language": {
514                            "type": "string",
515                            "description": "Programming language (optional)"
516                        }
517                    },
518                    "oneOf": [
519                        {"required": ["file_path"]},
520                        {"required": ["content"]}
521                    ]
522                }),
523                category: "code-analysis".to_string(),
524                requires_permission: false,
525                permissions: vec![],
526            },
527        ];
528
529        capabilities.tools = analysis_tools;
530        Ok(capabilities)
531    }
532
533    async fn handle_tool_request(&self, request: McpToolRequest) -> Result<McpToolResponse> {
534        info!("Handling Code Analysis tool request: {}", request.tool);
535
536        // Get file path or content
537        let file_path = request
538            .arguments
539            .get("file_path")
540            .and_then(|v| v.as_str())
541            .unwrap_or("unknown");
542
543        let content = if let Some(content_value) = request.arguments.get("content") {
544            content_value.as_str().unwrap_or("").to_string()
545        } else if file_path != "unknown" {
546            // In a real implementation, we would read the file
547            // For now, return an error asking for content
548            return Ok(McpToolResponse {
549                id: request.id,
550                content: vec![McpContent::text(
551                    "File reading not implemented. Please provide 'content' parameter.".to_string(),
552                )],
553                is_error: true,
554                error: Some("File reading not implemented".to_string()),
555                metadata: HashMap::new(),
556            });
557        } else {
558            return Ok(McpToolResponse {
559                id: request.id,
560                content: vec![McpContent::text(
561                    "Either 'file_path' or 'content' parameter is required".to_string(),
562                )],
563                is_error: true,
564                error: Some("Missing required parameter".to_string()),
565                metadata: HashMap::new(),
566            });
567        };
568
569        let language = request
570            .arguments
571            .get("language")
572            .and_then(|v| v.as_str())
573            .map(|s| s.to_string())
574            .unwrap_or_else(|| self.detect_language(file_path, &content));
575
576        match request.tool.as_str() {
577            "analyze_code" => {
578                debug!("Performing comprehensive code analysis for: {}", file_path);
579
580                let analysis_type = request
581                    .arguments
582                    .get("analysis_type")
583                    .and_then(|v| v.as_str())
584                    .unwrap_or("comprehensive");
585
586                match analysis_type {
587                    "comprehensive" => {
588                        let complexity = self.analyze_complexity(file_path, &content).await?;
589                        let quality = self.analyze_quality(&content).await;
590                        let security = self.analyze_security(&content, &language).await;
591                        let dependencies = self.analyze_dependencies(&content, &language).await;
592
593                        // Generate suggestions
594                        let mut suggestions = Vec::new();
595                        if complexity.cyclomatic_complexity > 10 {
596                            suggestions
597                                .push("Consider breaking down complex functions".to_string());
598                        }
599                        if complexity.nested_depth > 4 {
600                            suggestions
601                                .push("Reduce nesting depth for better readability".to_string());
602                        }
603                        if quality.documentation_ratio < 10.0 {
604                            suggestions.push("Add more documentation and comments".to_string());
605                        }
606
607                        let result = CodeAnalysisResult {
608                            language: language.clone(),
609                            file_path: file_path.to_string(),
610                            complexity,
611                            quality,
612                            security,
613                            dependencies,
614                            suggestions,
615                        };
616
617                        let content_text = format!(
618                            "Code Analysis Complete\n\
619                            Language: {}\n\
620                            Cyclomatic Complexity: {}\n\
621                            Lines of Code: {}\n\
622                            Functions: {}\n\
623                            Security Issues: {}\n\
624                            Risk Score: {}/100",
625                            result.language,
626                            result.complexity.cyclomatic_complexity,
627                            result.complexity.lines_of_code,
628                            result.complexity.functions,
629                            result.security.vulnerabilities.len(),
630                            result.security.risk_score
631                        );
632
633                        let mut metadata = HashMap::new();
634                        metadata
635                            .insert("analysis_result".to_string(), serde_json::to_value(result)?);
636
637                        Ok(McpToolResponse {
638                            id: request.id,
639                            content: vec![McpContent::text(content_text)],
640                            is_error: false,
641                            error: None,
642                            metadata,
643                        })
644                    }
645                    "complexity" => {
646                        let complexity = self.analyze_complexity(file_path, &content).await?;
647                        let content_text = format!(
648                            "Complexity Analysis\n\
649                            Cyclomatic Complexity: {}\n\
650                            Cognitive Complexity: {}\n\
651                            Lines of Code: {}\n\
652                            Functions: {}\n\
653                            Classes: {}\n\
654                            Max Nesting Depth: {}",
655                            complexity.cyclomatic_complexity,
656                            complexity.cognitive_complexity,
657                            complexity.lines_of_code,
658                            complexity.functions,
659                            complexity.classes,
660                            complexity.nested_depth
661                        );
662
663                        let mut metadata = HashMap::new();
664                        metadata
665                            .insert("complexity".to_string(), serde_json::to_value(complexity)?);
666
667                        Ok(McpToolResponse {
668                            id: request.id,
669                            content: vec![McpContent::text(content_text)],
670                            is_error: false,
671                            error: None,
672                            metadata,
673                        })
674                    }
675                    "security" => {
676                        let security = self.analyze_security(&content, &language).await;
677                        let content_text = format!(
678                            "Security Analysis\n\
679                            Vulnerabilities Found: {}\n\
680                            Risk Score: {}/100\n\
681                            Security Hotspots: {}",
682                            security.vulnerabilities.len(),
683                            security.risk_score,
684                            security.security_hotspots.len()
685                        );
686
687                        let mut metadata = HashMap::new();
688                        metadata.insert("security".to_string(), serde_json::to_value(security)?);
689
690                        Ok(McpToolResponse {
691                            id: request.id,
692                            content: vec![McpContent::text(content_text)],
693                            is_error: false,
694                            error: None,
695                            metadata,
696                        })
697                    }
698                    _ => {
699                        warn!("Unknown analysis type: {}", analysis_type);
700                        Ok(McpToolResponse {
701                            id: request.id,
702                            content: vec![McpContent::text(format!(
703                                "Unknown analysis type: {}",
704                                analysis_type
705                            ))],
706                            is_error: true,
707                            error: Some("Invalid analysis type".to_string()),
708                            metadata: HashMap::new(),
709                        })
710                    }
711                }
712            }
713            "detect_language" => {
714                debug!("Detecting language for: {}", file_path);
715                let detected_language = self.detect_language(file_path, &content);
716
717                let content_text = format!("Detected Language: {}", detected_language);
718                let mut metadata = HashMap::new();
719                metadata.insert(
720                    "language".to_string(),
721                    serde_json::Value::String(detected_language),
722                );
723
724                Ok(McpToolResponse {
725                    id: request.id,
726                    content: vec![McpContent::text(content_text)],
727                    is_error: false,
728                    error: None,
729                    metadata,
730                })
731            }
732            "complexity_analysis" => {
733                debug!("Analyzing complexity for: {}", file_path);
734                let complexity = self.analyze_complexity(file_path, &content).await?;
735
736                let content_text = format!(
737                    "Complexity Analysis\n\
738                    Cyclomatic Complexity: {}\n\
739                    Lines of Code: {}\n\
740                    Functions: {}",
741                    complexity.cyclomatic_complexity,
742                    complexity.lines_of_code,
743                    complexity.functions
744                );
745
746                let mut metadata = HashMap::new();
747                metadata.insert("complexity".to_string(), serde_json::to_value(complexity)?);
748
749                Ok(McpToolResponse {
750                    id: request.id,
751                    content: vec![McpContent::text(content_text)],
752                    is_error: false,
753                    error: None,
754                    metadata,
755                })
756            }
757            "security_analysis" => {
758                debug!("Analyzing security for: {}", file_path);
759                let security = self.analyze_security(&content, &language).await;
760
761                let content_text = format!(
762                    "Security Analysis\n\
763                    Vulnerabilities: {}\n\
764                    Risk Score: {}/100",
765                    security.vulnerabilities.len(),
766                    security.risk_score
767                );
768
769                let mut metadata = HashMap::new();
770                metadata.insert("security".to_string(), serde_json::to_value(security)?);
771
772                Ok(McpToolResponse {
773                    id: request.id,
774                    content: vec![McpContent::text(content_text)],
775                    is_error: false,
776                    error: None,
777                    metadata,
778                })
779            }
780            _ => {
781                warn!("Unknown Code Analysis tool: {}", request.tool);
782                Err(McpToolsError::Server(format!(
783                    "Unknown Code Analysis tool: {}",
784                    request.tool
785                )))
786            }
787        }
788    }
789
790    async fn get_stats(&self) -> Result<crate::common::ServerStats> {
791        self.base.get_stats().await
792    }
793
794    async fn initialize(&mut self) -> Result<()> {
795        info!("Initializing Code Analysis MCP Server");
796        Ok(())
797    }
798
799    async fn shutdown(&mut self) -> Result<()> {
800        info!("Shutting down Code Analysis MCP Server");
801        Ok(())
802    }
803}