Skip to content

Commit ff518b9

Browse files
committed
Expose predefined queries as MCP resources
Add a resources module that registers two URIs on the server: teslamate://queries returns a JSON index of every predefined tool with its description and SQL source filename, and teslamate://queries/{name} returns the raw SQL for a given query. Clients can now browse the query catalog without invoking a tool, which is the idiomatic MCP way of exposing read-only catalog data. FastMCP resources cannot receive a Context, so anything that needs the DB pool (live schema, query results) stays on the tool side.
1 parent a8fca67 commit ff518b9

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

src/teslamate_mcp/resources.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""MCP resources: predefined-query metadata exposed by URI.
2+
3+
Resource handlers in FastMCP cannot receive a Context, so anything that needs
4+
runtime database access (like the live schema) stays in tool form. The
5+
resources here are derived purely from the bundled SQL + TOML files, so they
6+
are safe to serve as static-content URIs.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import json
12+
13+
from mcp.server.fastmcp import FastMCP
14+
15+
from .tools.registry import PredefinedTool
16+
17+
18+
def register_resources(mcp: FastMCP, tools: list[PredefinedTool]) -> None:
19+
"""Expose the list of predefined queries and each query's raw SQL."""
20+
21+
by_name: dict[str, PredefinedTool] = {t.name: t for t in tools}
22+
23+
@mcp.resource(
24+
uri="teslamate://queries",
25+
name="Predefined query index",
26+
description="List of every predefined query tool with its description.",
27+
mime_type="application/json",
28+
)
29+
async def queries_index() -> str:
30+
index = [
31+
{"name": t.name, "description": t.description, "source": t.source} for t in tools
32+
]
33+
return json.dumps(index, indent=2)
34+
35+
@mcp.resource(
36+
uri="teslamate://queries/{name}",
37+
name="Predefined query SQL",
38+
description="Raw SQL source for a named predefined query.",
39+
mime_type="text/sql",
40+
)
41+
async def query_sql(name: str) -> str:
42+
try:
43+
return by_name[name].sql
44+
except KeyError as exc:
45+
raise ValueError(f"Unknown query: {name}") from exc

src/teslamate_mcp/server.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from .config import Settings
1515
from .db import build_pool
16+
from .resources import register_resources
1617
from .schema import load_schema
1718
from .tools import (
1819
discover_predefined_tools,
@@ -68,6 +69,10 @@ async def lifespan(_server: FastMCP) -> AsyncIterator[AppContext]:
6869
statement_timeout_ms=settings.query_timeout_ms,
6970
row_limit=settings.custom_sql_row_limit,
7071
)
71-
logger.info("Registered %d predefined tools + run_sql + get_database_schema", len(tools))
72+
register_resources(mcp, tools)
73+
logger.info(
74+
"Registered %d predefined tools + run_sql + get_database_schema + 2 resources",
75+
len(tools),
76+
)
7277

7378
return mcp

0 commit comments

Comments
 (0)