53 lines
1.6 KiB
Python
53 lines
1.6 KiB
Python
# 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
|