""" Code Complexity Analyzer MCP Server This MCP server analyzes Python code complexity and provides insights about code quality, maintainability, and potential refactoring opportunities. Tags: building-mcp-track-enterprise """ import ast import re from typing import Dict, List, Tuple import gradio as gr def calculate_cyclomatic_complexity(code: str) -> Dict[str, any]: """ Calculate cyclomatic complexity of Python code. Args: code: Python source code as a string Returns: Dictionary with complexity metrics """ try: tree = ast.parse(code) except SyntaxError as e: return {"error": f"Syntax error in code: {str(e)}"} complexity = 1 # Base complexity # Count decision points that increase complexity for node in ast.walk(tree): if isinstance(node, (ast.If, ast.While, ast.For, ast.ExceptHandler)): complexity += 1 elif isinstance(node, ast.BoolOp): complexity += len(node.values) - 1 elif isinstance(node, (ast.ListComp, ast.DictComp, ast.SetComp, ast.GeneratorExp)): complexity += 1 # Determine complexity level if complexity <= 10: level = "Low (Simple)" recommendation = "Code is easy to understand and maintain." elif complexity <= 20: level = "Moderate" recommendation = "Consider breaking into smaller functions." elif complexity <= 50: level = "High" recommendation = "Refactoring strongly recommended. Break into smaller, focused functions." else: level = "Very High (Critical)" recommendation = "Immediate refactoring required. Code is difficult to test and maintain." return { "cyclomatic_complexity": complexity, "complexity_level": level, "recommendation": recommendation } def analyze_code_metrics(code: str) -> Dict[str, any]: """ Analyze various code metrics including LOC, functions, classes, etc. Args: code: Python source code as a string Returns: Dictionary with code metrics """ try: tree = ast.parse(code) except SyntaxError as e: return {"error": f"Syntax error in code: {str(e)}"} lines = code.split('\n') loc = len(lines) # Count non-empty, non-comment lines sloc = sum(1 for line in lines if line.strip() and not line.strip().startswith('#')) # Count comments comments = sum(1 for line in lines if line.strip().startswith('#')) # Count functions and classes functions = sum(1 for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)) classes = sum(1 for node in ast.walk(tree) if isinstance(node, ast.ClassDef)) # Calculate comment ratio comment_ratio = (comments / sloc * 100) if sloc > 0 else 0 return { "lines_of_code": loc, "source_lines_of_code": sloc, "comment_lines": comments, "comment_ratio_percent": round(comment_ratio, 2), "functions": functions, "classes": classes } def detect_code_smells(code: str) -> List[str]: """ Detect common code smells in Python code. Args: code: Python source code as a string Returns: List of detected code smells """ smells = [] try: tree = ast.parse(code) except SyntaxError: return ["Syntax error prevents analysis"] # Check for long functions (>50 lines) for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): if hasattr(node, 'end_lineno') and hasattr(node, 'lineno'): func_lines = node.end_lineno - node.lineno if func_lines > 50: smells.append(f"Long function '{node.name}' ({func_lines} lines)") # Check for too many parameters if isinstance(node, ast.FunctionDef): param_count = len(node.args.args) if param_count > 5: smells.append(f"Function '{node.name}' has too many parameters ({param_count})") # Check for deeply nested code (>4 levels) if isinstance(node, (ast.If, ast.For, ast.While)): depth = sum(1 for parent in ast.walk(tree) if isinstance(parent, (ast.If, ast.For, ast.While))) if depth > 4: smells.append("Deeply nested code blocks detected") break # Check for duplicate code patterns (simple check) lines = [line.strip() for line in code.split('\n') if line.strip()] if len(lines) != len(set(lines)): duplicate_count = len(lines) - len(set(lines)) if duplicate_count > 3: smells.append(f"Possible duplicate code: {duplicate_count} duplicate lines") if not smells: smells.append("No obvious code smells detected!") return smells def full_code_analysis(code: str) -> str: """ Perform complete code analysis combining all metrics. Args: code: Python source code as a string Returns: Formatted analysis report """ if not code.strip(): return "Please provide Python code to analyze." complexity = calculate_cyclomatic_complexity(code) metrics = analyze_code_metrics(code) smells = detect_code_smells(code) # Build report report = "# Code Analysis Report\n\n" # Complexity section report += "## Complexity Analysis\n" if "error" in complexity: report += f"Error: {complexity['error']}\n\n" else: report += f"- **Cyclomatic Complexity**: {complexity['cyclomatic_complexity']}\n" report += f"- **Complexity Level**: {complexity['complexity_level']}\n" report += f"- **Recommendation**: {complexity['recommendation']}\n\n" # Metrics section report += "## Code Metrics\n" if "error" in metrics: report += f"Error: {metrics['error']}\n\n" else: report += f"- **Total Lines**: {metrics['lines_of_code']}\n" report += f"- **Source Lines**: {metrics['source_lines_of_code']}\n" report += f"- **Comment Lines**: {metrics['comment_lines']}\n" report += f"- **Comment Ratio**: {metrics['comment_ratio_percent']}%\n" report += f"- **Functions**: {metrics['functions']}\n" report += f"- **Classes**: {metrics['classes']}\n\n" # Code smells section report += "## Code Smells Detected\n" for smell in smells: report += f"- {smell}\n" return report # Create Gradio interface with gr.Blocks(title="Code Complexity Analyzer MCP Server") as demo: gr.Markdown(""" # Code Complexity Analyzer MCP Server Analyze Python code complexity and quality metrics. This MCP server provides: - Cyclomatic complexity calculation - Code metrics (LOC, comments, functions, classes) - Code smell detection - Refactoring recommendations **Category**: Enterprise MCP Server **Tags**: building-mcp-track-enterprise """) with gr.Tab("Full Analysis"): code_input = gr.Code( label="Python Code", language="python", lines=20, value="# Paste your Python code here\ndef example():\n pass" ) analyze_btn = gr.Button("Analyze Code", variant="primary") analysis_output = gr.Markdown(label="Analysis Report") analyze_btn.click( fn=full_code_analysis, inputs=code_input, outputs=analysis_output ) with gr.Tab("Complexity Only"): complexity_input = gr.Code(label="Python Code", language="python", lines=15) complexity_btn = gr.Button("Calculate Complexity") complexity_output = gr.JSON(label="Complexity Metrics") complexity_btn.click( fn=calculate_cyclomatic_complexity, inputs=complexity_input, outputs=complexity_output ) with gr.Tab("Code Metrics"): metrics_input = gr.Code(label="Python Code", language="python", lines=15) metrics_btn = gr.Button("Analyze Metrics") metrics_output = gr.JSON(label="Code Metrics") metrics_btn.click( fn=analyze_code_metrics, inputs=metrics_input, outputs=metrics_output ) with gr.Tab("Code Smells"): smells_input = gr.Code(label="Python Code", language="python", lines=15) smells_btn = gr.Button("Detect Code Smells") smells_output = gr.JSON(label="Detected Code Smells") smells_btn.click( fn=detect_code_smells, inputs=smells_input, outputs=smells_output ) if __name__ == "__main__": demo.launch(mcp_server=True, server_name="0.0.0.0", server_port=7860)