196 lines
3.6 KiB
Python
196 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
from mcp.server.fastmcp import FastMCP
|
|
|
|
from core.config import (
|
|
WORKSPACE_ROOT,
|
|
API_HOST,
|
|
API_PORT,
|
|
SERVER_NAME,
|
|
)
|
|
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
|
|
|
|
|
|
# =========================
|
|
# RUNTIME BOOTSTRAP
|
|
# =========================
|
|
|
|
executor.start()
|
|
|
|
|
|
# =========================
|
|
# MCP SERVER
|
|
# =========================
|
|
|
|
mcp = FastMCP(SERVER_NAME)
|
|
|
|
|
|
# =========================
|
|
# REGISTRY → MCP AUTO BIND
|
|
# =========================
|
|
|
|
def bind_registry_tools():
|
|
for tool in registry.all_tools():
|
|
|
|
tool_name = tool.name
|
|
tool_description = getattr(
|
|
tool,
|
|
"description",
|
|
f"{tool_name} tool"
|
|
)
|
|
|
|
def make_handler(bound_tool):
|
|
def handler(**kwargs):
|
|
ctx = ToolContext(
|
|
dry_run=safety.dry_run
|
|
)
|
|
|
|
return registry.run(
|
|
name=bound_tool.name,
|
|
payload=kwargs,
|
|
ctx=ctx
|
|
)
|
|
|
|
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():
|
|
load_all_tools()
|
|
bind_registry_tools()
|
|
|
|
bus.emit(Event(
|
|
name="system_boot",
|
|
source="CORE",
|
|
level="INFO",
|
|
data={
|
|
"workspace": str(WORKSPACE_ROOT),
|
|
"server": SERVER_NAME,
|
|
"tool_count": len(registry._tools)
|
|
}
|
|
))
|
|
|
|
|
|
# =========================
|
|
# METRICS
|
|
# =========================
|
|
|
|
def get_metrics_snapshot():
|
|
return metrics.snapshot()
|
|
|
|
|
|
# =========================
|
|
# DEBUG EVENTS (OPTIONAL)
|
|
# =========================
|
|
|
|
def debug_event_printer(event: Event):
|
|
print(
|
|
f"[EVENT] "
|
|
f"{event.source} - "
|
|
f"{event.level} - "
|
|
f"{event.name}"
|
|
)
|
|
|
|
|
|
# bus.subscribe(debug_event_printer)
|
|
|
|
|
|
# =========================
|
|
# MCP RUNNER
|
|
# =========================
|
|
|
|
def run_mcp():
|
|
boot_system()
|
|
|
|
bus.emit(Event(
|
|
name="mcp_start",
|
|
source="CORE",
|
|
level="INFO",
|
|
data={
|
|
"server": SERVER_NAME
|
|
}
|
|
))
|
|
|
|
mcp.run(transport="stdio")
|
|
|
|
|
|
# =========================
|
|
# FASTAPI HOOK (ISOLATED)
|
|
# =========================
|
|
|
|
async def run_api():
|
|
from fastapi import FastAPI
|
|
import uvicorn
|
|
|
|
app = FastAPI()
|
|
|
|
@app.get("/metrics")
|
|
def metrics_route():
|
|
return get_metrics_snapshot()
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
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:
|
|
bus.emit(Event(
|
|
name="system_shutdown",
|
|
source="CORE",
|
|
level="INFO"
|
|
))
|
|
|
|
except Exception as e:
|
|
bus.emit(Event(
|
|
name="fatal_error",
|
|
source="CORE",
|
|
level="ERROR",
|
|
data={
|
|
"error": str(e)
|
|
}
|
|
))
|
|
raise |
