diff --git a/web/app.py b/web/app.py index 2ea8000..8888c66 100644 --- a/web/app.py +++ b/web/app.py @@ -2806,6 +2806,18 @@ async def api_mcp_env_redeploy(): } +@app.get("/api/settings/mcp/tools") +async def api_mcp_tools(): + """List all MCP tools registered in legal_mcp.""" + from web.mcp_introspection import list_mcp_tools + try: + tools = await list_mcp_tools() + except Exception as e: + logger.exception("mcp_tools_introspection_failed") + raise HTTPException(500, f"Tools introspection failed: {e}") + return {"tools": tools, "count": len(tools)} + + # ── Settings: Tag → Company Mappings ────────────────────────────── @app.get("/api/settings/paperclip-companies") diff --git a/web/mcp_introspection.py b/web/mcp_introspection.py new file mode 100644 index 0000000..fbaa54e --- /dev/null +++ b/web/mcp_introspection.py @@ -0,0 +1,52 @@ +# web/mcp_introspection.py +"""Introspect MCP tools from the FastMCP instance.""" + +from __future__ import annotations + +import inspect +from typing import Any + + +async def list_mcp_tools() -> list[dict[str, Any]]: + """List all registered MCP tools with metadata.""" + from legal_mcp.server import mcp + + tools = await mcp.list_tools() + out: list[dict[str, Any]] = [] + for t in tools: + # Resolve underlying callable for source location + fn = _resolve_callable(t.name) + source_location = "" + module = "" + if fn is not None: + try: + file = inspect.getfile(fn) + _, line = inspect.getsourcelines(fn) + source_location = f"{file}:{line}" + module = fn.__module__ + except Exception: + pass + out.append({ + "name": t.name, + "description": t.description or "", + "params_schema": getattr(t, "inputSchema", None), + "module": module, + "source_location": source_location, + }) + return sorted(out, key=lambda r: (r["module"], r["name"])) + + +def _resolve_callable(tool_name: str): + """Find the python function backing a registered tool name.""" + from legal_mcp.tools import ( + cases, documents, drafting, precedent_library, + precedents, search, workflow, + ) + for mod in ( + cases, documents, drafting, precedent_library, + precedents, search, workflow, + ): + fn = getattr(mod, tool_name, None) + if callable(fn): + return fn + return None