Fix critical issues: closure bug, error handling, graceful shutdown, type hints, logging

This commit is contained in:
AuraCrimsonRose
2026-06-03 04:37:50 -05:00
parent cfbc9405a9
commit bc6e23f420

99
main.py
View File

@@ -1,5 +1,8 @@
from __future__ import annotations from __future__ import annotations
import logging
from typing import Any
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
from core.config import ( from core.config import (
@@ -7,6 +10,7 @@ from core.config import (
API_HOST, API_HOST,
API_PORT, API_PORT,
SERVER_NAME, SERVER_NAME,
DEBUG,
) )
from core.events import bus, Event from core.events import bus, Event
from core.metrics import metrics from core.metrics import metrics
@@ -17,6 +21,18 @@ from core.tools.base import ToolContext
from tools.discovery import load_all_tools from tools.discovery import load_all_tools
# =========================
# LOGGING SETUP
# =========================
log_level = logging.DEBUG if DEBUG else logging.INFO
logging.basicConfig(
level=log_level,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# ========================= # =========================
# RUNTIME BOOTSTRAP # RUNTIME BOOTSTRAP
# ========================= # =========================
@@ -35,7 +51,8 @@ mcp = FastMCP(SERVER_NAME)
# REGISTRY → MCP AUTO BIND # REGISTRY → MCP AUTO BIND
# ========================= # =========================
def bind_registry_tools(): def bind_registry_tools() -> None:
"""Bind all registered tools to MCP server with proper error handling."""
for tool in registry.all_tools(): for tool in registry.all_tools():
tool_name = tool.name tool_name = tool.name
@@ -45,17 +62,34 @@ def bind_registry_tools():
f"{tool_name} tool" f"{tool_name} tool"
) )
def make_handler(bound_tool): # Fix issue #2: Use default argument to avoid closure binding bug
def handler(**kwargs): def make_handler(bound_tool: Any) -> Any:
"""Create handler with proper binding to prevent late-binding issues."""
def handler(**kwargs: Any) -> Any:
ctx = ToolContext( ctx = ToolContext(
dry_run=safety.dry_run dry_run=safety.dry_run
) )
return registry.run( try:
name=bound_tool.name, return registry.run(
payload=kwargs, name=bound_tool.name,
ctx=ctx payload=kwargs,
) ctx=ctx
)
except Exception as e:
# Fix issue #3: Add error handling in tool handler
logger.exception(f"Error executing tool {bound_tool.name}")
bus.emit(Event(
name="tool_handler_error",
source="CORE",
level="ERROR",
data={
"tool": bound_tool.name,
"error": str(e),
"error_type": type(e).__name__
}
))
raise
return handler return handler
@@ -78,10 +112,13 @@ def bind_registry_tools():
# SYSTEM BOOT # SYSTEM BOOT
# ========================= # =========================
def boot_system(): def boot_system() -> None:
"""Initialize and boot the system."""
load_all_tools() load_all_tools()
bind_registry_tools() bind_registry_tools()
tool_count = len(registry.all_tools())
bus.emit(Event( bus.emit(Event(
name="system_boot", name="system_boot",
source="CORE", source="CORE",
@@ -89,16 +126,19 @@ def boot_system():
data={ data={
"workspace": str(WORKSPACE_ROOT), "workspace": str(WORKSPACE_ROOT),
"server": SERVER_NAME, "server": SERVER_NAME,
"tool_count": len(registry._tools) "tool_count": tool_count
} }
)) ))
logger.info(f"System boot complete. Loaded {tool_count} tools.")
# ========================= # =========================
# METRICS # METRICS
# ========================= # =========================
def get_metrics_snapshot(): def get_metrics_snapshot() -> dict[str, Any]:
"""Return current metrics snapshot."""
return metrics.snapshot() return metrics.snapshot()
@@ -106,7 +146,8 @@ def get_metrics_snapshot():
# DEBUG EVENTS (OPTIONAL) # DEBUG EVENTS (OPTIONAL)
# ========================= # =========================
def debug_event_printer(event: Event): def debug_event_printer(event: Event) -> None:
"""Print debug information for events."""
print( print(
f"[EVENT] " f"[EVENT] "
f"{event.source} - " f"{event.source} - "
@@ -118,11 +159,26 @@ def debug_event_printer(event: Event):
# bus.subscribe(debug_event_printer) # bus.subscribe(debug_event_printer)
# =========================
# GRACEFUL SHUTDOWN
# =========================
def shutdown_gracefully() -> None:
"""Clean shutdown of executor and other resources."""
logger.info("Initiating graceful shutdown...")
try:
executor.shutdown()
logger.info("Executor shut down successfully.")
except Exception as e:
logger.error(f"Error during executor shutdown: {e}")
# ========================= # =========================
# MCP RUNNER # MCP RUNNER
# ========================= # =========================
def run_mcp(): def run_mcp() -> None:
"""Run the MCP server."""
boot_system() boot_system()
bus.emit(Event( bus.emit(Event(
@@ -134,6 +190,7 @@ def run_mcp():
} }
)) ))
logger.info(f"Starting MCP server: {SERVER_NAME}")
mcp.run(transport="stdio") mcp.run(transport="stdio")
@@ -141,18 +198,19 @@ def run_mcp():
# FASTAPI HOOK (ISOLATED) # FASTAPI HOOK (ISOLATED)
# ========================= # =========================
async def run_api(): async def run_api() -> None:
"""Run FastAPI metrics server (intended for separate process or async context)."""
from fastapi import FastAPI from fastapi import FastAPI
import uvicorn import uvicorn
app = FastAPI() app = FastAPI()
@app.get("/metrics") @app.get("/metrics")
def metrics_route(): def metrics_route() -> dict[str, Any]:
return get_metrics_snapshot() return get_metrics_snapshot()
@app.get("/health") @app.get("/health")
def health(): def health() -> dict[str, str]:
return { return {
"status": "ok" "status": "ok"
} }
@@ -178,19 +236,24 @@ if __name__ == "__main__":
run_mcp() run_mcp()
except KeyboardInterrupt: except KeyboardInterrupt:
logger.info("KeyboardInterrupt received.")
bus.emit(Event( bus.emit(Event(
name="system_shutdown", name="system_shutdown",
source="CORE", source="CORE",
level="INFO" level="INFO"
)) ))
shutdown_gracefully()
except Exception as e: except Exception as e:
logger.exception("Fatal error occurred.")
bus.emit(Event( bus.emit(Event(
name="fatal_error", name="fatal_error",
source="CORE", source="CORE",
level="ERROR", level="ERROR",
data={ data={
"error": str(e) "error": str(e),
"error_type": type(e).__name__
} }
)) ))
raise shutdown_gracefully()
raise