fix(server): resolve lint error and clean up production logs

- Fix F821 undefined name `core` by using local import in fallback path
- Remove all internal terminology from logs (Phase X, Option B)
- Clean Phase/Option B references from tool docstrings (visible to MCP clients)
- Suppress websockets DeprecationWarning (noisy, harmless)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-18 02:59:48 +03:30
parent 7daf604a32
commit 076a0b940e

View File

@@ -23,9 +23,13 @@ import logging
import os import os
import sys import sys
import time import time
import warnings
from datetime import UTC, datetime from datetime import UTC, datetime
from typing import Optional from typing import Optional
# Suppress noisy deprecation warning from websockets (transitive dependency)
warnings.filterwarnings("ignore", category=DeprecationWarning, module="websockets")
from fastmcp import FastMCP from fastmcp import FastMCP
from fastmcp.exceptions import ToolError from fastmcp.exceptions import ToolError
from fastmcp.server.dependencies import get_http_headers from fastmcp.server.dependencies import get_http_headers
@@ -45,7 +49,7 @@ from core import (
get_auth_manager, get_auth_manager,
get_project_manager, get_project_manager,
get_rate_limiter, get_rate_limiter,
# Option B modules (new clean architecture) # Core architecture modules
get_site_manager, get_site_manager,
get_site_registry, get_site_registry,
get_tool_registry, get_tool_registry,
@@ -235,7 +239,9 @@ mcp = FastMCP("MCP Hub")
_TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), "core", "templates") _TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), "core", "templates")
if not os.path.isdir(_TEMPLATES_DIR): if not os.path.isdir(_TEMPLATES_DIR):
# Fallback for pip-installed package: resolve relative to core package # Fallback for pip-installed package: resolve relative to core package
_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(core.__file__)), "templates") import core as _core_pkg
_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(_core_pkg.__file__)), "templates")
templates = Jinja2Templates(directory=_TEMPLATES_DIR) templates = Jinja2Templates(directory=_TEMPLATES_DIR)
logger.info("Jinja2 template engine initialized") logger.info("Jinja2 template engine initialized")
@@ -244,7 +250,7 @@ auth_manager = get_auth_manager()
api_key_manager = get_api_key_manager() api_key_manager = get_api_key_manager()
project_manager = get_project_manager() project_manager = get_project_manager()
audit_logger = get_audit_logger() audit_logger = get_audit_logger()
csrf_manager = get_csrf_manager() # Phase E: CSRF protection csrf_manager = get_csrf_manager()
# Initialize site registry (legacy - kept for backward compatibility) # Initialize site registry (legacy - kept for backward compatibility)
site_registry = get_site_registry() site_registry = get_site_registry()
@@ -254,8 +260,7 @@ site_registry.discover_sites(plugin_types)
# Initialize unified tool generator (legacy - kept for backward compatibility) # Initialize unified tool generator (legacy - kept for backward compatibility)
unified_tool_generator = UnifiedToolGenerator(project_manager) unified_tool_generator = UnifiedToolGenerator(project_manager)
# === Option B Architecture (New Clean Architecture) === # Initialize site manager
# Initialize site manager (replacement for SiteRegistry)
site_manager = get_site_manager() site_manager = get_site_manager()
site_manager.discover_sites(plugin_types) site_manager.discover_sites(plugin_types)
@@ -280,7 +285,7 @@ logger.info(f"Site breakdown: {site_manager.get_count_by_type()}")
for full_id, plugin in project_manager.projects.items(): for full_id, plugin in project_manager.projects.items():
logger.info(f" - {full_id} ({plugin.get_plugin_name()})") logger.info(f" - {full_id} ({plugin.get_plugin_name()})")
# Log discovered sites (Option B) # Log discovered sites
logger.info("\nDiscovered sites:") logger.info("\nDiscovered sites:")
for site_info in site_manager.list_all_sites(): for site_info in site_manager.list_all_sites():
alias_display = ( alias_display = (
@@ -462,7 +467,6 @@ def extract_plugin_type_from_tool(tool_name: str) -> str | None:
""" """
Extract plugin type from tool name for tool visibility filtering. Extract plugin type from tool name for tool visibility filtering.
Phase 5.5: Tool Visibility Filter
Each API key should only see tools related to its plugin type. Each API key should only see tools related to its plugin type.
Examples: Examples:
@@ -511,8 +515,6 @@ def check_tool_visibility(tool_name: str, api_key_project_id: str) -> bool:
""" """
Check if API key has visibility to the requested tool. Check if API key has visibility to the requested tool.
Phase 5.5: Tool Visibility Filter
Rules: Rules:
- Global API keys (project_id="*") see ALL tools - Global API keys (project_id="*") see ALL tools
- Plugin-specific keys (project_id="wordpress_xxx") see only that plugin's tools - Plugin-specific keys (project_id="wordpress_xxx") see only that plugin's tools
@@ -787,7 +789,6 @@ class UserAuthMiddleware(Middleware):
key = api_key_manager.keys.get(key_id) if key_id else None key = api_key_manager.keys.get(key_id) if key_id else None
if key: if key:
# Phase 5.5: Tool Visibility Filter
# Check if API key has visibility to this tool # Check if API key has visibility to this tool
if not check_tool_visibility(tool_name, key.project_id): if not check_tool_visibility(tool_name, key.project_id):
plugin_type = extract_plugin_type_from_tool(tool_name) plugin_type = extract_plugin_type_from_tool(tool_name)
@@ -945,16 +946,16 @@ mcp.add_middleware(AuditLoggingMiddleware())
logger.info("Audit logging middleware enabled") logger.info("Audit logging middleware enabled")
# === RATE LIMITING (Phase 7.3) === # === RATE LIMITING ===
# Initialize rate limiter # Initialize rate limiter
rate_limiter = get_rate_limiter() rate_limiter = get_rate_limiter()
logger.info("Rate limiter initialized (Phase 7.3)") logger.info("Rate limiter initialized")
class RateLimitMiddleware(Middleware): class RateLimitMiddleware(Middleware):
""" """
Middleware to enforce rate limiting for all tool calls (Phase 7.3). Middleware to enforce rate limiting for all tool calls.
Uses Token Bucket algorithm to prevent API abuse with multi-level limits: Uses Token Bucket algorithm to prevent API abuse with multi-level limits:
- Per minute: 60 requests (default) - Per minute: 60 requests (default)
@@ -1040,10 +1041,10 @@ class RateLimitMiddleware(Middleware):
# Add rate limiting middleware to MCP server # Add rate limiting middleware to MCP server
mcp.add_middleware(RateLimitMiddleware()) mcp.add_middleware(RateLimitMiddleware())
logger.info("Rate limiting middleware enabled (Phase 7.3)") logger.info("Rate limiting middleware enabled")
# === HEALTH MONITORING (Phase 7.2) === # === HEALTH MONITORING ===
from core import initialize_health_monitor from core import initialize_health_monitor
@@ -1054,12 +1055,12 @@ health_monitor = initialize_health_monitor(
metrics_retention_hours=24, metrics_retention_hours=24,
max_metrics_per_project=1000, max_metrics_per_project=1000,
) )
logger.info("Health monitor initialized (Phase 7.2)") logger.info("Health monitor initialized")
class HealthMetricsMiddleware(Middleware): class HealthMetricsMiddleware(Middleware):
""" """
Middleware to track health metrics for all tool calls (Phase 7.2). Middleware to track health metrics for all tool calls.
Tracks: Tracks:
- Response time - Response time
@@ -1148,7 +1149,7 @@ class HealthMetricsMiddleware(Middleware):
# Add health metrics middleware # Add health metrics middleware
mcp.add_middleware(HealthMetricsMiddleware()) mcp.add_middleware(HealthMetricsMiddleware())
logger.info("Health metrics middleware enabled (Phase 7.2)") logger.info("Health metrics middleware enabled")
# === ENDPOINT MIDDLEWARE HELPER === # === ENDPOINT MIDDLEWARE HELPER ===
@@ -1269,7 +1270,7 @@ async def get_project_info(project_id: str) -> str:
@mcp.tool() @mcp.tool()
async def check_all_projects_health() -> str: async def check_all_projects_health() -> str:
""" """
Check health status of all projects with enhanced metrics (Phase 7.2). Check health status of all projects with enhanced metrics.
Performs comprehensive health checks on all configured projects including: Performs comprehensive health checks on all configured projects including:
- Accessibility and response time - Accessibility and response time
@@ -1295,7 +1296,7 @@ async def check_all_projects_health() -> str:
@mcp.tool() @mcp.tool()
async def get_project_health(project_id: str) -> str: async def get_project_health(project_id: str) -> str:
""" """
Get detailed health information for a specific project (Phase 7.2). Get detailed health information for a specific project.
Args: Args:
project_id: Full project identifier (e.g., "wordpress_site1") project_id: Full project identifier (e.g., "wordpress_site1")
@@ -1322,7 +1323,7 @@ async def get_project_health(project_id: str) -> str:
@mcp.tool() @mcp.tool()
async def get_system_metrics() -> str: async def get_system_metrics() -> str:
""" """
Get overall MCP server metrics and statistics (Phase 7.2). Get overall MCP server metrics and statistics.
Returns system-wide metrics including: Returns system-wide metrics including:
- Uptime - Uptime
@@ -1348,7 +1349,7 @@ async def get_system_metrics() -> str:
@mcp.tool() @mcp.tool()
async def get_system_uptime() -> str: async def get_system_uptime() -> str:
""" """
Get MCP server uptime information (Phase 7.2). Get MCP server uptime information.
Returns: Returns:
JSON string with uptime in various formats (seconds, minutes, hours, days) JSON string with uptime in various formats (seconds, minutes, hours, days)
@@ -1367,7 +1368,7 @@ async def get_system_uptime() -> str:
@mcp.tool() @mcp.tool()
async def get_project_metrics(project_id: str, hours: int = 1) -> str: async def get_project_metrics(project_id: str, hours: int = 1) -> str:
""" """
Get historical metrics for a specific project (Phase 7.2). Get historical metrics for a specific project.
Args: Args:
project_id: Full project identifier (e.g., "wordpress_site1") project_id: Full project identifier (e.g., "wordpress_site1")
@@ -1397,7 +1398,7 @@ async def get_project_metrics(project_id: str, hours: int = 1) -> str:
@mcp.tool() @mcp.tool()
async def export_health_metrics(output_path: str = "logs/metrics_export.json") -> str: async def export_health_metrics(output_path: str = "logs/metrics_export.json") -> str:
""" """
Export all health metrics to a JSON file (Phase 7.2). Export all health metrics to a JSON file.
Args: Args:
output_path: Path to output file (default: logs/metrics_export.json) output_path: Path to output file (default: logs/metrics_export.json)
@@ -1452,7 +1453,7 @@ async def _reset_rate_limit_impl(client_id: str = None) -> str:
@mcp.tool() @mcp.tool()
async def get_rate_limit_stats(client_id: str = None) -> str: async def get_rate_limit_stats(client_id: str = None) -> str:
""" """
Get rate limiting statistics (Phase 7.3). Get rate limiting statistics.
Args: Args:
client_id: Optional client identifier to get specific client stats. client_id: Optional client identifier to get specific client stats.
@@ -1467,7 +1468,7 @@ async def get_rate_limit_stats(client_id: str = None) -> str:
@mcp.tool() @mcp.tool()
async def reset_rate_limit(client_id: str = None) -> str: async def reset_rate_limit(client_id: str = None) -> str:
""" """
Reset rate limit state for a client or all clients (Phase 7.3). Reset rate limit state for a client or all clients.
CAUTION: This is an administrative tool. Use with care. CAUTION: This is an administrative tool. Use with care.
@@ -1596,25 +1597,18 @@ def register_project_tools():
This function is called at startup to register all tools This function is called at startup to register all tools
from all discovered projects. from all discovered projects.
Option B Architecture (Phase 3 Complete):
- Uses ToolRegistry for centralized tool management
- Uses ToolGenerator for WordPress plugin (refactored in Phase 3)
- Type-safe with Pydantic models in ToolRegistry
- Tool specifications from plugin.get_tool_specifications()
Note: FastMCP requires using the @mcp.tool() decorator or mcp.tool(function) Note: FastMCP requires using the @mcp.tool() decorator or mcp.tool(function)
for tool registration. for tool registration.
""" """
logger.info("=" * 60) logger.info("=" * 60)
logger.info("TOOL REGISTRATION - Option B Architecture (Phase 3)") logger.info("TOOL REGISTRATION")
logger.info("=" * 60) logger.info("=" * 60)
# Phase 3: Use ToolGenerator for refactored plugins
logger.info("Generating tools with ToolGenerator...") logger.info("Generating tools with ToolGenerator...")
from plugins.wordpress.plugin import WordPressPlugin from plugins.wordpress.plugin import WordPressPlugin
# Generate tools for WordPress (refactored in Phase 3) # Generate tools for WordPress
logger.info("Generating WordPress tools from plugin specifications...") logger.info("Generating WordPress tools from plugin specifications...")
try: try:
wordpress_tools = tool_generator.generate_tools(WordPressPlugin, "wordpress") wordpress_tools = tool_generator.generate_tools(WordPressPlugin, "wordpress")
@@ -1810,7 +1804,7 @@ def register_project_tools():
logger.info("=" * 60) logger.info("=" * 60)
# System tools count (Phase 7.2 + 7.3 + API Keys): # System tools count (health + rate limit + API keys):
# - 10 system/health/rate limit tools # - 10 system/health/rate limit tools
# - 6 API key management tools # - 6 API key management tools
system_tools_count = 16 system_tools_count = 16
@@ -1819,7 +1813,7 @@ def register_project_tools():
logger.info( logger.info(
f"Total tools available: {total_tools} ({tool_registry.get_count()} plugin + {system_tools_count} system)" f"Total tools available: {total_tools} ({tool_registry.get_count()} plugin + {system_tools_count} system)"
) )
logger.info("🎯 Option B Architecture: Tool count stays constant regardless of site count!") logger.info("Tool count stays constant regardless of site count")
return total_tools return total_tools
@@ -3100,7 +3094,7 @@ async def health_check(request: Request) -> JSONResponse:
"status": "healthy", "status": "healthy",
"uptime": uptime_seconds, "uptime": uptime_seconds,
"projects": len(project_manager.projects), "projects": len(project_manager.projects),
"sites": site_manager.get_count(), # Option B "sites": site_manager.get_count(),
"tools": _total_tool_count, # Total tools (plugin + system) "tools": _total_tool_count, # Total tools (plugin + system)
"timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"), "timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
} }