dejun-huang/Code-Complexity-Analyzer
7
1"""2Code Complexity Analyzer MCP Server3 4This MCP server analyzes Python code complexity and provides insights5about code quality, maintainability, and potential refactoring opportunities.6 7Tags: building-mcp-track-enterprise8"""9 10import ast11import re12from typing import Dict, List, Tuple13import gradio as gr14 15 16def calculate_cyclomatic_complexity(code: str) -> Dict[str, any]:17 """18 Calculate cyclomatic complexity of Python code.19 20 Args:21 code: Python source code as a string22 23 Returns:24 Dictionary with complexity metrics25 """26 try:27 tree = ast.parse(code)28 except SyntaxError as e:29 return {"error": f"Syntax error in code: {str(e)}"}30 31 complexity = 1 # Base complexity32 33 # Count decision points that increase complexity34 for node in ast.walk(tree):35 if isinstance(node, (ast.If, ast.While, ast.For, ast.ExceptHandler)):36 complexity += 137 elif isinstance(node, ast.BoolOp):38 complexity += len(node.values) - 139 elif isinstance(node, (ast.ListComp, ast.DictComp, ast.SetComp, ast.GeneratorExp)):40 complexity += 141 42 # Determine complexity level43 if complexity <= 10:44 level = "Low (Simple)"45 recommendation = "Code is easy to understand and maintain."46 elif complexity <= 20:47 level = "Moderate"48 recommendation = "Consider breaking into smaller functions."49 elif complexity <= 50:50 level = "High"51 recommendation = "Refactoring strongly recommended. Break into smaller, focused functions."52 else:53 level = "Very High (Critical)"54 recommendation = "Immediate refactoring required. Code is difficult to test and maintain."55 56 return {57 "cyclomatic_complexity": complexity,58 "complexity_level": level,59 "recommendation": recommendation60 }61 62 63def analyze_code_metrics(code: str) -> Dict[str, any]:64 """65 Analyze various code metrics including LOC, functions, classes, etc.66 67 Args:68 code: Python source code as a string69 70 Returns:71 Dictionary with code metrics72 """73 try:74 tree = ast.parse(code)75 except SyntaxError as e:76 return {"error": f"Syntax error in code: {str(e)}"}77 78 lines = code.split('\n')79 loc = len(lines)80 81 # Count non-empty, non-comment lines82 sloc = sum(1 for line in lines if line.strip() and not line.strip().startswith('#'))83 84 # Count comments85 comments = sum(1 for line in lines if line.strip().startswith('#'))86 87 # Count functions and classes88 functions = sum(1 for node in ast.walk(tree) if isinstance(node, ast.FunctionDef))89 classes = sum(1 for node in ast.walk(tree) if isinstance(node, ast.ClassDef))90 91 # Calculate comment ratio92 comment_ratio = (comments / sloc * 100) if sloc > 0 else 093 94 return {95 "lines_of_code": loc,96 "source_lines_of_code": sloc,97 "comment_lines": comments,98 "comment_ratio_percent": round(comment_ratio, 2),99 "functions": functions,100 "classes": classes101 }102 103 104def detect_code_smells(code: str) -> List[str]:105 """106 Detect common code smells in Python code.107 108 Args:109 code: Python source code as a string110 111 Returns:112 List of detected code smells113 """114 smells = []115 116 try:117 tree = ast.parse(code)118 except SyntaxError:119 return ["Syntax error prevents analysis"]120 121 # Check for long functions (>50 lines)122 for node in ast.walk(tree):123 if isinstance(node, ast.FunctionDef):124 if hasattr(node, 'end_lineno') and hasattr(node, 'lineno'):125 func_lines = node.end_lineno - node.lineno126 if func_lines > 50:127 smells.append(f"Long function '{node.name}' ({func_lines} lines)")128 129 # Check for too many parameters130 if isinstance(node, ast.FunctionDef):131 param_count = len(node.args.args)132 if param_count > 5:133 smells.append(f"Function '{node.name}' has too many parameters ({param_count})")134 135 # Check for deeply nested code (>4 levels)136 if isinstance(node, (ast.If, ast.For, ast.While)):137 depth = sum(1 for parent in ast.walk(tree)138 if isinstance(parent, (ast.If, ast.For, ast.While)))139 if depth > 4:140 smells.append("Deeply nested code blocks detected")141 break142 143 # Check for duplicate code patterns (simple check)144 lines = [line.strip() for line in code.split('\n') if line.strip()]145 if len(lines) != len(set(lines)):146 duplicate_count = len(lines) - len(set(lines))147 if duplicate_count > 3:148 smells.append(f"Possible duplicate code: {duplicate_count} duplicate lines")149 150 if not smells:151 smells.append("No obvious code smells detected!")152 153 return smells154 155 156def full_code_analysis(code: str) -> str:157 """158 Perform complete code analysis combining all metrics.159 160 Args:161 code: Python source code as a string162 163 Returns:164 Formatted analysis report165 """166 if not code.strip():167 return "Please provide Python code to analyze."168 169 complexity = calculate_cyclomatic_complexity(code)170 metrics = analyze_code_metrics(code)171 smells = detect_code_smells(code)172 173 # Build report174 report = "# Code Analysis Report\n\n"175 176 # Complexity section177 report += "## Complexity Analysis\n"178 if "error" in complexity:179 report += f"Error: {complexity['error']}\n\n"180 else:181 report += f"- **Cyclomatic Complexity**: {complexity['cyclomatic_complexity']}\n"182 report += f"- **Complexity Level**: {complexity['complexity_level']}\n"183 report += f"- **Recommendation**: {complexity['recommendation']}\n\n"184 185 # Metrics section186 report += "## Code Metrics\n"187 if "error" in metrics:188 report += f"Error: {metrics['error']}\n\n"189 else:190 report += f"- **Total Lines**: {metrics['lines_of_code']}\n"191 report += f"- **Source Lines**: {metrics['source_lines_of_code']}\n"192 report += f"- **Comment Lines**: {metrics['comment_lines']}\n"193 report += f"- **Comment Ratio**: {metrics['comment_ratio_percent']}%\n"194 report += f"- **Functions**: {metrics['functions']}\n"195 report += f"- **Classes**: {metrics['classes']}\n\n"196 197 # Code smells section198 report += "## Code Smells Detected\n"199 for smell in smells:200 report += f"- {smell}\n"201 202 return report203 204 205# Create Gradio interface206with gr.Blocks(title="Code Complexity Analyzer MCP Server") as demo:207 gr.Markdown("""208 # Code Complexity Analyzer MCP Server209 210 Analyze Python code complexity and quality metrics. This MCP server provides:211 - Cyclomatic complexity calculation212 - Code metrics (LOC, comments, functions, classes)213 - Code smell detection214 - Refactoring recommendations215 216 **Category**: Enterprise MCP Server217 **Tags**: building-mcp-track-enterprise218 """)219 220 with gr.Tab("Full Analysis"):221 code_input = gr.Code(222 label="Python Code",223 language="python",224 lines=20,225 value="# Paste your Python code here\ndef example():\n pass"226 )227 analyze_btn = gr.Button("Analyze Code", variant="primary")228 analysis_output = gr.Markdown(label="Analysis Report")229 230 analyze_btn.click(231 fn=full_code_analysis,232 inputs=code_input,233 outputs=analysis_output234 )235 236 with gr.Tab("Complexity Only"):237 complexity_input = gr.Code(label="Python Code", language="python", lines=15)238 complexity_btn = gr.Button("Calculate Complexity")239 complexity_output = gr.JSON(label="Complexity Metrics")240 241 complexity_btn.click(242 fn=calculate_cyclomatic_complexity,243 inputs=complexity_input,244 outputs=complexity_output245 )246 247 with gr.Tab("Code Metrics"):248 metrics_input = gr.Code(label="Python Code", language="python", lines=15)249 metrics_btn = gr.Button("Analyze Metrics")250 metrics_output = gr.JSON(label="Code Metrics")251 252 metrics_btn.click(253 fn=analyze_code_metrics,254 inputs=metrics_input,255 outputs=metrics_output256 )257 258 with gr.Tab("Code Smells"):259 smells_input = gr.Code(label="Python Code", language="python", lines=15)260 smells_btn = gr.Button("Detect Code Smells")261 smells_output = gr.JSON(label="Detected Code Smells")262 263 smells_btn.click(264 fn=detect_code_smells,265 inputs=smells_input,266 outputs=smells_output267 )268 269 270if __name__ == "__main__":271 demo.launch(mcp_server=True, server_name="0.0.0.0", server_port=7860)272 