feat(settings): add MCP tools introspection endpoint
This commit is contained in:
12
web/app.py
12
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 ──────────────────────────────
|
# ── Settings: Tag → Company Mappings ──────────────────────────────
|
||||||
|
|
||||||
@app.get("/api/settings/paperclip-companies")
|
@app.get("/api/settings/paperclip-companies")
|
||||||
|
|||||||
52
web/mcp_introspection.py
Normal file
52
web/mcp_introspection.py
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user