#!/usr/bin/env python3 """ MCP Code Analysis Tool - Gradio Implementation This MCP server provides code analysis capabilities including: - Code quality assessment - Basic static analysis - Security vulnerability detection - Code metrics and statistics - Multi-language support Supports MCP protocol via Gradio interface. """ import logging import os import re from typing import Any import gradio as gr # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class CodeAnalyzer: """Code analysis service with static analysis capabilities.""" def __init__(self): """Initialize the code analyzer.""" self.security_patterns = { "sql_injection": [ r"SELECT.*FROM.*WHERE.*=.*\+", r"INSERT.*INTO.*VALUES.*\+", r"UPDATE.*SET.*=.*\+", ], "xss_vulnerability": [ r"innerHTML\s*=", r"document\.write\s*\(", r"eval\s*\(", ], "hardcoded_secrets": [ r'password\s*=\s*["\'][^"\']{3,}["\']', r'api_key\s*=\s*["\'][^"\']{10,}["\']', r'secret\s*=\s*["\'][^"\']{5,}["\']', ], "unsafe_functions": [ r"exec\s*\(", r"system\s*\(", r"shell_exec\s*\(", ] } def analyze_code(self, code: str, language: str = "auto") -> dict[str, Any]: """Analyze code and return comprehensive analysis results.""" try: if not code.strip(): return {"error": "No code provided for analysis"} # Detect language if auto if language == "auto": language = self._detect_language(code) # Perform analysis analysis = { "language": language, "metrics": self._calculate_metrics(code), "quality": self._assess_quality(code, language), "security": self._check_security(code), "suggestions": self._generate_suggestions(code, language) } return analysis except Exception as e: logger.error(f"Error analyzing code: {e}") return {"error": f"Analysis failed: {e!s}"} def _detect_language(self, code: str) -> str: """Detect programming language from code content.""" # Simple heuristics for language detection if re.search(r"def\s+\w+\s*\(|import\s+\w+|from\s+\w+\s+import", code): return "python" if re.search(r"function\s+\w+\s*\(|var\s+\w+|let\s+\w+|const\s+\w+", code): return "javascript" if re.search(r"public\s+class\s+\w+|private\s+\w+|System\.out\.print", code): return "java" if re.search(r"#include\s*<|int\s+main\s*\(|printf\s*\(", code): return "c" if re.search(r"SELECT\s+.*FROM|INSERT\s+INTO|UPDATE\s+.*SET", code, re.IGNORECASE): return "sql" return "unknown" def _calculate_metrics(self, code: str) -> dict[str, Any]: """Calculate basic code metrics.""" lines = code.split("\n") metrics = { "total_lines": len(lines), "blank_lines": sum(1 for line in lines if not line.strip()), "comment_lines": sum(1 for line in lines if line.strip().startswith(("#", "//", "/*", "--"))), "code_lines": 0, "max_line_length": 0, "functions": 0, "classes": 0, "complexity_estimate": "low" } code_lines = [] for line in lines: stripped = line.strip() if stripped and not stripped.startswith(("#", "//", "/*", "--")): code_lines.append(line) metrics["max_line_length"] = max(metrics["max_line_length"], len(line)) metrics["code_lines"] = len(code_lines) # Count functions and classes (basic patterns) metrics["functions"] = len(re.findall(r"def\s+\w+|function\s+\w+|public\s+\w+\s*\(", code)) metrics["classes"] = len(re.findall(r"class\s+\w+|public\s+class\s+\w+", code)) # Complexity estimate complexity_indicators = len(re.findall(r"if\s*\(|for\s*\(|while\s*\(|try\s*{|catch\s*\(", code)) if complexity_indicators > 10: metrics["complexity_estimate"] = "high" elif complexity_indicators > 5: metrics["complexity_estimate"] = "medium" return metrics def _assess_quality(self, code: str, language: str) -> dict[str, Any]: """Assess code quality based on various factors.""" quality = { "score": 100, "issues": [], "grade": "A", "recommendations": [] } lines = code.split("\n") # Check for long lines if any(len(line) > 120 for line in lines): quality["score"] -= 10 quality["issues"].append("Lines too long (>120 characters)") # Check for magic numbers magic_numbers = re.findall(r"\b\d{2,}\b", code) if len(magic_numbers) > 3: quality["score"] -= 10 quality["issues"].append("Multiple magic numbers found") # Check for comments comment_ratio = sum(1 for line in lines if line.strip().startswith(("#", "//", "/*"))) / max(len(lines), 1) if comment_ratio < 0.1: quality["score"] -= 15 quality["issues"].append("Insufficient code comments") # Determine grade if quality["score"] >= 90: quality["grade"] = "A" elif quality["score"] >= 80: quality["grade"] = "B" elif quality["score"] >= 70: quality["grade"] = "C" else: quality["grade"] = "D" return quality def _check_security(self, code: str) -> dict[str, Any]: """Check for potential security vulnerabilities.""" security = { "vulnerabilities": [], "risk_level": "low", "secure": True } for vuln_type, patterns in self.security_patterns.items(): for pattern in patterns: matches = re.findall(pattern, code, re.IGNORECASE) if matches: security["vulnerabilities"].append({ "type": vuln_type, "count": len(matches), "description": vuln_type.replace("_", " ").title() }) security["secure"] = False # Determine risk level if len(security["vulnerabilities"]) > 2: security["risk_level"] = "high" elif len(security["vulnerabilities"]) > 0: security["risk_level"] = "medium" return security def _generate_suggestions(self, code: str, language: str) -> list[str]: """Generate improvement suggestions.""" suggestions = [] if language == "python": if "import *" in code: suggestions.append("Avoid wildcard imports (import *)") elif language == "javascript": if "var " in code: suggestions.append("Consider using 'let' or 'const' instead of 'var'") if len(code.split("\n")) > 50: suggestions.append("Consider breaking large functions into smaller ones") return suggestions # Initialize the code analyzer code_analyzer = CodeAnalyzer() def analyze_code_mcp(code: str, language: str = "auto") -> str: """MCP-compatible code analysis function.""" try: if not code.strip(): return "Error: No code provided for analysis" analysis = code_analyzer.analyze_code(code, language) if "error" in analysis: return f"Analysis Error: {analysis['error']}" # Format results result = [] result.append("🔍 CODE ANALYSIS RESULTS") result.append("=" * 40) result.append(f"Language: {analysis['language'].title()}") result.append("") # Metrics metrics = analysis["metrics"] result.append("📊 CODE METRICS:") result.append(f" • Total Lines: {metrics['total_lines']}") result.append(f" • Code Lines: {metrics['code_lines']}") result.append(f" • Functions: {metrics['functions']}") result.append(f" • Complexity: {metrics['complexity_estimate'].title()}") result.append("") # Quality quality = analysis["quality"] result.append("⭐ QUALITY ASSESSMENT:") result.append(f" • Grade: {quality['grade']}") result.append(f" • Score: {quality['score']}/100") if quality["issues"]: result.append(" • Issues:") for issue in quality["issues"]: result.append(f" - {issue}") result.append("") # Security security = analysis["security"] result.append("🔒 SECURITY ANALYSIS:") result.append(f" • Risk Level: {security['risk_level'].title()}") result.append(f" • Secure: {'Yes' if security['secure'] else 'No'}") if security["vulnerabilities"]: result.append(" • Vulnerabilities:") for vuln in security["vulnerabilities"]: result.append(f" - {vuln['description']} ({vuln['count']} occurrences)") result.append("") # Suggestions suggestions = analysis["suggestions"] if suggestions: result.append("💡 SUGGESTIONS:") for suggestion in suggestions: result.append(f" • {suggestion}") logger.info(f"Successfully analyzed {metrics['total_lines']} lines of {analysis['language']} code") return "\n".join(result) except Exception as e: error_msg = f"Error analyzing code: {e!s}" logger.error(error_msg) return error_msg def create_gradio_interface(): """Create and configure the Gradio interface.""" interface = gr.Interface( fn=analyze_code_mcp, inputs=[ gr.Textbox( label="Source Code", placeholder="Paste your code here for analysis...", lines=15, max_lines=30 ), gr.Dropdown( label="Programming Language", choices=["auto", "python", "javascript", "java", "c", "sql"], value="auto" ) ], outputs=[ gr.Textbox( label="Analysis Results", lines=20, show_copy_button=True ) ], title="🔍 MCP Code Analyzer", description=""" **Code Analysis MCP Server** Analyze your code for: - Code quality and metrics - Security vulnerabilities - Best practice recommendations Supports: Python, JavaScript, Java, C, SQL """, examples=[ [ """def fibonacci(n): if n <= 1: return n else: return fibonacci(n-1) + fibonacci(n-2) for i in range(10): print(f"F({i}) = {fibonacci(i)}")""", "python" ] ], allow_flagging="never", analytics_enabled=False ) return interface def main(): """Main function to run the Gradio app.""" port = int(os.getenv("GRADIO_SERVER_PORT", 7860)) host = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0") logger.info(f"Starting MCP Code Analyzer on {host}:{port}") interface = create_gradio_interface() interface.launch( server_name=host, server_port=port, share=False, debug=False, quiet=False, show_error=True ) if __name__ == "__main__": main()