mcp-sql-metrics
Exposes read-only PostgreSQL queries as typed MCP tools, enabling natural-language data analysis while keeping raw tables hidden and enforcing safety limits.
README
mcp-sql-metrics
An MCP server that exposes read-only SQL queries as typed tools.
You declare your queries in a YAML file. The server turns each one into an MCP tool with a real argument schema, runs it as a parameterized read against PostgreSQL, and returns the rows as JSON.
Why this exists
The intuitive way to let a language model answer questions about your data is to export the data and put it in the prompt. That approach breaks down for three reasons:
- The context window doesn't fit a real history. A year of orders is not going in a prompt.
- Cost scales with the size of the dump, not with the size of the answer.
- Models make arithmetic errors. Asking one to sum a column is asking for a wrong number that looks right.
The approach that works is the opposite: give the model tools, not data. The model decides which question to ask and interprets the result; PostgreSQL does the arithmetic, because PostgreSQL does not make arithmetic mistakes.
This server is that pattern, packaged. It also means the model never sees a full table — only the aggregated result of a query it asked for, which is a meaningful difference when the underlying data is sensitive.
Install
pip install mcp-sql-metrics
Use
Write a query catalogue:
queries:
- name: daily_sales
description: >
Returns total revenue, order count and units sold for a single day.
Use it when the user asks how a specific date performed.
params:
- name: day
type: string
description: The date to report on, in YYYY-MM-DD format.
sql: |
SELECT
COUNT(*) AS orders,
COALESCE(SUM(total), 0) AS revenue
FROM orders
WHERE DATE(created_at) = %(day)s
Run it:
export DATABASE_URL="postgresql://user:pass@host/db"
export QUERIES_FILE="queries.yaml"
mcp-sql-metrics
Or point an MCP client at it:
{
"mcpServers": {
"sql-metrics": {
"command": "mcp-sql-metrics",
"env": {
"DATABASE_URL": "postgresql://user:pass@host/db",
"QUERIES_FILE": "/path/to/queries.yaml"
}
}
}
}
To explore the tools interactively:
uv run mcp dev src/mcp_sql_metrics/server.py
Safety model
Read-only is enforced in three independent places, because one check is a single point of failure:
- At load time, every statement must start with
SELECTorWITH, must be a single statement, and must not contain a write or DDL keyword. A bad query stops the server from starting rather than failing later. - At execution time, the connection is put in read-only mode for the transaction, so a write cannot happen even if a statement slipped past the first check.
- Arguments are always bound as query parameters, never interpolated into the SQL string. There is no code path that builds a statement by concatenation.
Two more limits protect the caller rather than the database:
- Row cap per query (default 200). When a result is capped, the response says so explicitly, so the model knows to narrow its filters instead of assuming it saw everything.
- Statement timeout per query (default 10s), set with
SET LOCALso it cannot leak to another transaction.
Error handling
Errors are returned to the model as structured JSON rather than raised, and they are written so the model can correct itself:
{
"error": "missing required argument 'day'",
"expected": ["day"]
}
A model that gets "expected": ["day"] back will usually fix the call on its own. A model that gets a stack trace will not.
Configuration reference
| Field | Required | Default | Notes |
|---|---|---|---|
name |
yes | — | Becomes the tool name; must be a valid identifier |
description |
yes | — | What the model reads to decide when to call the tool |
sql |
yes | — | A single SELECT or WITH … SELECT |
params |
no | [] |
name, type, description; default makes it optional |
max_rows |
no | 200 |
Rows returned before the result is marked truncated |
timeout_ms |
no | 10000 |
Per-statement timeout |
Parameter types: string, integer, number, boolean. The set is deliberately small — every type the model can send maps to something the driver binds safely.
Environment
| Variable | Required | Notes |
|---|---|---|
DATABASE_URL |
yes | PostgreSQL DSN. The server exits at startup if unset. |
QUERIES_FILE |
no | Path to the catalogue. Defaults to queries.yaml. |
Point it at a database role with SELECT-only grants. The read-only transaction is a safety net, not a substitute for correct permissions.
Development
pip install -e ".[dev]"
pytest
ruff check .
License
MIT
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.