bestpractices_mcp_server.py•23.2 kB
"""
Best Practices MCP Server
A Model Context Protocol server that provides Python coding best practices
and guidelines for general coding and API development to AI assistants.
"""
import os
import logging
from typing import Optional
from fastmcp import FastMCP
from data_manager import (
search_practices,
get_category,
list_all_categories,
get_examples,
FileLoadError,
ValidationError,
)
# Configure logging
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
logging.basicConfig(
level=getattr(logging, LOG_LEVEL),
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
# Initialize FastMCP server
mcp = FastMCP("bestpractices-mcp-server")
logger.info("Best Practices MCP Server initialized")
# Tool 1: search_best_practices
@mcp.tool()
def search_best_practices(keyword: str) -> dict:
"""
Search for best practices by keyword across all categories.
Searches in descriptions, examples, and topic names for the keyword.
Results are organized by category and include related topics.
Args:
keyword: Search term to find in descriptions, examples, or topic names
Returns:
Dictionary containing matching practices with descriptions, examples, and related topics
"""
logger.info(f"Searching for keyword: {keyword}")
if not keyword or not keyword.strip():
return {
"error": "invalid_input",
"message": "Search keyword cannot be empty",
"suggestion": "Please provide a non-empty search term",
}
try:
result = search_practices(keyword)
logger.info(f"Search returned {result.get('count', 0)} matches")
return result
except Exception as e:
logger.error(f"Search failed: {e}")
return {
"error": "search_failed",
"message": f"Search failed: {str(e)}",
"suggestion": "Please try again or check the server logs",
}
# Tool 2: get_practice_by_category
@mcp.tool()
def get_practice_by_category(category: str, topic: Optional[str] = None) -> dict:
"""
Retrieve best practices for a specific category or topic.
Args:
category: Category name (general_coding, fastapi_specific, performance, code_quality)
topic: Optional specific topic within the category
Returns:
Dictionary containing practice details with descriptions, examples, and links
"""
logger.info(f"Getting category: {category}, topic: {topic}")
try:
result = get_category(category, topic)
return result
except Exception as e:
logger.error(f"Failed to get category: {e}")
return {
"error": "retrieval_failed",
"message": f"Failed to retrieve category: {str(e)}",
}
# Tool 3: list_categories
@mcp.tool()
def list_categories() -> dict:
"""
List all available best practice categories.
Returns:
Dictionary with category names, descriptions, and topic counts
"""
logger.info("Listing all categories")
try:
result = list_all_categories()
return result
except Exception as e:
logger.error(f"Failed to list categories: {e}")
return {
"error": "listing_failed",
"message": f"Failed to list categories: {str(e)}",
}
# Tool 4: get_examples
@mcp.tool()
def get_examples_tool(topic: str) -> dict:
"""
Get code examples for a specific topic.
Args:
topic: Topic name to retrieve examples for
Returns:
Dictionary containing code examples with context and explanations
"""
logger.info(f"Getting examples for topic: {topic}")
if not topic or not topic.strip():
return {
"error": "invalid_input",
"message": "Topic name cannot be empty",
"suggestion": "Please provide a valid topic name",
}
try:
result = get_examples(topic)
return result
except Exception as e:
logger.error(f"Failed to get examples: {e}")
return {
"error": "retrieval_failed",
"message": f"Failed to retrieve examples: {str(e)}",
}
if __name__ == "__main__":
logger.info("Starting Best Practices MCP Server")
mcp.run()
# Tool 5: review_code
def _review_code_impl(code: str, context: str = "general") -> dict:
"""
Review code against best practices and provide feedback.
Analyzes code and identifies violations, good patterns, and provides
specific recommendations with examples.
Args:
code: Code snippet to review
context: Context type - "general" for Python or "fastapi" for FastAPI code
Returns:
Dictionary with violations, recommendations, and good patterns identified
"""
logger.info(f"Reviewing code with context: {context}")
if not code or not code.strip():
return {
"error": "invalid_input",
"message": "Code cannot be empty",
"suggestion": "Please provide code to review",
}
if context not in ["general", "fastapi"]:
return {
"error": "invalid_context",
"message": f"Invalid context '{context}'. Must be 'general' or 'fastapi'",
"suggestion": "Use 'general' for Python code or 'fastapi' for FastAPI code",
}
try:
from data_manager import load_best_practices
data = load_best_practices()
practices = data.get("python_best_practices", {})
violations = []
good_patterns = []
score = 100
# Detect context if not specified correctly
code_lower = code.lower()
detected_context = context
if "fastapi" in code_lower or "@app." in code_lower or "fastapi" in code_lower:
detected_context = "fastapi"
# Check general coding practices
general_practices = practices.get("general_coding", {})
# Check type hints
if "def " in code and "->" not in code and ":" not in code.split("def")[1].split(")")[0]:
violations.append(
{
"severity": "important",
"issue": "Missing type hints",
"line": 0,
"recommendation": "Add type hints to function parameters and return values",
"example": "def function_name(param: str) -> dict:\n return {}",
"related_practice": "general_coding.type_hints",
}
)
score -= 15
else:
good_patterns.append("Uses type hints for better code clarity")
# Check for bare except
if "except:" in code or "except :" in code:
violations.append(
{
"severity": "critical",
"issue": "Bare except clause",
"line": 0,
"recommendation": "Use specific exception types instead of bare except",
"example": "try:\n ...\nexcept ValueError as e:\n handle_error(e)",
"related_practice": "general_coding.error_handling",
}
)
score -= 20
elif "except " in code:
good_patterns.append("Uses specific exception handling")
# Check for docstrings
if '"""' in code or "'''" in code:
good_patterns.append("Includes docstrings for documentation")
elif "def " in code or "class " in code:
violations.append(
{
"severity": "nice-to-have",
"issue": "Missing docstrings",
"line": 0,
"recommendation": "Add docstrings to functions and classes",
"example": '"""Function description.\n\nArgs:\n param: Description\n\nReturns:\n Description\n"""',
"related_practice": "general_coding.documentation",
}
)
score -= 10
# FastAPI-specific checks
if detected_context == "fastapi":
fastapi_practices = practices.get("fastapi_specific", {})
# Check for response models
if "@app." in code and "response_model" not in code:
violations.append(
{
"severity": "important",
"issue": "Missing response_model in route",
"line": 0,
"recommendation": "Define response_model for FastAPI routes",
"example": "@app.get('/users', response_model=User)",
"related_practice": "fastapi_specific.route_definition",
}
)
score -= 15
# Check for async
if "@app." in code and "async def" not in code:
violations.append(
{
"severity": "nice-to-have",
"issue": "Not using async/await",
"line": 0,
"recommendation": "Consider using async/await for I/O-bound operations",
"example": "async def get_user(user_id: str) -> User:\n return await db.get(user_id)",
"related_practice": "fastapi_specific.async_operations",
}
)
score -= 10
elif "async def" in code:
good_patterns.append("Uses async/await for better performance")
# Ensure score doesn't go below 0
score = max(0, score)
summary = f"Code review complete. Score: {score}/100. "
if len(violations) == 0:
summary += "No violations found! Consider the suggestions for advanced improvements."
else:
summary += f"Found {len(violations)} issue(s) to address."
return {
"violations": violations,
"good_patterns": good_patterns,
"overall_score": score,
"summary": summary,
"detected_context": detected_context,
}
except Exception as e:
logger.error(f"Code review failed: {e}")
return {
"error": "review_failed",
"message": f"Code review failed: {str(e)}",
}
@mcp.tool()
def review_code(code: str, context: str = "general") -> dict:
"""
Review code against best practices and provide feedback.
Analyzes code and identifies violations, good patterns, and provides
specific recommendations with examples.
Args:
code: Code snippet to review
context: Context type - "general" for Python or "fastapi" for FastAPI code
Returns:
Dictionary with violations, recommendations, and good patterns identified
"""
return _review_code_impl(code, context)
# Tool 6: suggest_improvements
def _suggest_improvements_impl(code: str, focus_area: Optional[str] = None) -> dict:
"""
Suggest specific improvements for code with before/after examples.
Provides prioritized, actionable suggestions with code examples and
trade-off explanations.
Args:
code: Code snippet to improve
focus_area: Optional area to focus on (error_handling, async, security, etc.)
Returns:
Dictionary with prioritized improvements, code examples, and trade-offs
"""
logger.info(f"Suggesting improvements, focus_area: {focus_area}")
if not code or not code.strip():
return {
"error": "invalid_input",
"message": "Code cannot be empty",
"suggestion": "Please provide code to analyze",
}
try:
from data_manager import load_best_practices
data = load_best_practices()
improvements = []
code_lower = code.lower()
# Type hints improvement
if focus_area in [None, "type_hints", "typing"] and "def " in code:
if "->" not in code:
improvements.append(
{
"priority": "important",
"description": "Add type hints for better code clarity and IDE support",
"before": "def calculate_total(price, quantity):\n return price * quantity",
"after": "def calculate_total(price: float, quantity: int) -> float:\n return price * quantity",
"trade_offs": "Slightly more verbose, but provides better documentation and catches type errors early",
"related_practice": "general_coding.type_hints",
}
)
# Error handling improvement
if focus_area in [None, "error_handling", "exceptions"]:
if "except:" in code or "except :" in code:
improvements.append(
{
"priority": "critical",
"description": "Replace bare except with specific exception types",
"before": "try:\n result = process()\nexcept:\n return None",
"after": "try:\n result = process()\nexcept ValueError as e:\n logger.error(f'Processing failed: {e}')\n return None",
"trade_offs": "More explicit error handling, easier debugging, but requires knowing expected exceptions",
"related_practice": "general_coding.error_handling",
}
)
# Async improvement
if focus_area in [None, "async", "performance"] and "def " in code and "async" not in code:
if "fastapi" in code_lower or "@app" in code:
improvements.append(
{
"priority": "important",
"description": "Use async/await for I/O-bound operations",
"before": "def get_user(user_id: str) -> User:\n return db.query(User).filter(User.id == user_id).first()",
"after": "async def get_user(user_id: str) -> User:\n result = await db.execute(select(User).filter(User.id == user_id))\n return result.scalar_one_or_none()",
"trade_offs": "Better performance for I/O operations, but requires async database driver",
"related_practice": "fastapi_specific.async_operations",
}
)
# Docstring improvement
if focus_area in [None, "documentation", "docstrings"]:
if ("def " in code or "class " in code) and '"""' not in code:
improvements.append(
{
"priority": "nice-to-have",
"description": "Add comprehensive docstrings",
"before": "def calculate_discount(price, percent):\n return price * (1 - percent / 100)",
"after": 'def calculate_discount(price: float, percent: float) -> float:\n """\n Calculate final price after discount.\n \n Args:\n price: Original price\n percent: Discount percentage (0-100)\n \n Returns:\n Final price after discount\n """\n return price * (1 - percent / 100)',
"trade_offs": "More verbose, but significantly improves code maintainability and understanding",
"related_practice": "general_coding.documentation",
}
)
# If code is already good
if len(improvements) == 0:
return {
"message": "Code looks good! No major improvements needed.",
"improvements": [],
"alternative_patterns": [
"Consider adding logging for better debugging",
"Consider adding input validation",
"Consider adding unit tests",
],
}
return {"improvements": improvements, "count": len(improvements)}
except Exception as e:
logger.error(f"Improvement suggestion failed: {e}")
return {
"error": "suggestion_failed",
"message": f"Failed to suggest improvements: {str(e)}",
}
@mcp.tool()
def suggest_improvements(code: str, focus_area: Optional[str] = None) -> dict:
"""
Suggest specific improvements for code with before/after examples.
Provides prioritized, actionable suggestions with code examples and
trade-off explanations.
Args:
code: Code snippet to improve
focus_area: Optional area to focus on (error_handling, async, security, etc.)
Returns:
Dictionary with prioritized improvements, code examples, and trade-offs
"""
return _suggest_improvements_impl(code, focus_area)
# Resources
@mcp.resource("bestpractices://general/{topic}")
def get_general_resource(topic: str) -> str:
"""Get general coding best practice resource."""
logger.info(f"Resource request: general/{topic}")
try:
result = get_category("general_coding", topic)
if "error" in result:
return f"# Error\n\n{result['message']}"
# Format as markdown
md = f"# {topic}\n\n"
md += f"**Category:** general_coding\n\n"
md += f"## Description\n\n{result.get('description', '')}\n\n"
if "examples" in result:
md += "## Examples\n\n"
for name, code in result["examples"].items():
md += f"### {name}\n\n```python\n{code}\n```\n\n"
return md
except Exception as e:
logger.error(f"Resource error: {e}")
return f"# Error\n\nFailed to load resource: {str(e)}"
@mcp.resource("bestpractices://fastapi/{topic}")
def get_fastapi_resource(topic: str) -> str:
"""Get FastAPI-specific best practice resource."""
logger.info(f"Resource request: fastapi/{topic}")
try:
result = get_category("fastapi_specific", topic)
if "error" in result:
return f"# Error\n\n{result['message']}"
md = f"# {topic}\n\n"
md += f"**Category:** fastapi_specific\n\n"
md += f"## Description\n\n{result.get('description', '')}\n\n"
if "examples" in result:
md += "## Examples\n\n"
for name, code in result["examples"].items():
md += f"### {name}\n\n```python\n{code}\n```\n\n"
return md
except Exception as e:
return f"# Error\n\nFailed to load resource: {str(e)}"
@mcp.resource("bestpractices://performance/{topic}")
def get_performance_resource(topic: str) -> str:
"""Get performance optimization best practice resource."""
logger.info(f"Resource request: performance/{topic}")
try:
result = get_category("performance", topic)
if "error" in result:
return f"# Error\n\n{result['message']}"
md = f"# {topic}\n\n"
md += f"**Category:** performance\n\n"
md += f"## Description\n\n{result.get('description', '')}\n\n"
if "examples" in result:
md += "## Examples\n\n"
for name, code in result["examples"].items():
md += f"### {name}\n\n```python\n{code}\n```\n\n"
return md
except Exception as e:
return f"# Error\n\nFailed to load resource: {str(e)}"
@mcp.resource("bestpractices://code_quality/{topic}")
def get_code_quality_resource(topic: str) -> str:
"""Get code quality best practice resource."""
logger.info(f"Resource request: code_quality/{topic}")
try:
result = get_category("code_quality", topic)
if "error" in result:
return f"# Error\n\n{result['message']}"
md = f"# {topic}\n\n"
md += f"**Category:** code_quality\n\n"
md += f"## Description\n\n{result.get('description', '')}\n\n"
examples_or_tools = result.get("examples") or result.get("tools", {})
if examples_or_tools:
md += "## Examples\n\n"
for name, code in examples_or_tools.items():
md += f"### {name}\n\n```\n{code}\n```\n\n"
return md
except Exception as e:
return f"# Error\n\nFailed to load resource: {str(e)}"
@mcp.resource("bestpractices://all")
def get_all_practices() -> str:
"""Get complete best practices guide."""
logger.info("Resource request: all practices")
try:
from data_manager import load_best_practices
data = load_best_practices()
practices = data.get("python_best_practices", {})
md = "# Python Best Practices Guide\n\n"
md += "Complete guide for Python coding and FastAPI development.\n\n"
for category_name, category_data in practices.items():
md += f"## {category_name.replace('_', ' ').title()}\n\n"
if isinstance(category_data, dict):
for topic_name, topic_data in category_data.items():
if isinstance(topic_data, dict):
md += f"### {topic_name.replace('_', ' ').title()}\n\n"
md += f"{topic_data.get('description', '')}\n\n"
return md
except Exception as e:
return f"# Error\n\nFailed to load complete guide: {str(e)}"
# Prompts
@mcp.prompt()
def review_python_code() -> list:
"""
Prompt for comprehensive Python code review.
Requests user's code and performs analysis against general Python best practices.
"""
return [
{
"role": "user",
"content": "Please provide your Python code for review. I'll analyze it against best practices and provide specific recommendations."
}
]
@mcp.prompt()
def review_fastapi_endpoint() -> list:
"""
Prompt for FastAPI-specific code review.
Requests FastAPI code and analyzes against FastAPI best practices.
"""
return [
{
"role": "user",
"content": "Please provide your FastAPI code for review. I'll analyze it against FastAPI-specific best practices including routes, models, async operations, and more."
}
]
@mcp.prompt()
def suggest_code_improvements() -> list:
"""
Prompt for getting prioritized improvement suggestions.
Requests code and provides actionable improvements with examples.
"""
return [
{
"role": "user",
"content": "Please provide your code and optionally specify a focus area (e.g., error_handling, async, security). I'll suggest specific improvements with before/after examples."
}
]
@mcp.prompt()
def show_examples() -> list:
"""
Prompt for retrieving examples for a specific topic.
Requests topic name and returns relevant code examples.
"""
return [
{
"role": "user",
"content": "What topic would you like to see examples for? (e.g., type_hints, async_operations, pydantic_models)"
}
]