MCP server¶
An MCP (Model Context Protocol) server that lets an AI client (Claude Desktop, Cursor) call a subset of the REST API as tools. It is a thin client of the running server; it contains no query logic of its own and needs the app running.
What it is¶
- Binary:
bin/mcp-server, built withmake build-mcp - Transport: stdio only (no HTTP mode)
- Authentication to the app:
PGQUERYNARRATIVE_API_KEY, sent asAuthorization: Bearer <key>on every call; set it to the same value asSECURITY_API_KEYwhen the server has auth enabled - Target URL:
PGQUERYNARRATIVE_URL, defaulthttp://localhost:8080 - Call timeout:
PGQUERYNARRATIVE_TIMEOUT_SECONDS, default 180.generate_reportandask_questionrun one or two LLM calls server-side; against a local Ollama model on CPU this alone can take 30-100+ seconds, so the default has headroom for that. Lower it for a cloud LLM provider, raise it for slower hardware. - Safety limits: the target host is locked to prevent SSRF via a malicious URL, and responses are capped at 1 MiB
Configuring a client¶
{
"mcpServers": {
"pgquerynarrative": {
"command": "/path/to/pgquerynarrative/bin/mcp-server",
"env": {
"PGQUERYNARRATIVE_URL": "http://localhost:8080",
"PGQUERYNARRATIVE_API_KEY": "your-secret-key"
}
}
}
}
Claude Desktop config path: macOS
~/Library/Application Support/Claude/claude_desktop_config.json, Windows
%APPDATA%\Claude\, Linux ~/.config/Claude/. Cursor:
.cursor/mcp.json in the project root, or Settings → MCP. Template:
config/mcp-example.json. Restart the client after editing.
Tool surface¶
| Tool | Calls | Notes |
|---|---|---|
run_query |
POST /queries/run |
sql, limit, connection_id |
explain_sql |
POST /suggestions/explain |
Plain-English explanation of a query (LLM) |
list_schemas / get_schema |
GET /schema |
connection_id |
get_context |
GET /schema + GET /queries/saved |
connection_id applies only to the schema call, not the saved-query list |
list_connections |
GET /connections |
- |
suggest_queries |
GET /suggestions/queries |
intent, limit |
ask_question |
POST /suggestions/ask |
Natural language → SQL → narrative (LLM) |
generate_report |
POST /reports/generate |
Workbench narrative report (LLM, with deterministic fallback) |
list_saved_queries |
GET /queries/saved |
limit, offset, connection_id |
list_reports / get_report |
GET /reports / GET /reports/{id} |
limit, offset, connection_id |
All accept optional connection_id where relevant, see
Multiple connections.
AI-backed tools¶
Three of the twelve tools call the LLM the server is configured with:
ask_question, generate_report, and explain_sql. The rest are
deterministic: schema lookups, saved queries, and run_query never touch
an LLM.
MCP has no LLM configuration of its own. ask_question and generate_report
inherit whichever provider and model the running server has configured
(LLM_PROVIDER, LLM_MODEL, …); an MCP client can't select or override
either. The same is true of the governance controls: budgets, PII redaction,
and the external-data gate for cloud providers all apply to an MCP-triggered
call exactly as they would to the equivalent REST call, since it's the same
server code path underneath. See LLM providers for what's
configured and how, and note generate_report's deterministic fallback
(metrics-only narrative) applies here too when the LLM call fails or its
output can't be trusted.
If the configured LLM is unreachable or the call fails, ask_question and
explain_sql return an LLM error; generate_report still succeeds via its
fallback.
What is not supported¶
There are no MCP tools for investigations, EXPLAIN/EXPLAIN ANALYZE, plan compare,
result verification, pg_stat_statements / regressions, dashboards, schedules, share
links, or chat. The investigation workflow (Investigate a slow query
onward) is REST- and UI-only today; use the REST API directly for it.
Troubleshooting¶
| Symptom | Cause |
|---|---|
| Connection refused | The app isn't running at PGQUERYNARRATIVE_URL |
401 in the tool error |
Auth is on server-side and PGQUERYNARRATIVE_API_KEY is missing or wrong |
| Tool call rejected before any HTTP request | PGQUERYNARRATIVE_URL points at a host the SSRF lock refuses |
context deadline exceeded on generate_report / ask_question |
Local LLM call took longer than PGQUERYNARRATIVE_TIMEOUT_SECONDS; raise it |
The exact error (API POST /api/v1/queries/run: 401, connection refused, …) is
shown in the chat's tool result.