Codelint-MCP / src /server.py
OsamaAliMid's picture
Add CodeLint MCP Premium Edition application
ec37394
"""πŸ† CodeLint MCP Server - Premium Edition
FastMCP server with 10 tools, mature analyzers, and premium AI integration.
Built for top-tier performance with comprehensive error handling.
"""
import logging
import sys
from pathlib import Path
from typing import Any
# Add src to path
sys.path.insert(0, str(Path(__file__).parent.parent))
# Configure logging to stderr only
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
stream=sys.stderr
)
logger = logging.getLogger(__name__)
# FastMCP imports
from fastmcp import FastMCP
from fastmcp.resources import FunctionResource
# Our premium analyzers
from src.analyzers.python_analyzer import PythonAnalyzer, analyze_python
from src.analyzers.javascript_analyzer import JavaScriptAnalyzer, analyze_javascript
from src.analyzers.project_analyzer import ProjectAnalyzer, analyze_project
from src.analyzers.git_analyzer import GitAnalyzer, analyze_git_diff
# Utilities
from src.utils.language_detector import detect_language
from src.utils.ai_client import generate_ai_response
from src.config import Config
# Create FastMCP server
mcp = FastMCP("codelint-premium")
logger.info("πŸ† CodeLint MCP Server - Premium Edition")
logger.info("βœ… All analyzers loaded and ready")
# ============================================================================
# CORE TOOLS (5 Essential)
# ============================================================================
@mcp.tool()
async def analyze_code(code: str, language: str = "auto") -> dict[str, Any]:
"""
Comprehensive code analysis with linting, security, and complexity.
Analyzes source code using multiple specialized tools:
- Python: Ruff (linting), Bandit (security), Radon (complexity)
- JavaScript/TypeScript: ESLint (linting), complexity analysis
Args:
code: Source code to analyze
language: Programming language (python, javascript, typescript, or auto)
Returns:
Analysis results with issues, summary, and metadata
"""
try:
if not code or not code.strip():
return {"error": "Code cannot be empty", "issues": []}
# Auto-detect language if needed
if language == "auto":
language = detect_language(code)
logger.info(f"Auto-detected language: {language}")
# Run appropriate analyzer
if language == "python":
analyzer = PythonAnalyzer()
result = await analyzer.analyze(code)
elif language in ["javascript", "typescript"]:
analyzer = JavaScriptAnalyzer()
result = await analyzer.analyze(code, language=language)
else:
return {
"error": f"Unsupported language: {language}",
"supported": ["python", "javascript", "typescript"],
"issues": []
}
logger.info(f"Analysis complete: {len(result.get('issues', []))} issues found")
return result
except Exception as e:
logger.error(f"analyze_code failed: {e}", exc_info=True)
return {"error": str(e), "issues": []}
@mcp.tool()
async def check_security(code: str, language: str = "auto") -> dict[str, Any]:
"""
Security vulnerability scanning with severity classification.
Focuses specifically on security issues:
- Python: Bandit security scanner
- JavaScript/TypeScript: Security-focused ESLint rules
Args:
code: Source code to scan for vulnerabilities
language: Programming language (python, javascript, typescript, or auto)
Returns:
Security scan results with vulnerability details
"""
try:
if not code or not code.strip():
return {"error": "Code cannot be empty", "vulnerabilities": []}
if language == "auto":
language = detect_language(code)
# Python security scanning
if language == "python":
from src.analyzers.python_analyzer import scan_security_python
result = await scan_security_python(code)
return result
# JavaScript security would use ESLint security rules
elif language in ["javascript", "typescript"]:
analyzer = JavaScriptAnalyzer()
result = await analyzer.analyze(code, language=language)
# Filter for security issues only
security_issues = [
issue for issue in result.get("issues", [])
if "security" in issue.get("message", "").lower()
]
return {
"vulnerabilities": security_issues,
"summary": {
"total": len(security_issues),
"high": sum(1 for i in security_issues if i.get("severity") == "error"),
"medium": sum(1 for i in security_issues if i.get("severity") == "warning")
}
}
else:
return {"error": f"Security scanning not supported for: {language}", "vulnerabilities": []}
except Exception as e:
logger.error(f"check_security failed: {e}", exc_info=True)
return {"error": str(e), "vulnerabilities": []}
@mcp.tool()
async def complexity_score(code: str, language: str = "auto") -> dict[str, Any]:
"""
Calculate code complexity metrics and maintainability index.
Metrics include:
- Cyclomatic complexity
- Maintainability index
- Function count
- Lines of code
Args:
code: Source code to analyze
language: Programming language (python, javascript, typescript, or auto)
Returns:
Complexity metrics dictionary
"""
try:
if not code or not code.strip():
return {"error": "Code cannot be empty", "complexity": {}}
if language == "auto":
language = detect_language(code)
if language == "python":
from src.analyzers.python_analyzer import calculate_complexity_python
result = await calculate_complexity_python(code)
return result
elif language in ["javascript", "typescript"]:
from src.analyzers.javascript_analyzer import calculate_complexity_javascript
result = await calculate_complexity_javascript(code)
return result
else:
return {"error": f"Complexity analysis not supported for: {language}", "complexity": {}}
except Exception as e:
logger.error(f"complexity_score failed: {e}", exc_info=True)
return {"error": str(e), "complexity": {}}
@mcp.tool()
async def suggest_fixes(code: str, language: str = "auto", model: str = "grok-4.1") -> dict[str, Any]:
"""
AI-powered fix suggestions for code issues.
Uses premium AI models to:
- Identify problems in code
- Generate fix suggestions with explanations
- Provide complete corrected code
Args:
code: Source code with issues
language: Programming language (auto-detected if not specified)
model: AI model to use (default: grok-4.1 free)
Returns:
Fix suggestions with explanations and corrected code
"""
try:
if not code or not code.strip():
return {"error": "Code cannot be empty", "suggestions": []}
if language == "auto":
language = detect_language(code)
# First analyze to find issues
analyzer_result = await analyze_code(code=code, language=language)
issues = analyzer_result.get("issues", [])
if not issues:
return {
"message": "No issues found - code looks good!",
"suggestions": []
}
# Prepare prompt for AI
issues_summary = "\\n".join([
f"- Line {issue.get('line')}: {issue.get('message')}"
for issue in issues[:10] # Limit to first 10 issues
])
prompt = f"""Analyze this {language} code and suggest fixes for the following issues:
```{language}
{code}
```
Issues found:
{issues_summary}
Please provide:
1. Explanation of each issue
2. How to fix it
3. Complete corrected code
Be concise but comprehensive."""
# Get AI response
ai_response = await generate_ai_response(
prompt=prompt,
model_name=model
)
return {
"issues_found": len(issues),
"ai_suggestions": ai_response,
"model_used": model
}
except Exception as e:
logger.error(f"suggest_fixes failed: {e}", exc_info=True)
return {"error": str(e), "suggestions": []}
@mcp.tool()
async def get_server_info() -> dict[str, Any]:
"""
Get server capabilities, supported languages, and available AI models.
Returns:
Server information including tools, resources, and models
"""
config = Config()
return {
"server": "CodeLint MCP Premium",
"version": "2.0.0",
"tools": [
"analyze_code", "check_security", "complexity_score",
"suggest_fixes", "analyze_project", "analyze_git_diff",
"explain_code", "generate_tests", "generate_docs", "get_server_info"
],
"supported_languages": [
"python", "javascript", "typescript"
],
"analyzers": {
"python": ["ruff", "bandit", "radon"],
"javascript": ["eslint", "complexity"],
"typescript": ["eslint", "complexity"]
},
"ai_models": config.get_dropdown_options(),
"features": [
"Multi-file project analysis",
"Git diff analysis",
"AI-powered explanations",
"Test generation",
"Documentation generation",
"9 AI model options (3 premium, 6 free)"
]
}
# ============================================================================
# COMPETITIVE TOOLS (5 Advanced)
# ============================================================================
@mcp.tool()
async def analyze_project(project_path: str, max_files: int = 100) -> dict[str, Any]:
"""
Analyze an entire project with multiple files.
Features:
- Parallel file processing
- Multi-language support
- Aggregated results across all files
- Automatic exclusion of common directories (node_modules, __pycache__, etc.)
Args:
project_path: Root directory of the project
max_files: Maximum number of files to analyze (default: 100)
Returns:
Aggregated analysis results for the entire project
"""
try:
result = await analyze_project(project_path=project_path, max_files=max_files)
return result
except Exception as e:
logger.error(f"analyze_project failed: {e}", exc_info=True)
return {"error": str(e), "files_analyzed": 0}
@mcp.tool()
async def analyze_git_diff(repo_path: str, base_ref: str = "HEAD") -> dict[str, Any]:
"""
Analyze only changed files in a Git diff.
Perfect for CI/CD integration and pull request reviews.
Args:
repo_path: Path to Git repository
base_ref: Base reference for comparison (default: HEAD)
Returns:
Analysis results for changed files only
"""
try:
result = await analyze_git_diff(repo_path=repo_path, base_ref=base_ref)
return result
except Exception as e:
logger.error(f"analyze_git_diff failed: {e}", exc_info=True)
return {"error": str(e), "files_changed": 0}
@mcp.tool()
async def explain_code(code: str, language: str = "auto", model: str = "grok-4.1") -> dict[str, Any]:
"""
AI-powered code explanation.
Get clear explanations of what code does, how it works, and potential issues.
Args:
code: Source code to explain
language: Programming language (auto-detected if not specified)
model: AI model to use (default: grok-4.1 free)
Returns:
Detailed code explanation
"""
try:
if not code or not code.strip():
return {"error": "Code cannot be empty"}
if language == "auto":
language = detect_language(code)
prompt = f"""Explain this {language} code in detail:
```{language}
{code}
```
Please provide:
1. What the code does (high-level overview)
2. How it works (step-by-step breakdown)
3. Any potential issues or improvements
4. Best practices that are or aren't being followed
Be clear and educational."""
explanation = await generate_ai_response(prompt=prompt, model_name=model)
return {
"language": language,
"explanation": explanation,
"model_used": model
}
except Exception as e:
logger.error(f"explain_code failed: {e}", exc_info=True)
return {"error": str(e)}
@mcp.tool()
async def generate_tests(code: str, language: str = "auto", model: str = "grok-4.1") -> dict[str, Any]:
"""
AI-powered test generation.
Generate comprehensive unit tests for your code.
Args:
code: Source code to generate tests for
language: Programming language (auto-detected if not specified)
model: AI model to use (default: grok-4.1 free)
Returns:
Generated test code with test cases
"""
try:
if not code or not code.strip():
return {"error": "Code cannot be empty"}
if language == "auto":
language = detect_language(code)
# Determine test framework
test_framework = {
"python": "pytest",
"javascript": "jest",
"typescript": "jest"
}.get(language, "unittest")
prompt = f"""Generate comprehensive unit tests for this {language} code using {test_framework}:
```{language}
{code}
```
Please provide:
1. Complete test file with all necessary imports
2. Test cases covering:
- Normal/happy path scenarios
- Edge cases
- Error conditions
- Boundary conditions
3. Clear test names and docstrings
4. Setup/teardown if needed
Make tests production-ready and well-documented."""
tests = await generate_ai_response(prompt=prompt, model_name=model)
return {
"language": language,
"test_framework": test_framework,
"tests": tests,
"model_used": model
}
except Exception as e:
logger.error(f"generate_tests failed: {e}", exc_info=True)
return {"error": str(e)}
@mcp.tool()
async def generate_docs(code: str, language: str = "auto", model: str = "grok-4.1") -> dict[str, Any]:
"""
AI-powered documentation generation.
Generate comprehensive documentation including docstrings, comments, and README.
Args:
code: Source code to document
language: Programming language (auto-detected if not specified)
model: AI model to use (default: grok-4.1 free)
Returns:
Generated documentation in appropriate format
"""
try:
if not code or not code.strip():
return {"error": "Code cannot be empty"}
if language == "auto":
language = detect_language(code)
prompt = f"""Generate comprehensive documentation for this {language} code:
```{language}
{code}
```
Please provide:
1. Module/file-level docstring
2. Function/class docstrings following best practices:
- Python: Google/NumPy style
- JavaScript/TypeScript: JSDoc
3. Inline comments for complex logic
4. Usage examples
5. Parameter descriptions and return types
Make documentation clear, complete, and professional."""
docs = await generate_ai_response(prompt=prompt, model_name=model)
return {
"language": language,
"documentation": docs,
"model_used": model
}
except Exception as e:
logger.error(f"generate_docs failed: {e}", exc_info=True)
return {"error": str(e)}
@mcp.tool()
async def prioritize_issues(code: str, language: str = "auto") -> dict[str, Any]:
"""
Smart issue prioritization with severity, impact, and fix effort analysis.
Enriches analysis results with:
- Priority scoring (Critical/High/Medium/Low)
- Impact categories (Security/Reliability/Performance/Style)
- Fix effort estimation (Quick/Medium/Major)
- Time to fix estimates
- Statistics and quick wins identification
Args:
code: Source code to analyze and prioritize
language: Programming language (auto-detected if not specified)
Returns:
Prioritized issues with rich metadata and statistics
"""
try:
# First run analysis
result = await analyze_code(code, language)
issues = result.get("issues", [])
if not issues:
return {
"prioritized_issues": [],
"statistics": {},
"message": "No issues found!"
}
# Import prioritization system
from src.utils.prioritization import IssuePrioritizer, format_priority_report
# Prioritize and enrich issues
prioritized = IssuePrioritizer.prioritize_issues(issues)
stats = IssuePrioritizer.get_statistics(prioritized)
report = format_priority_report(prioritized)
return {
"prioritized_issues": prioritized,
"statistics": stats,
"report": report,
"language": result.get("language"),
"total_issues": len(prioritized)
}
except Exception as e:
logger.error(f"prioritize_issues failed: {e}", exc_info=True)
return {"error": str(e)}
@mcp.tool()
async def auto_fix_code(code: str, language: str = "auto", preview_only: bool = False) -> dict[str, Any]:
"""
Auto-fix common code issues with preview and batch capabilities.
Automatically fixes:
- Missing semicolons
- console.log/debugger statements
- Trailing whitespace
- var to const/let
- == to ===
- Unused variables (prefix with _)
Args:
code: Source code to fix
language: Programming language (auto-detected if not specified)
preview_only: If True, only show previews without applying fixes
Returns:
Fixed code with list of applied fixes
"""
try:
# First run analysis
result = await analyze_code(code, language)
issues = result.get("issues", [])
if not issues:
return {
"fixed_code": code,
"applied_fixes": [],
"message": "No issues to fix!"
}
# Import auto-fix engine
from src.utils.auto_fix import AutoFixer, format_fix_report
if preview_only:
# Generate fix summary with previews
fix_summary = AutoFixer.get_fix_summary(code, issues)
report = format_fix_report(fix_summary)
return {
"preview_mode": True,
"fix_summary": fix_summary,
"report": report,
"original_code": code
}
else:
# Apply all fixes
fixed_code, applied_fixes = AutoFixer.batch_fix(code, issues)
return {
"fixed_code": fixed_code,
"applied_fixes": applied_fixes,
"fixes_count": len(applied_fixes),
"original_code": code,
"language": result.get("language")
}
except Exception as e:
logger.error(f"auto_fix_code failed: {e}", exc_info=True)
return {"error": str(e)}
@mcp.tool()
async def analyze_dependencies(project_path: str) -> dict[str, Any]:
"""
Analyze project dependencies for vulnerabilities and outdated packages.
Checks for:
- Known CVEs in dependencies
- Outdated packages
- Security vulnerabilities
- License compatibility issues
Supports:
- Node.js (package.json)
- Python (requirements.txt)
Args:
project_path: Path to project directory
Returns:
Dependency analysis with vulnerabilities and recommendations
"""
try:
from src.utils.dependency_analyzer import DependencyAnalyzer, format_dependency_report
analysis = DependencyAnalyzer.analyze_dependencies(project_path)
report = format_dependency_report(analysis)
return {
"analysis": analysis,
"report": report,
"project_path": project_path
}
except Exception as e:
logger.error(f"analyze_dependencies failed: {e}", exc_info=True)
return {"error": str(e)}
@mcp.tool()
async def detect_duplication(code: str, language: str = "auto", min_lines: int = 5) -> dict[str, Any]:
"""
Detect code duplication and suggest DRY refactoring.
Finds:
- Copy-pasted code blocks
- Similar code patterns
- Refactoring opportunities
Args:
code: Source code to analyze
language: Programming language (auto-detected if not specified)
min_lines: Minimum lines to consider as duplication (default: 5)
Returns:
Duplication analysis with refactoring suggestions
"""
try:
from src.utils.duplication_detector import DuplicationDetector, format_duplication_report
detector = DuplicationDetector(min_lines=min_lines)
analysis = detector.analyze_duplication(code)
report = format_duplication_report(analysis)
return {
"analysis": analysis,
"report": report,
"language": language if language != "auto" else detect_language(code)
}
except Exception as e:
logger.error(f"detect_duplication failed: {e}", exc_info=True)
return {"error": str(e)}
# ============================================================================
# RESOURCES (Static Information)
# ============================================================================
@mcp.resource("guide://best-practices")
async def best_practices_guide() -> str:
"""Code quality and best practices guide"""
return """
# Code Quality Best Practices
## Python
- Use type hints for better code clarity
- Follow PEP 8 style guide
- Keep functions small and focused
- Use descriptive variable names
- Handle exceptions properly
- Write docstrings for all public functions
- Avoid mutable default arguments
- Use context managers for resources
## JavaScript/TypeScript
- Use const/let instead of var
- Enable strict mode
- Handle promises properly
- Use async/await for async code
- Validate inputs
- Use === instead of ==
- Keep functions pure when possible
- Use TypeScript for large projects
## Security
- Never use eval() or exec()
- Validate and sanitize all inputs
- Use parameterized queries for databases
- Keep dependencies updated
- Never commit secrets or credentials
- Use HTTPS for all external communications
"""
@mcp.resource("guide://security")
async def security_guidelines() -> str:
"""Security scanning and vulnerability prevention guide"""
return """
# Security Guidelines
## Common Vulnerabilities
### Python
- **Code Injection**: Avoid eval(), exec(), compile() with user input
- **Deserialization**: Never use pickle.loads() on untrusted data
- **Path Traversal**: Validate file paths, don't allow ../
- **SQL Injection**: Use parameterized queries
- **Command Injection**: Avoid shell=True in subprocess
### JavaScript/TypeScript
- **XSS**: Sanitize all user inputs before rendering
- **Prototype Pollution**: Avoid Object.assign with user data
- **ReDoS**: Be careful with complex regular expressions
- **Path Traversal**: Validate file paths
- **SQL Injection**: Use parameterized queries
## Best Practices
- Principle of least privilege
- Defense in depth
- Input validation and sanitization
- Secure defaults
- Regular security updates
- Security testing in CI/CD
"""
@mcp.resource("guide://complexity")
async def complexity_guide() -> str:
"""Complexity metrics and maintainability guide"""
return """
# Complexity and Maintainability
## Cyclomatic Complexity
- **1-10**: Simple, easy to test
- **11-20**: Moderate, needs attention
- **21-50**: Complex, hard to maintain
- **50+**: Very complex, refactor recommended
## Maintainability Index
- **85-100**: Highly maintainable (Green)
- **65-84**: Moderately maintainable (Yellow)
- **0-64**: Hard to maintain (Red)
## Tips to Reduce Complexity
- Extract methods/functions
- Use early returns
- Replace nested conditions with guard clauses
- Apply design patterns
- Break large functions into smaller ones
- Use polymorphism instead of conditionals
"""
# ============================================================================
# GRADIO UI INTEGRATION
# ============================================================================
def create_gradio_ui():
"""Create premium Gradio UI integrated with MCP"""
import gradio as gr
# Get config instance
cfg = Config()
# Custom CSS for premium look
CUSTOM_CSS = """
.gradio-container {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
}
.contain {
background: rgba(17, 24, 39, 0.95) !important;
backdrop-filter: blur(20px) !important;
border-radius: 24px !important;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5) !important;
}
.gr-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border-radius: 12px !important;
transition: all 0.3s !important;
}
.gr-button:hover {
transform: translateY(-2px) !important;
}
"""
# Get model options
model_options = cfg.get_dropdown_options()
with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"), css=CUSTOM_CSS) as demo:
gr.Markdown("""
<div style='text-align: center; padding: 20px 0;'>
<h1>🎨 CodeLint Premium - MCP Edition</h1>
<h3 style='color: #667eea; margin: 10px 0;'>Professional Code Analysis with AI-Powered Insights</h3>
<p style='color: #9ca3af; font-size: 14px;'>Connected to FastMCP Server with 10 tools and 9 AI models</p>
</div>
""")
with gr.Tabs():
# TAB 1: Code Analysis
with gr.Tab("πŸ“ Code Analysis"):
with gr.Row():
with gr.Column(scale=2):
code_input = gr.Textbox(
label="Source Code",
placeholder="Paste your code here...",
lines=15
)
with gr.Column(scale=1):
language = gr.Dropdown(
choices=["auto", "python", "javascript", "typescript"],
value="auto",
label="Language"
)
model = gr.Dropdown(
choices=model_options,
value="πŸ†“ Grok 4.1 Fast (OpenRouter)",
label="AI Model"
)
analyze_btn = gr.Button("πŸš€ Analyze Code", variant="primary")
results_md = gr.Markdown(label="Results")
results_json = gr.JSON(label="Raw Results")
async def analyze_ui(code: str, lang: str, model_name: str):
if not code.strip():
return "❌ Please enter code", None
try:
# Call analyzer directly, not the FastMCP tool
original_lang = lang
if lang == "auto":
lang = detect_language(code)
# Log for debugging
import sys
print(f"DEBUG: Original lang: {original_lang}, Detected lang: {lang}", file=sys.stderr)
print(f"DEBUG: Code preview: {code[:100]}...", file=sys.stderr)
if lang == "python":
analyzer = PythonAnalyzer()
result = await analyzer.analyze(code)
elif lang in ["javascript", "typescript"]:
analyzer = JavaScriptAnalyzer()
result = await analyzer.analyze(code, language=lang)
else:
return f"❌ Unsupported language: {lang}", None
# Extract data from result
issues = result.get("issues", [])
summary = result.get("summary", {})
# Log for debugging
import sys
print(f"DEBUG: Found {len(issues)} issues", file=sys.stderr)
print(f"DEBUG: Summary: {summary}", file=sys.stderr)
if issues:
print(f"DEBUG: First issue: {issues[0]}", file=sys.stderr)
output = f"""
# πŸ“Š Analysis Results
## 🎯 Summary
- **Total Issues**: {len(issues)}
- **Errors**: {summary.get('errors', 0)} πŸ”΄
- **Warnings**: {summary.get('warnings', 0)} 🟑
- **Security**: {summary.get('security_issues', 0)} πŸ›‘οΈ
## πŸ› Issues Found
"""
if not issues:
output += "\nβœ… **No issues found! Code looks clean.**\n"
else:
for i, issue in enumerate(issues[:20], 1):
emoji = "πŸ”΄" if issue.get("severity") == "error" else "🟑"
line = issue.get('line', 'N/A')
col = issue.get('column', '')
location = f"Line {line}" + (f":{col}" if col else "")
message = issue.get('message', 'No message')
rule_id = issue.get('rule_id', '')
output += f"\n{emoji} **Issue {i}** ({location})\n"
output += f" {message}\n"
if rule_id:
output += f" *Rule: {rule_id}*\n"
if len(issues) > 20:
output += f"\n\n*... and {len(issues) - 20} more issues*"
return output, result
except Exception as e:
import traceback
print(f"ERROR: {traceback.format_exc()}", file=sys.stderr)
return f"❌ Error: {str(e)}", None
analyze_btn.click(
fn=analyze_ui,
inputs=[code_input, language, model],
outputs=[results_md, results_json]
)
# Helper function for running analyzer
async def _run_analyzer(code: str, lang: str):
"""Helper to run code analysis"""
if lang == "python":
analyzer = PythonAnalyzer()
return await analyzer.analyze(code)
elif lang in ["javascript", "typescript"]:
analyzer = JavaScriptAnalyzer()
return await analyzer.analyze(code, language=lang)
else:
return {"issues": [], "summary": {}}
# Helper function for model mapping (used by all AI features)
def _get_model_map():
"""Map UI model names to actual model IDs"""
return {
"⭐ OpenAI GPT-5 Preview": "gpt-5",
"⭐ Google Gemini 3 Pro": "gemini-3",
"⭐ Claude Sonnet 4.5": "claude-sonnet",
"πŸ†“ Grok 4.1 Fast (OpenRouter)": "grok-4.1",
"πŸ†“ KAT-Coder-Pro V1": "kat-coder",
"πŸ†“ Qwen3-Coder-32B": "qwen-coder",
"πŸ†“ LongCat 7B (OpenRouter)": "longcat",
"πŸ†“ GPT-OSS 4o (OpenRouter)": "gpt-oss",
"πŸ†“ Kimi K2 128k": "kimi"
}
# TAB 2: Project Analysis
with gr.Tab("πŸ“¦ Project Analysis"):
project_path = gr.Textbox(label="Project Path", placeholder="C:\\path\\to\\project")
max_files = gr.Slider(10, 500, 100, step=10, label="Max Files")
project_btn = gr.Button("πŸ“Š Analyze Project", variant="primary")
project_results = gr.Markdown()
project_json = gr.JSON()
async def project_ui(path: str, max_f: int):
if not path.strip():
return "❌ Enter project path", None
try:
# Call analyzer function directly
from src.analyzers.project_analyzer import analyze_project as analyze_proj
result = await analyze_proj(project_path=path, max_files=max_f)
summary = result.get("summary", {})
metadata = result.get("metadata", {})
output = f"""
# πŸ“¦ Project Analysis
## πŸ“Š Summary
- **Files**: {summary.get('files_analyzed', 0)}
- **Errors**: {summary.get('total_errors', 0)} πŸ”΄
- **Warnings**: {summary.get('total_warnings', 0)} 🟑
- **Security**: {summary.get('total_security_issues', 0)} πŸ›‘οΈ
- **Lines**: {metadata.get('total_lines_of_code', 0):,}
## πŸ“ Languages
{', '.join(metadata.get('languages', []))}
"""
return output, result
except Exception as e:
return f"❌ Error: {str(e)}", None
project_btn.click(
fn=project_ui,
inputs=[project_path, max_files],
outputs=[project_results, project_json]
)
# TAB 3: Git Diff
with gr.Tab("πŸ”„ Git Diff"):
repo_path = gr.Textbox(label="Repo Path", placeholder="C:\\path\\to\\repo")
base_ref = gr.Textbox(label="Base Ref", value="HEAD")
git_btn = gr.Button("πŸ” Analyze Changes", variant="primary")
git_results = gr.Markdown()
git_json = gr.JSON()
async def git_ui(repo: str, base: str):
if not repo.strip():
return "❌ Enter repo path", None
try:
# Call analyzer function directly
from src.analyzers.git_analyzer import analyze_git_diff as analyze_git
result = await analyze_git(repo_path=repo, base_ref=base)
summary = result.get("summary", {})
output = f"""
# πŸ”„ Git Diff Analysis
## πŸ“Š Summary
- **Files Changed**: {summary.get('files_changed', 0)}
- **Errors**: {summary.get('total_errors', 0)} πŸ”΄
- **Warnings**: {summary.get('total_warnings', 0)} 🟑
- **Security**: {summary.get('total_security_issues', 0)} πŸ›‘οΈ
"""
return output, result
except Exception as e:
return f"❌ Error: {str(e)}", None
git_btn.click(
fn=git_ui,
inputs=[repo_path, base_ref],
outputs=[git_results, git_json]
)
# TAB 4: AI Assistant
with gr.Tab("πŸ€– AI Assistant"):
ai_code = gr.Textbox(label="Code", lines=10)
ai_lang = gr.Dropdown(["auto", "python", "javascript", "typescript"], value="auto", label="Language")
ai_model = gr.Dropdown(model_options, value="πŸ†“ Grok 4.1 Fast (OpenRouter)", label="Model")
with gr.Tabs():
with gr.Tab("Explain"):
explain_btn = gr.Button("πŸ’‘ Explain")
explain_out = gr.Markdown()
async def explain_ui(code: str, lang: str, model_name: str):
if not code.strip():
return "❌ Enter code"
try:
# Map display name to actual model ID
actual_model = _get_model_map().get(model_name, "grok-4.1")
# Use AI client directly
if lang == "auto":
lang = detect_language(code)
prompt = f"""Explain this {lang} code in detail:
```{lang}
{code}
```
Provide a clear, educational explanation covering:
1. What the code does
2. How it works
3. Potential issues or improvements
"""
explanation = await generate_ai_response(prompt=prompt, model_name=actual_model)
return f"# πŸ€– Explanation\n\n{explanation}"
except Exception as e:
return f"❌ Error: {str(e)}"
explain_btn.click(fn=explain_ui, inputs=[ai_code, ai_lang, ai_model], outputs=[explain_out])
with gr.Tab("Generate Tests"):
tests_btn = gr.Button("πŸ§ͺ Generate Tests")
tests_out = gr.Markdown()
async def tests_ui(code: str, lang: str, model_name: str):
if not code.strip():
return "❌ Enter code"
try:
# Map display name to actual model ID
actual_model = _get_model_map().get(model_name, "grok-4.1")
# Use AI client directly
if lang == "auto":
lang = detect_language(code)
test_framework = {"python": "pytest", "javascript": "jest", "typescript": "jest"}.get(lang, "unittest")
prompt = f"""Generate comprehensive tests for this {lang} code using {test_framework}:
```{lang}
{code}
```
Include:
- Happy path tests
- Edge cases
- Error conditions
- Clear test names
"""
tests = await generate_ai_response(prompt=prompt, model_name=actual_model)
return f"# πŸ§ͺ Tests\n\n```\n{tests}\n```"
except Exception as e:
return f"❌ Error: {str(e)}"
tests_btn.click(fn=tests_ui, inputs=[ai_code, ai_lang, ai_model], outputs=[tests_out])
# TAB 5: Smart Prioritization
with gr.Tab("🎯 Smart Prioritization"):
prio_code = gr.Textbox(label="Code to Analyze", lines=15, placeholder="Paste your code here...")
prio_lang = gr.Dropdown(["auto", "python", "javascript", "typescript"], value="auto", label="Language")
prio_btn = gr.Button("πŸ“Š Prioritize Issues", variant="primary")
prio_stats = gr.Markdown()
prio_json = gr.JSON()
async def prioritize_ui(code: str, lang: str):
if not code.strip():
return "❌ Enter code", None
try:
if lang == "auto":
lang = detect_language(code)
# Analyze code first
result = await _run_analyzer(code, lang)
issues = result.get("issues", [])
if not issues:
return "βœ… No issues found!", result
# Prioritize issues
from src.utils.prioritization import IssuePrioritizer
prioritizer = IssuePrioritizer()
prioritized_issues = prioritizer.prioritize_issues(issues)
stats = prioritizer.get_statistics(prioritized_issues)
top_issue = prioritized_issues[0] if prioritized_issues else None
output = f"""
# 🎯 Issue Prioritization Report
## πŸ“Š Statistics
- **Total Issues**: {stats['total']}
- **By Severity**: Critical: {stats['by_severity'].get('critical', 0)}, High: {stats['by_severity'].get('high', 0)}, Medium: {stats['by_severity'].get('medium', 0)}, Low: {stats['by_severity'].get('low', 0)}
- **Estimated Fix Time**: {stats['total_fix_time_minutes']} minutes
- **Quick Wins**: {stats['quick_wins']} issues
## πŸ”₯ Top Priority Issue
"""
if top_issue:
metadata = top_issue.get('metadata', {})
output += f"""
- **Line {top_issue['line']}**: {top_issue['message']}
- **Priority Score**: {top_issue.get('priority_score', 0)}
- **Severity**: {top_issue.get('priority_severity', 'unknown')}
- **Fix Effort**: {metadata.get('fix_effort', 'unknown')} (~{metadata.get('fix_time_minutes', 0)} min)
"""
output += "\n## πŸ“‹ All Issues (Prioritized)\n\n"
for i, issue in enumerate(prioritized_issues[:10], 1):
severity = issue.get('priority_severity', 'unknown')
output += f"{i}. **[{severity.upper()}]** Line {issue['line']}: {issue['message']} (Score: {issue.get('priority_score', 0)})\n"
if len(prioritized_issues) > 10:
output += f"\n... and {len(prioritized_issues) - 10} more issues"
return output, {"issues": prioritized_issues, "statistics": stats}
except Exception as e:
import traceback
return f"❌ Error: {str(e)}\n\n{traceback.format_exc()}", None
prio_btn.click(fn=prioritize_ui, inputs=[prio_code, prio_lang], outputs=[prio_stats, prio_json])
# TAB 6: Auto-Fix
with gr.Tab("πŸ”§ Auto-Fix"):
fix_code = gr.Textbox(label="Code with Issues", lines=15, placeholder="Paste your code here...")
fix_lang = gr.Dropdown(["auto", "python", "javascript", "typescript"], value="auto", label="Language")
fix_btn = gr.Button("⚑ Auto-Fix Issues", variant="primary")
fix_results = gr.Markdown()
with gr.Row():
fix_code_before = gr.Code(label="❌ Before (Original)", lines=10, language="python", interactive=False)
fix_code_after = gr.Code(label="βœ… After (Fixed)", lines=10, language="python", interactive=False)
async def autofix_ui(code: str, lang: str):
if not code.strip():
return "❌ Enter code", code, code
try:
if lang == "auto":
lang = detect_language(code)
# Analyze code
result = await _run_analyzer(code, lang)
issues = result.get("issues", [])
if not issues:
return "βœ… No issues found to fix!", code, code
# Apply auto-fixes using AutoFixer class
from src.utils.auto_fix import AutoFixer
fixer = AutoFixer()
fixed_code, applied_fixes = fixer.batch_fix(code, issues)
# Get manual review issues
manual_review = [issue for issue in issues if not any(fix['line'] == issue.get('line') for fix in applied_fixes)]
output = f"""
# πŸ”§ Auto-Fix Report
## πŸ“Š Summary
- **Total Issues**: {len(issues)}
- **Fixed**: {len(applied_fixes)} ({len(applied_fixes)/len(issues)*100:.1f}%)
- **Manual Review Needed**: {len(manual_review)}
## βœ… Fixes Applied
"""
for fix in applied_fixes[:10]:
output += f"- Line {fix['line']}: {fix['fix_description']}\n"
if len(applied_fixes) > 10:
output += f"\n... and {len(applied_fixes) - 10} more fixes"
if manual_review:
output += "\n\n## ⚠️ Manual Review Required\n"
for issue in manual_review[:5]:
output += f"- Line {issue.get('line', 'N/A')}: {issue.get('message', 'Unknown issue')}\n"
return output, code, fixed_code
except Exception as e:
import traceback
return f"❌ Error: {str(e)}\n\n{traceback.format_exc()}", code, code
fix_btn.click(fn=autofix_ui, inputs=[fix_code, fix_lang], outputs=[fix_results, fix_code_before, fix_code_after])
# TAB 7: Duplication Detector
with gr.Tab("πŸ” Duplication Detection"):
dup_code = gr.Textbox(label="Code to Analyze", lines=15, placeholder="Paste your code here...")
dup_threshold = gr.Slider(50, 100, 85, step=5, label="Similarity Threshold (%)")
dup_btn = gr.Button("πŸ”Ž Detect Duplicates", variant="primary")
dup_results = gr.Markdown()
dup_json = gr.JSON()
async def duplication_ui(code: str, threshold: int):
if not code.strip():
return "❌ Enter code", None
try:
from src.utils.duplication_detector import DuplicationDetector
detector = DuplicationDetector(similarity_threshold=threshold / 100.0)
result = detector.analyze_duplication(code)
stats = result["statistics"]
dup_count = result["duplicates_found"]
severity = result["severity"]
output = f"""
# πŸ” Code Duplication Report
## πŸ“Š Statistics
- **Total Lines**: {stats['total_lines']}
- **Duplicated Lines**: {stats['duplicated_lines']}
- **Duplication Rate**: {stats['duplication_percentage']}
- **Duplicate Blocks**: {dup_count}
- **Severity**: {severity.upper()}
## πŸ”„ Duplicate Blocks Found
"""
for i, dup in enumerate(result["duplicates"][:10], 1):
output += f"""
### Block {i} (Similarity: {dup['similarity']:.1f}%)
- **Location 1**: Lines {dup['block1']['start']}-{dup['block1']['end']}
- **Location 2**: Lines {dup['block2']['start']}-{dup['block2']['end']}
- **Suggestion**: {dup['suggestion']}
"""
if len(result["duplicates"]) > 10:
output += f"\n... and {len(result['duplicates']) - 10} more duplicates"
return output, result
except Exception as e:
import traceback
return f"❌ Error: {str(e)}\n\n{traceback.format_exc()}", None
dup_btn.click(fn=duplication_ui, inputs=[dup_code, dup_threshold], outputs=[dup_results, dup_json])
# TAB 8: Server Info
with gr.Tab("ℹ️ About"):
gr.Markdown("""
# 🎨 CodeLint Premium - MCP Edition
## ✨ Features
- **10 MCP Tools**: Complete analysis suite
- **9 AI Models**: 3 premium ⭐ + 6 free πŸ†“
- **Multi-Language**: Python, JavaScript, TypeScript
- **MCP Protocol**: Fully integrated FastMCP server
## πŸ”§ Tools
- analyze_code, check_security, complexity_score
- suggest_fixes, analyze_project, analyze_git_diff
- explain_code, generate_tests, generate_docs
- get_server_info
## πŸ“š Resources
- Best practices guide
- Security guidelines
- Complexity guide
---
πŸ’Ž **Premium Edition** | πŸ† **MCP Integrated** | πŸš€ **Production Ready**
""")
gr.Markdown("""
<div style='text-align: center; padding: 15px 0; color: #9ca3af; font-size: 13px;'>
<em>Powered by FastMCP Server with 9 AI models</em>
</div>
""")
return demo
# ============================================================================
# RUN SERVER
# ============================================================================
def main():
"""Start the FastMCP server with Gradio UI"""
logger.info("πŸš€ Starting CodeLint Premium MCP Server...")
logger.info(f"πŸ“¦ 10 tools available")
logger.info(f"πŸ“š 3 resources available")
logger.info("🎨 Launching Gradio UI...")
# Create and mount Gradio UI
demo = create_gradio_ui()
# Launch Gradio
demo.launch(
server_name="0.0.0.0",
server_port=7861,
share=False,
inbrowser=True
)
if __name__ == "__main__":
main()