from __future__ import annotations import os import sys import importlib from pathlib import Path from typing import Any, Dict, List try: FastAPI = importlib.import_module("fastapi").FastAPI except Exception: FastAPI = None PLUGIN_DIR = (Path(__file__).resolve().parent / "deepchem" / "mcp_output" / "mcp_plugin").resolve() if str(PLUGIN_DIR) not in sys.path: sys.path.insert(0, str(PLUGIN_DIR)) if FastAPI is None: raise RuntimeError("fastapi is required to run app.py. Install dependencies from requirements.txt") app = FastAPI(title="DeepChem MCP Info API", version="1.0.0") def _extract_tools(mcp_obj: Any) -> List[Dict[str, str]]: tools: List[Dict[str, str]] = [] direct_tools = getattr(mcp_obj, "tools", None) if isinstance(direct_tools, dict): for key, tool_obj in direct_tools.items(): name = getattr(tool_obj, "name", None) or str(key) description = getattr(tool_obj, "description", "") tools.append({"name": str(name), "description": str(description)}) elif isinstance(direct_tools, (list, tuple, set)): for tool_obj in direct_tools: name = getattr(tool_obj, "name", None) or getattr(tool_obj, "__name__", "unknown") description = getattr(tool_obj, "description", "") or getattr(tool_obj, "__doc__", "") or "" tools.append({"name": str(name), "description": str(description).strip()}) if not tools: list_tools_fn = getattr(mcp_obj, "list_tools", None) if callable(list_tools_fn): try: listed = list_tools_fn() if isinstance(listed, (list, tuple)): for tool_obj in listed: if isinstance(tool_obj, dict): tools.append( { "name": str(tool_obj.get("name", "unknown")), "description": str(tool_obj.get("description", "")), } ) else: name = getattr(tool_obj, "name", None) or getattr(tool_obj, "__name__", "unknown") description = getattr(tool_obj, "description", "") tools.append({"name": str(name), "description": str(description)}) except Exception: pass dedup: Dict[str, Dict[str, str]] = {} for item in tools: dedup[item["name"]] = item return list(dedup.values()) @app.get("/") def info() -> Dict[str, Any]: return { "service": "deepchem-mcp", "description": "Supplementary FastAPI info service for the DeepChem MCP deployment.", "mcp_endpoint": "/mcp", "transport_env": os.getenv("MCP_TRANSPORT", "stdio"), "default_port": int(os.getenv("PORT", "7860")), } @app.get("/health") def health() -> Dict[str, str]: return {"status": "healthy"} @app.get("/tools") def tools() -> Dict[str, Any]: try: create_app = importlib.import_module("mcp_service").create_app mcp_obj = create_app() return {"status": "ok", "tools": _extract_tools(mcp_obj)} except Exception as exc: return {"status": "error", "tools": [], "error": str(exc)}