A Model Context Protocol (MCP) server that provides AI-powered PostgreSQL performance tuning capabilities. This server helps identify slow queries, recommend optimal indexes, analyze execution plans, and leverage HypoPG for hypothetical index testing. - Retrieve slow queries from pgstatstatements with detailed statistics - Analyze query execution plans with EXPLAIN and EXPLAIN ANALYZE - Identify p
Add this skill
npx mdskills install isdaniel/pgtuner-mcpComprehensive PostgreSQL tuning MCP with excellent tool coverage and detailed setup guidance
A Model Context Protocol (MCP) server that provides AI-powered PostgreSQL performance tuning capabilities. This server helps identify slow queries, recommend optimal indexes, analyze execution plans, and leverage HypoPG for hypothetical index testing.
pg_stat_statements with detailed statisticsEXPLAIN and EXPLAIN ANALYZEpip install pgtuner_mcp
Or using uv:
uv pip install pgtuner_mcp
git clone https://github.com/isdaniel/pgtuner_mcp.git
cd pgtuner_mcp
pip install -e .
| Variable | Description | Required |
|---|---|---|
DATABASE_URI | PostgreSQL connection string | Yes |
PGTUNER_EXCLUDE_USERIDS | Comma-separated list of user IDs (OIDs) to exclude from monitoring | No |
Connection String Format: postgresql://user:password@host:port/database
To run this MCP server, the PostgreSQL user requires specific permissions to query system catalogs and extensions. Below are the minimal permissions needed for different feature sets.
-- Create a dedicated monitoring user
CREATE USER pgtuner_monitor WITH PASSWORD 'secure_password';
-- Grant connection to the target database
GRANT CONNECT ON DATABASE your_database TO pgtuner_monitor;
-- Grant usage on schemas
GRANT USAGE ON SCHEMA public TO pgtuner_monitor;
GRANT USAGE ON SCHEMA pg_catalog TO pgtuner_monitor;
-- Grant SELECT on user tables and indexes (for table stats and analysis)
GRANT SELECT ON ALL TABLES IN SCHEMA public TO pgtuner_monitor;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO pgtuner_monitor;
-- Grant access to system catalog views (read-only)
GRANT pg_read_all_stats TO pgtuner_monitor; -- PostgreSQL 10+
For pgstattuple (Bloat Detection):
-- Create the extension (requires superuser or appropriate privileges)
CREATE EXTENSION IF NOT EXISTS pgstattuple;
-- Grant execution on pgstattuple functions
GRANT EXECUTE ON FUNCTION pgstattuple(regclass) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION pgstattuple_approx(regclass) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION pgstatindex(regclass) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION pgstatginindex(regclass) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION pgstathashindex(regclass) TO pgtuner_monitor;
-- Alternative: Use pg_stat_scan_tables role (PostgreSQL 14+)
GRANT pg_stat_scan_tables TO pgtuner_monitor;
For HypoPG (Hypothetical Index Testing):
-- Create the extension (requires superuser or appropriate privileges)
CREATE EXTENSION IF NOT EXISTS hypopg;
-- Grant SELECT on HypoPG views
GRANT SELECT ON hypopg_list_indexes TO pgtuner_monitor;
GRANT SELECT ON hypopg_hidden_indexes TO pgtuner_monitor;
-- Grant execution on HypoPG functions with proper signatures
GRANT EXECUTE ON FUNCTION hypopg_create_index(text) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_drop_index(oid) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_reset() TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_hide_index(oid) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_unhide_index(oid) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_relation_size(oid) TO pgtuner_monitor;
-- Note: HypoPG operations are session-scoped and don't affect the actual database
-- 1. Create the monitoring user
CREATE USER pgtuner_monitor WITH PASSWORD 'secure_password';
-- 2. Grant connection and schema access
GRANT CONNECT ON DATABASE your_database TO pgtuner_monitor;
GRANT USAGE ON SCHEMA public TO pgtuner_monitor;
-- 3. Grant read access to user tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO pgtuner_monitor;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO pgtuner_monitor;
-- 4. Grant system statistics access
GRANT pg_read_all_stats TO pgtuner_monitor; -- PostgreSQL 10+
-- Grant access to pg_stat_statements views explicitly
GRANT SELECT ON pg_stat_statements TO pgtuner_monitor;
GRANT SELECT ON pg_stat_statements_info TO pgtuner_monitor;
-- 5. Install and grant access to extensions (as superuser)
-- pg_stat_statements (required)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- pgstattuple (for bloat detection)
CREATE EXTENSION IF NOT EXISTS pgstattuple;
GRANT pg_stat_scan_tables TO pgtuner_monitor; -- PostgreSQL 14+
-- OR grant individual functions:
-- GRANT EXECUTE ON FUNCTION pgstattuple(regclass) TO pgtuner_monitor;
-- GRANT EXECUTE ON FUNCTION pgstattuple_approx(regclass) TO pgtuner_monitor;
-- GRANT EXECUTE ON FUNCTION pgstatindex(regclass) TO pgtuner_monitor;
-- hypopg (for hypothetical index testing)
CREATE EXTENSION IF NOT EXISTS hypopg;
GRANT SELECT ON hypopg_list_indexes TO pgtuner_monitor;
GRANT SELECT ON hypopg_hidden_indexes TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_create_index(text) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_drop_index(oid) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_reset() TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_hide_index(oid) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_unhide_index(oid) TO pgtuner_monitor;
GRANT EXECUTE ON FUNCTION hypopg_relation_size(oid) TO pgtuner_monitor;
-- 6. Verify permissions
SET ROLE pgtuner_monitor;
SELECT * FROM pg_stat_statements LIMIT 1;
SELECT * FROM pg_stat_activity WHERE pid = pg_backend_pid();
SELECT * FROM pgstattuple('pg_class') LIMIT 1;
SELECT * FROM hypopg_list_indexes();
RESET ROLE;
You can exclude specific PostgreSQL users from being included in query analysis and monitoring results. This is useful for filtering out:
Set the PGTUNER_EXCLUDE_USERIDS environment variable with a comma-separated list of user OIDs:
# Exclude user IDs 16384, 16385, and 16386
export PGTUNER_EXCLUDE_USERIDS="16384,16385,16386"
To find the OID for a specific PostgreSQL user:
SELECT usesysid, usename FROM pg_user WHERE usename = 'monitoring_user';
When configured, the following queries are filtered:
pg_stat_activity queries (filters on usesysid column)pg_stat_statements queries (filters on userid column)This affects tools like get_slow_queries, get_active_queries, analyze_wait_events, check_database_health, and get_index_recommendations.
Add to your cline_mcp_settings.json or Claude Desktop config:
{
"mcpServers": {
"pgtuner_mcp": {
"command": "python",
"args": ["-m", "pgtuner_mcp"],
"env": {
"DATABASE_URI": "postgresql://user:password@localhost:5432/mydb"
},
"disabled": false,
"autoApprove": []
}
}
}
Or Streamable HTTP Mode
{
"mcpServers": {
"pgtuner_mcp": {
"type": "http",
"url": "http://localhost:8080/mcp"
}
}
}
# Default mode (stdio)
python -m pgtuner_mcp
# Explicitly specify stdio mode
python -m pgtuner_mcp --mode stdio
The SSE (Server-Sent Events) mode provides a web-based transport for MCP communication. It's useful for web applications and clients that need HTTP-based communication.
# Start SSE server on default host/port (0.0.0.0:8080)
python -m pgtuner_mcp --mode sse
# Specify custom host and port
python -m pgtuner_mcp --mode sse --host localhost --port 3000
# Enable debug mode
python -m pgtuner_mcp --mode sse --debug
SSE Endpoints:
| Endpoint | Method | Description |
|---|---|---|
/sse | GET | SSE connection endpoint - clients connect here to receive server events |
/messages | POST | Send messages/requests to the server |
MCP Client Configuration for SSE:
For MCP clients that support SSE transport (like Claude Desktop or custom clients):
{
"mcpServers": {
"pgtuner_mcp": {
"type": "sse",
"url": "http://localhost:8080/sse"
}
}
}
The streamable-http mode implements the modern MCP Streamable HTTP protocol with a single /mcp endpoint. It supports both stateful (session-based) and stateless modes.
# Start Streamable HTTP server in stateful mode (default)
python -m pgtuner_mcp --mode streamable-http
# Start in stateless mode (fresh transport per request)
python -m pgtuner_mcp --mode streamable-http --stateless
# Specify custom host and port
python -m pgtuner_mcp --mode streamable-http --host localhost --port 8080
# Enable debug mode
python -m pgtuner_mcp --mode streamable-http --debug
Stateful vs Stateless:
mcp-session-id header. Ideal for long-running interactions.Endpoint: http://{host}:{port}/mcp
Note: All tools focus exclusively on user/application tables and indexes. System catalog tables (
pg_catalog,information_schema,pg_toast) are automatically excluded from all analyses.
| Tool | Description |
|---|---|
get_slow_queries | Retrieve slow queries from pg_stat_statements with detailed stats (total time, mean time, calls, cache hit ratio). Excludes system catalog queries. |
analyze_query | Analyze a query's execution plan with EXPLAIN ANALYZE, including automated issue detection |
get_table_stats | Get detailed table statistics including size, row counts, dead tuples, and access patterns |
analyze_disk_io_patterns | Analyze disk I/O read/write patterns, identify hot tables, buffer cache efficiency, and I/O bottlenecks. Supports filtering by analysis type (all, buffer_pool, tables, indexes, temp_files, checkpoints). |
| Tool | Description |
|---|---|
get_index_recommendations | AI-powered index recommendations based on query workload analysis |
explain_with_indexes | Run EXPLAIN with hypothetical indexes to test improvements without creating real indexes |
manage_hypothetical_indexes | Create, list, drop, or reset HypoPG hypothetical indexes. Supports hide/unhide existing indexes. |
find_unused_indexes | Find unused and duplicate indexes that can be safely dropped |
| Tool | Description |
|---|---|
check_database_health | Comprehensive health check with scoring (connections, cache, locks, replication, wraparound, disk, checkpoints) |
get_active_queries | Monitor active queries, find long-running transactions and blocked queries. By default excludes system processes. |
analyze_wait_events | Analyze wait events to identify I/O, lock, or CPU bottlenecks. Focuses on client backend processes. |
review_settings | Review PostgreSQL settings by category with optimization recommendations |
| Tool | Description |
|---|---|
analyze_table_bloat | Analyze table bloat using pgstattuple extension. Shows dead tuple counts, free space, and wasted space percentage. |
analyze_index_bloat | Analyze B-tree index bloat using pgstatindex. Shows leaf density, fragmentation, and empty/deleted pages. Also supports GIN and Hash indexes. |
get_bloat_summary | Get a comprehensive overview of database bloat with top bloated tables/indexes, total reclaimable space, and priority maintenance actions. |
| Tool | Description |
|---|---|
monitor_vacuum_progress | Track manual VACUUM, VACUUM FULL, and autovacuum operations. Monitor progress percentage, dead tuples collected, index vacuum rounds, and estimated time remaining. Includes autovacuum configuration review and tables needing maintenance. |
limit: Maximum queries to return (default: 10)min_calls: Minimum call count filter (default: 1)min_mean_time_ms: Minimum mean (average) execution time in milliseconds filterorder_by: Sort by mean_time, calls, or rowsquery (required): SQL query to analyzeanalyze: Execute query with EXPLAIN ANALYZE (default: true)buffers: Include buffer statistics (default: true)format: Output format - json, text, yaml, xmlworkload_queries: Optional list of specific queries to analyzemax_recommendations: Maximum recommendations (default: 10)min_improvement_percent: Minimum improvement threshold (default: 10%)include_hypothetical_testing: Test with HypoPG (default: true)target_tables: Focus on specific tablesinclude_recommendations: Include actionable recommendations (default: true)verbose: Include detailed statistics (default: false)table_name: Name of a specific table to analyze (optional)schema_name: Schema name (default: public)use_approx: Use pgstattuple_approx for faster analysis on large tables (default: false)min_table_size_gb: Minimum table size in GB to include in schema-wide scan (default: 5)include_toast: Include TOAST table analysis (default: false)index_name: Name of a specific index to analyze (optional)table_name: Analyze all indexes on this table (optional)schema_name: Schema name (default: public)min_index_size_gb: Minimum index size in GB to include (default: 5)min_bloat_percent: Only show indexes with bloat above this percentage (default: 20)schema_name: Schema to analyze (default: public)top_n: Number of top bloated objects to show (default: 10)min_size_gb: Minimum object size in GB to include (default: 5)action: Action to perform - progress (monitor active vacuum operations), needs_vacuum (find tables needing vacuum), autovacuum_status (review autovacuum configuration), or recent_activity (view recent vacuum history)schema_name: Schema to analyze (default: public, used with needs_vacuum action)top_n: Number of results to return (default: 20)analysis_type: Type of I/O analysis - all (comprehensive), buffer_pool (cache hit ratios), tables (table I/O patterns), indexes (index I/O patterns), temp_files (temporary file usage), or checkpoints (checkpoint I/O statistics)schema_name: Schema to analyze (default: public)top_n: Number of top I/O-intensive objects to show (default: 20)min_size_gb: Minimum object size in GB to include (default: 1)The server includes pre-defined prompt templates for guided tuning sessions:
| Prompt | Description |
|---|---|
diagnose_slow_queries | Systematic slow query investigation workflow |
index_optimization | Comprehensive index analysis and cleanup |
health_check | Full database health assessment |
query_tuning | Optimize a specific SQL query |
performance_baseline | Generate a baseline report for comparison |
pgtuner://docs/tools - Complete tool documentationpgtuner://docs/workflows - Common tuning workflows guidepgtuner://docs/prompts - Prompt template documentationpgtuner://table/{schema}/{table_name}/stats - Table statisticspgtuner://table/{schema}/{table_name}/indexes - Table index informationpgtuner://query/{query_hash}/stats - Query performance statisticspgtuner://settings/{category} - PostgreSQL settings (memory, checkpoint, wal, autovacuum, connections, all)pgtuner://health/{check_type} - Health checks (connections, cache, locks, replication, bloat, all)HypoPG enables testing indexes without actually creating them. This is extremely useful for:
HypoPG enables testing hypothetical indexes without creating them on disk.
-- Create the extension
CREATE EXTENSION IF NOT EXISTS hypopg;
-- Verify installation
SELECT * FROM hypopg_list_indexes();
The pg_stat_statements extension is required for query performance analysis. It tracks planning and execution statistics for all SQL statements executed by a server.
Add the following to your postgresql.conf file:
# Required: Load pg_stat_statements module
shared_preload_libraries = 'pg_stat_statements'
# Required: Enable query identifier computation
compute_query_id = on
# Maximum number of statements tracked (default: 5000)
pg_stat_statements.max = 10000
# Track all statements including nested ones (default: top)
# Options: top, all, none
pg_stat_statements.track = top
# Track utility commands like CREATE, ALTER, DROP (default: on)
pg_stat_statements.track_utility = on
Note: After modifying
shared_preload_libraries, a PostgreSQL server restart is required.
-- Connect to your database and create the extension
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- Verify installation
SELECT * FROM pg_stat_statements LIMIT 1;
The pgstattuple extension is required for bloat detection tools (analyze_table_bloat, analyze_index_bloat, get_bloat_summary). It provides functions to get tuple-level statistics for tables and indexes.
-- Create the extension
CREATE EXTENSION IF NOT EXISTS pgstattuple;
-- Verify installation
SELECT * FROM pgstattuple('pg_class') LIMIT 1;
| Setting | Overhead | Recommendation |
|---|---|---|
pg_stat_statements | Low (~1-2%) | Always enable |
track_io_timing | Low-Medium (~2-5%) | Enable in production, test first |
track_functions = all | Low | Enable for function-heavy workloads |
pg_stat_statements.track_planning | Medium | Enable only when investigating planning issues |
log_min_duration_statement | Low | Recommended for slow query identification |
Tip: Use
pg_test_timingto measure the timing overhead on your specific system before enablingtrack_io_timing.
# Get top 10 slowest queries
slow_queries = await get_slow_queries(limit=10, order_by="total_time")
# Analyze a specific query's execution plan
analysis = await analyze_query(
query="SELECT * FROM orders WHERE user_id = 123",
analyze=True,
buffers=True
)
# Analyze workload and get recommendations
recommendations = await get_index_recommendations(
max_recommendations=5,
min_improvement_percent=20,
include_hypothetical_testing=True
)
# Recommendations include CREATE INDEX statements
for rec in recommendations["recommendations"]:
print(rec["create_statement"])
# Run comprehensive health check
health = await check_database_health(
include_recommendations=True,
verbose=True
)
print(f"Health Score: {health['overall_score']}/100")
print(f"Status: {health['status']}")
# Review specific areas
for issue in health["issues"]:
print(f"{issue}")
# Find indexes that can be dropped
unused = await find_unused_indexes(
schema_name="public",
include_duplicates=True
)
# Get DROP statements
for stmt in unused["recommendations"]:
print(stmt)
docker pull dog830228/pgtuner_mcp
# Streamable HTTP mode (recommended for web applications)
docker run -p 8080:8080 \
-e DATABASE_URI=postgresql://user:pass@host:5432/db \
dog830228/pgtuner_mcp --mode streamable-http
# Streamable HTTP stateless mode (for serverless)
docker run -p 8080:8080 \
-e DATABASE_URI=postgresql://user:pass@host:5432/db \
dog830228/pgtuner_mcp --mode streamable-http --stateless
# SSE mode (legacy web applications)
docker run -p 8080:8080 \
-e DATABASE_URI=postgresql://user:pass@host:5432/db \
dog830228/pgtuner_mcp --mode sse
# stdio mode (for MCP clients like Claude Desktop)
docker run -i \
-e DATABASE_URI=postgresql://user:pass@host:5432/db \
dog830228/pgtuner_mcp --mode stdio
pg_stat_statements (required for query analysis)hypopg (optional, for hypothetical index testing)Core dependencies:
mcp[cli]>=1.12.0 - Model Context Protocol SDKpsycopg[binary,pool]>=3.1.0 - PostgreSQL adapter with connection poolingpglast>=7.10 - PostgreSQL query parserOptional (for HTTP modes):
starlette>=0.27.0 - ASGI frameworkuvicorn>=0.23.0 - ASGI serverContributions are welcome! Please feel free to submit a Pull Request.
Install via CLI
npx mdskills install isdaniel/pgtuner-mcpPostgreSQL Performance Tuning MCP is a free, open-source AI agent skill. A Model Context Protocol (MCP) server that provides AI-powered PostgreSQL performance tuning capabilities. This server helps identify slow queries, recommend optimal indexes, analyze execution plans, and leverage HypoPG for hypothetical index testing. - Retrieve slow queries from pgstatstatements with detailed statistics - Analyze query execution plans with EXPLAIN and EXPLAIN ANALYZE - Identify p
Install PostgreSQL Performance Tuning MCP with a single command:
npx mdskills install isdaniel/pgtuner-mcpThis downloads the skill files into your project and your AI agent picks them up automatically.
PostgreSQL Performance Tuning MCP works with Claude Code, Claude Desktop, Cursor, Vscode Copilot, Windsurf, Continue Dev, Gemini Cli, Amp, Roo Code, Goose. Skills use the open SKILL.md format which is compatible with any AI coding agent that reads markdown instructions.