
PostgreSQL Performance (read-only)
io.github.gouranshulv0.1.0Updated Oct 10, 2026
Read-only PostgreSQL performance diagnostics: slow queries, EXPLAIN, index advice, locks, functions
Overview
Read-only PostgreSQL performance diagnostics: slow queries, EXPLAIN plans, index suggestions, table health, locks and functions.
- What it does
- Gives an assistant real numbers from pg_stat_statements, EXPLAIN and the PostgreSQL catalogs through a narrow read-only interface. Tools include top_slow_queries, explain_query, suggest_indexes, unused_indexes, table_health, blocking_sessions, slow_functions and explain_function, plus schema resources and a guided diagnose_slow_database prompt. It recommends indexes but never creates them, and never cancels or terminates sessions.
- When to use it
- Use it when a database feels slow and you want the assistant to look at actual plans, table sizes, existing indexes and query load instead of guessing. Suited to diagnosing slow queries, index candidates, bloat and lock contention on a replica or staging database.
- Requirements
- Docker to run the published image, or Java 25 to build from source. Needs a PostgreSQL database with pg_stat_statements in shared_preload_libraries and a read-only role with pg_read_all_stats and pg_monitor. Set PGPERF_DB_URL, PGPERF_DB_USER, PGPERF_DB_PASSWORD and MCP_API_KEY; clients send the key as an Authorization bearer header. Function tools additionally need track_functions and auto_explain.
Installation
In SourceWeft
- Open PostgreSQL Performance (read-only) in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
pg-perf-mcp
[CI] [License: MIT] [Java 25] [Spring Boot 4.1]
An MCP server that lets AI assistants diagnose PostgreSQL performance problems safely: slow queries, EXPLAIN plans, index suggestions, table health and lock contention. It is read-only by design.
A real Claude Code session against the demo shop, sped up. First it finds which database functions are slow and why. Then it explains a slow catalog query and estimates the fix with a hypothetical index. Asked to create the index, it says it can't: every tool is read-only.
Why
When a page is slow, developers paste a query into a chat and ask "why is this slow?". The assistant then guesses, because it cannot see the plan, the table sizes, the existing indexes or which queries actually dominate the load.
pg-perf-mcp gives the assistant real numbers from pg_stat_statements, EXPLAIN and the
catalogs, through a narrow, audited, read-only interface. It cannot change data, cannot run DDL,
and never creates the indexes it recommends.
Demo
A condensed, illustrative session against the demo shop (docker compose up, then
./demo/workload.sh). Exact numbers vary from run to run:
Architecture
Tools
Resources: pg://schema/overview (tables, row estimates, sizes, indexes) and
pg://schema/{table} (columns, indexes with scan counts, foreign keys flagged when unindexed).
Prompt: diagnose_slow_database, a guided workflow: slow queries, then explain, suggest
indexes, table health, functions, then a prioritized summary.
Why a separate tool for database functions
EXPLAIN shows a function call as one opaque expression, and charges its time to whichever plan
node evaluates it. On the demo catalog page (20 products, each calling shop.product_rating),
explain_query blames the index scan on products (184 ms self time) and finds nothing else
serious. explain_function on the same query, against the full demo data:
How it works (ADR 0006): the query runs once
with auto_explain enabled for that transaction only, so the plan of each nested statement comes
back as a notice; per-function time comes from pg_stat_xact_user_functions and per-statement
calls from pg_stat_statements, both as before/after differences.
Security model
Five independent layers stand between the assistant and a write (details in ADR 0001):
- SQL guard: exactly one plain
SELECT. It is checked by a PostgreSQL-faithful lexer and JSqlParser, and both must agree. Blocks DML/DDL, multiple statements, data-modifying CTEs,SELECT INTO,FOR UPDATE/SHARE,COPY,DO, and dangerous functions (pg_sleep*,*_file,lo_*,dblink*,set_config,pg_terminate_backend,query_to_xml, ...). It fails closed. - Read-only role:
mcp_readonlyhasSELECTpluspg_read_all_stats/pg_monitor, withdefault_transaction_read_only = onandstatement_timeout = 5sset on the role. - Read-only connections: Hikari
readOnly=trueand a read-only session default. - One rolled-back read-only transaction per call:
statement_timeout/lock_timeoutset locally,standard_conforming_stringsforced on, always rolled back. - Row cap: every result is capped (default 200 rows).
Around those layers: a bearer API key (constant-time comparison; only /actuator/health is
open), a JSON audit line per call (SQL stored as a SHA-256 hash plus an 80-character preview),
error messages with no stack traces or connection details, and metrics mcp.tool.duration and
mcp.guard.rejections.
Quickstart
Requirements: Docker. There are two ways to start:
- Use it on your database: pull the published image. No clone or build.
- Try the demo shop: clone the repo and get a seeded database with realistic problems to diagnose.
Either way, finish with connecting your AI client.
Use it on your database
1. Pull the image (public on GHCR, built for amd64 and arm64):
2. Prepare the database (once). Create a read-only role like mcp_readonly (see
docker/init.sql) and make sure pg_stat_statements is in
shared_preload_libraries. Prefer a replica or staging database: analyze=true and
explain_function execute the query. For the function tools, also
enable function tracking.
3. Run it. Put the settings in a file so secrets stay out of your shell history:
host.docker.internal reaches a database on your machine (Docker Desktop; on Linux add
--add-host=host.docker.internal:host-gateway). The MCP endpoint is now
http://localhost:8080/mcp.
4. Connect your AI client: see below.
Try the demo shop
To build and test from source you also need Java 25.
Generate some slow-query statistics (optional, but makes top_slow_queries interesting):
Connect from Claude Code
In PowerShell, set the key first, or the header goes out empty and the server answers 401:
Then ask: "Use pg-perf to find out why the shop database is slow", or run the
diagnose_slow_database prompt.
Connect from Claude Desktop
Claude Desktop launches stdio servers, so use the mcp-remote bridge
(claude_desktop_config.json):
Connect from Cursor
~/.cursor/mcp.json (or .cursor/mcp.json in a project):
Connect from VS Code (Copilot agent mode)
.vscode/mcp.json. VS Code prompts for the key once and stores it securely:
Enable the function tools (optional)
slow_functions and explain_function need two extra settings on your own database. The demo
shop already has them, and every other tool works without them:
Without these, explain_function still reports what it can and says in notes what is missing.
Configuration
Properties can also be set as environment variables (PGPERF_QUERY_STATEMENT_TIMEOUT=3s, ...).
Metrics are at /actuator/prometheus (requires the API key).
Build and test
This runs unit tests, Testcontainers integration tests against PostgreSQL 18 (same image, init
scripts and read-only role as compose; needs Docker), an end-to-end test with the MCP Java SDK
client over HTTP, and a JaCoCo gate (80% lines on the guard and analysis packages).
Design decisions
- 0001: Read-only by design, enforced in depth
- 0002: Streamable HTTP transport
- 0003: Summarized plans instead of raw EXPLAIN
- 0004: Suggest indexes, never execute them
- 0005: Platform versions and API differences
- 0006: Looking inside database functions
Roadmap
- OAuth 2.1 resource server per the MCP authorization spec (per-user identity instead of a shared key)
- Multiple named database targets, with read replicas preferred
- Workload-level index advice (weigh candidates across all top queries, not one at a time)
auto_explainlog /pg_stat_kcacheingestion for plans of queries that already ran in production- Optional raw-plan output and per-tool rate limits
- MCP elicitation to confirm
analyze=trueon expensive queries
License
MIT © 2026 Anshul Gour
Source: README.md at commit 2b97e2b
Tools
0Version history
1- v0.1.0LatestOct 10, 2026


