| """ |
| 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 |
|
|
| |
| 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 |
|
|
| |
| 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) |
|
|
| |
| sloc = sum(1 for line in lines if line.strip() and not line.strip().startswith('#')) |
|
|
| |
| comments = sum(1 for line in lines if line.strip().startswith('#')) |
|
|
| |
| 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)) |
|
|
| |
| 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"] |
|
|
| |
| 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)") |
|
|
| |
| 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})") |
|
|
| |
| 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 |
|
|
| |
| 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) |
|
|
| |
| report = "# Code Analysis Report\n\n" |
|
|
| |
| 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" |
|
|
| |
| 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" |
|
|
| |
| report += "## Code Smells Detected\n" |
| for smell in smells: |
| report += f"- {smell}\n" |
|
|
| return report |
|
|
|
|
| |
| 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) |
|
|