import importlib from fastapi import FastAPI, HTTPException from registry import TOOLS, BY_NAME app = FastAPI(title="XTOOLS", description="90two tool server") _registry_cache = {} def _load(tool_name: str): if tool_name not in _registry_cache: mod = importlib.import_module(f"tools.{tool_name}") _registry_cache[tool_name] = mod.run return _registry_cache[tool_name] def _validate(tool_name: str, params: dict) -> dict: spec = BY_NAME[tool_name] cleaned = {} for p in spec["params"]: name = p["name"] if name in params: cleaned[name] = params[name] elif p["required"]: raise HTTPException(status_code=422, detail=f"Missing required param: {name}") else: cleaned[name] = p["default"] return cleaned @app.get("/") def health(): return {"status": "ok", "tool_count": len(TOOLS)} @app.get("/registry") def get_registry(): """Full tool manifest -- name, category, auth type, and param schema for every tool.""" return {"tools": TOOLS} @app.post("/tools/{tool_name}") def call_tool(tool_name: str, params: dict = None): if tool_name not in BY_NAME: raise HTTPException(status_code=404, detail=f"Unknown tool: {tool_name}") params = params or {} cleaned = _validate(tool_name, params) run_fn = _load(tool_name) try: return run_fn(cleaned) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"{type(e).__name__}: {e}")