Files
python-mcp/main.py
AuroraCrimsonRose e471f9bc54 Added many tools
2026-06-03 06:01:06 -05:00

260 lines
6.0 KiB
Python

from __future__ import annotations
import logging
from typing import Any
from mcp.server.fastmcp import FastMCP
from core.config import (
WORKSPACE_ROOT,
API_HOST,
API_PORT,
SERVER_NAME,
DEBUG,
)
from core.events import bus, Event
from core.metrics import metrics
from core.safety import safety
from core.executor import executor
from core.tools.registry import registry
from core.tools.base import ToolContext
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
# =========================
executor.start()
# =========================
# MCP SERVER
# =========================
mcp = FastMCP(SERVER_NAME)
# =========================
# REGISTRY → MCP AUTO BIND
# =========================
def bind_registry_tools() -> None:
"""Bind all registered tools to MCP server with proper error handling."""
for tool in registry.all_tools():
tool_name = tool.name
tool_description = getattr(
tool,
"description",
f"{tool_name} tool"
)
# Fix issue #2: Use default argument to avoid closure binding bug
def make_handler(bound_tool: Any) -> Any:
"""Create handler with proper binding to prevent late-binding issues."""
def handler(**kwargs: Any) -> Any:
ctx = ToolContext(
dry_run=safety.dry_run
)
try:
return registry.run(
name=bound_tool.name,
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
mcp.tool(
name=tool_name,
description=tool_description
)(make_handler(tool))
bus.log(
"CORE",
"mcp_tool_bound",
"INFO",
{
"tool": tool_name
}
)
# =========================
# SYSTEM BOOT
# =========================
def boot_system() -> None:
"""Initialize and boot the system."""
load_all_tools()
bind_registry_tools()
tool_count = len(registry.all_tools())
bus.emit(Event(
name="system_boot",
source="CORE",
level="INFO",
data={
"workspace": str(WORKSPACE_ROOT),
"server": SERVER_NAME,
"tool_count": tool_count
}
))
logger.info(f"System boot complete. Loaded {tool_count} tools.")
# =========================
# METRICS
# =========================
def get_metrics_snapshot() -> dict[str, Any]:
"""Return current metrics snapshot."""
return metrics.snapshot()
# =========================
# DEBUG EVENTS (OPTIONAL)
# =========================
def debug_event_printer(event: Event) -> None:
"""Print debug information for events."""
print(
f"[EVENT] "
f"{event.source} - "
f"{event.level} - "
f"{event.name}"
)
# 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() # type: ignore[attr-defined]
logger.info("Executor shut down successfully.")
except Exception as e:
logger.error(f"Error during executor shutdown: {e}")
# =========================
# MCP RUNNER
# =========================
def run_mcp() -> None:
"""Run the MCP server."""
boot_system()
bus.emit(Event(
name="mcp_start",
source="CORE",
level="INFO",
data={
"server": SERVER_NAME
}
))
logger.info(f"Starting MCP server: {SERVER_NAME}")
mcp.run(transport="stdio")
# =========================
# FASTAPI HOOK (ISOLATED)
# =========================
async def run_api() -> None:
"""Run FastAPI metrics server (intended for separate process or async context)."""
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/metrics")
def metrics_route() -> dict[str, Any]:
return get_metrics_snapshot()
@app.get("/health")
def health() -> dict[str, str]:
return {
"status": "ok"
}
config = uvicorn.Config(
app,
host=API_HOST,
port=API_PORT,
log_level="info"
)
server = uvicorn.Server(config)
await server.serve()
# =========================
# ENTRYPOINT
# =========================
if __name__ == "__main__":
try:
run_mcp()
except KeyboardInterrupt:
logger.info("KeyboardInterrupt received.")
bus.emit(Event(
name="system_shutdown",
source="CORE",
level="INFO"
))
shutdown_gracefully()
except Exception as e:
logger.exception("Fatal error occurred.")
bus.emit(Event(
name="fatal_error",
source="CORE",
level="ERROR",
data={
"error": str(e),
"error_type": type(e).__name__
}
))
shutdown_gracefully()
raise