CoolFace
Apppublic

dejun-huang/Code-Complexity-Analyzer

sourceHugging Facemitupdated 10mo agoView on Hugging Face
7likes
app.py272 linesDownload Raw Back to root
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