Skip to main content

CLI + MCP PostgreSQL health, migration safety, and query explain toolkit for agents

Reason this release was yanked:

Merged back into pg-dash; use @indiekitai/pg-dash instead

Project description

English | ไธญๆ–‡

PG Health ๐Ÿ˜

CLI + MCP-first, agent-native PostgreSQL health, migration safety, and query explain toolkit.

pg-health lives in your terminal and inside your AI agent (via MCP). Want a browser dashboard instead? See its sibling pg-dash.

Features

  • ๐Ÿšฆ check-migration โ€” static safety analysis of a migration SQL file (14 rules: missing CONCURRENTLY, NOT NULL without DEFAULT, DROP TABLE, prepared-statement cache invalidation, โ€ฆ)
  • ๐Ÿ”ฌ explain โ€” EXPLAIN (ANALYZE, BUFFERS) with a readable plan tree and heuristic warnings (large Seq Scan, heavy Nested Loop)
  • ๐Ÿ” Health check โ€” 20+ rules: cache hit ratio, connection usage, unused indexes, table bloat, replication lag, lock waits, vacuum stats, missing FK indexes, transaction ID age, WAL/background writer stats, config audit, and more
  • ๐Ÿ› ๏ธ suggest / fix โ€” prioritized recommendations with one-line SQL, plus safe auto-fixes (drop unused indexes, vacuum, analyze) with dry-run by default
  • ๐Ÿ“ฑ Notifications โ€” Telegram, Slack, webhook, email
  • ๐Ÿ“ˆ History & trend โ€” SQLite-backed time series over repeat checks
  • ๐Ÿค– MCP server โ€” 5 first-class tools for agents (check, suggest, fix, check_migration, explain), installed by default

Quick Start

# Install (pipx recommended)
pipx install pg-health

# Run a health check
pg-health check -c "postgresql://user:pass@host:5432/dbname"

# Analyze a migration before you ship it
pg-health check-migration migrations/20260423_add_role.sql

# EXPLAIN a slow query
pg-health explain "SELECT * FROM users WHERE email = 'a@b.c'" -c "postgresql://..."

# Start the MCP server for Claude / Cursor / etc.
pg-health-mcp

CLI Usage

# Basic check
pg-health check -c "postgresql://user:pass@localhost:5432/mydb"

# Save report to JSON file
pg-health check -c "..." -o report.json

# JSON output to stdout (agent-friendly)
pg-health check -c "..." --json

# Quiet mode - just output status (OK, WARNING, or CRITICAL)
pg-health check -c "..." --quiet

# With custom thresholds
pg-health check -c "..." --config config.yaml

# Get actionable recommendations
pg-health suggest -c "..."

# Apply quick fixes (with dry-run)
pg-health fix unused-indexes -c "..." --dry-run
pg-health fix vacuum -c "..." --tables orders,users
pg-health fix all -c "..."

# Analyze a migration SQL file (exits 1 on errors โ€” CI-friendly)
pg-health check-migration migrations/20260423_add_role.sql
pg-health check-migration migration.sql --json

# EXPLAIN a query with plan tree rendering
pg-health explain "SELECT * FROM orders WHERE user_id = 1" -c "..."
pg-health explain "SELECT * FROM users" -c "..." --no-analyze
pg-health explain "..." -c "..." --json

# Start web interface
pg-health serve --port 8767

# Generate status badge
pg-health badge -c "..." -o badge.svg

Exit Codes

The check command returns meaningful exit codes for automation:

Exit Code Status Meaning
0 OK All checks passed
1 WARNING One or more warnings
2 CRITICAL One or more critical issues

Agent-Friendly Output

For CI/CD pipelines and monitoring scripts:

# JSON output for parsing
pg-health check -c "..." --json | jq '.status'

# Simple status for shell scripts
if [ "$(pg-health check -c "..." --quiet)" = "OK" ]; then
  echo "Database healthy"
fi

# Use exit codes
pg-health check -c "..." --quiet
case $? in
  0) echo "OK" ;;
  1) echo "WARNING - check logs" ;;
  2) echo "CRITICAL - immediate attention needed" ;;
esac

MCP Server (for AI Agents)

pg-health ships with an MCP server for integration with Claude, Cursor, and other agents โ€” installed by default as of v2.0. No extras needed.

Add to Claude Desktop / Claude Code

Add to ~/.claude/mcp.json (or ~/.config/claude/claude_desktop_config.json):

{
  "mcpServers": {
    "pg-health": {
      "command": "pg-health-mcp",
      "env": { "DATABASE_URL": "postgresql://user:pass@host:5432/db" }
    }
  }
}

Available Tools

Tool Description
pg_health_check Run comprehensive health check, returns status + issues
pg_health_suggest Get prioritized optimization recommendations
pg_health_fix Apply fixes (dry-run by default for safety)
pg_health_check_migration Analyze a migration SQL file for safety issues (14 static rules)
pg_health_explain EXPLAIN ANALYZE a SELECT query, return parsed plan tree

Migrating from pg-dash MCP

If your ~/.claude/mcp.json currently has the pg-dash MCP server:

"pg-dash": {
  "command": "npx",
  "args": ["-y", "@indiekitai/pg-dash-mcp"],
  "env": { "DATABASE_URL": "..." }
}

Replace it with:

"pg-health": {
  "command": "pg-health-mcp",
  "env": { "DATABASE_URL": "..." }
}

Existing agent prompts keep working during the transition โ€” the following deprecated tool names delegate to the new pg_health_* tools for 3 months, then get removed in v3.0:

Deprecated alias Routes to
pg_dash_health pg_health_check
pg_dash_check_migration pg_health_check_migration
pg_dash_analyze_query pg_health_explain
pg_dash_unused_indexes subset of pg_health_check

The aliases prepend a one-line banner reminding you to update โ€” use that as your TODO signal.

Other pg_dash_* tools that used to ship with pg-dash MCP (pg_dash_schema_diff, pg_dash_bloat, pg_dash_query_stats, โ€ฆ) do not have CLI equivalents. They live on as web UI features in pg-dash.

Example Usage

Claude or other agents can:

> Check my database health
[uses pg_health_check with your connection string]

> Is this migration safe to deploy?
[uses pg_health_check_migration with the path to the SQL file]

> Why is this query slow?
[uses pg_health_explain with the query]

> What should I optimize?
[uses pg_health_suggest]

> Drop the unused indexes (dry run first)
[uses pg_health_fix with fix_type="unused-indexes", dry_run=True]

Direct Python Usage

from pg_health.mcp_server import (
    pg_health_check, pg_health_suggest, pg_health_fix,
    pg_health_check_migration, pg_health_explain,
)

result = pg_health_check("postgresql://user:pass@host:5432/db")
suggestions = pg_health_suggest("postgresql://...")
fixes = pg_health_fix("postgresql://...", fix_type="unused-indexes", dry_run=True)
migration_report = pg_health_check_migration("postgresql://...", "./migration.sql")
plan = pg_health_explain("postgresql://...", "SELECT * FROM users WHERE id = 1")

Status Badge

Generate an SVG badge for dashboards or READMEs:

# Output to file
pg-health badge -c "..." -o badge.svg

# Output to stdout (pipe to file or HTTP response)
pg-health badge -c "..."

Badge shows:

  • Green "DB Health | OK" - All checks passed
  • Yellow "DB Health | 2 warnings" - Warning-level issues
  • Red "DB Health | CRITICAL" - Critical issues detected

Auto-Suggest

Get actionable recommendations based on your database health:

pg-health suggest -c "postgresql://user:pass@host:5432/db"

Output:

๐Ÿ” Analyzing database health...

โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
Recommendations
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”

๐Ÿ”ด HIGH PRIORITY

1. Increase shared_buffers
   Why: Cache hit ratio is 89.0% (should be >95%)
   Impact: Better cache hit ratio means faster queries
   Action: Edit postgresql.conf, set shared_buffers to ~25% of RAM. Current: 128MB

2. VACUUM ANALYZE public.orders
   Why: 150,000 dead tuples (15.2% bloat)
   Impact: Reclaim disk space, improve query performance
   SQL: VACUUM ANALYZE public.orders;

๐ŸŸก MEDIUM PRIORITY

3. Drop unused index idx_old_column
   Why: 0 scans since stats reset, 50MB wasted
   Impact: Free 50MB disk space, faster writes
   SQL: DROP INDEX public.idx_old_column;

๐ŸŸข LOW PRIORITY

4. Consider partitioning public.logs
   Why: Table is 12GB with 50,000,000 rows
   Impact: Improved query performance, easier maintenance
   Action: Partition by date/time column if available

Recommendations include:

  • Cache tuning - shared_buffers optimization when cache hit ratio is low
  • Unused indexes - Indexes that waste space and slow writes
  • Vacuum suggestions - Tables with high dead tuple counts
  • Missing indexes - Tables with heavy sequential scans
  • Partitioning hints - Large tables that could benefit from partitioning
  • Statistics updates - Tables with outdated statistics
  • Slow query analysis - Via pg_stat_statements

Quick Fix

Apply fixes automatically or preview with --dry-run:

# Preview what would be dropped
pg-health fix unused-indexes -c "..." --dry-run

# Actually drop unused indexes
pg-health fix unused-indexes -c "..."

# Vacuum specific tables
pg-health fix vacuum -c "..." --tables orders,users

# Run all safe fixes
pg-health fix all -c "..." --dry-run

Safe fixes (can auto-execute):

Fix Type What it does
unused-indexes DROP INDEX for indexes with 0 scans
vacuum VACUUM ANALYZE tables with high dead tuples
analyze ANALYZE tables with outdated statistics
all Run all of the above

Unsafe operations (suggest only shows SQL, never executes):

  • CREATE INDEX (may lock table)
  • Config changes (requires restart)
  • Schema changes

Configuration

Threshold Configuration

Create a YAML config file to customize thresholds:

# config.yaml
thresholds:
  cache_hit_ratio:
    warning: 0.95    # Warn if < 95%
    critical: 0.90   # Critical if < 90%
  
  connections:
    warning: 0.70    # Warn if > 70% of max_connections
    critical: 0.90
  
  replication_lag:
    warning: 10      # Warn if > 10 seconds behind
    critical: 60     # Critical if > 60 seconds behind
  
  dead_tuples:
    warning: 100000  # Warn if any table has > 100k dead tuples
    critical: 1000000
  
  lock_waits:
    warning: 5       # Warn if > 5 queries waiting for locks
    critical: 20
  
  table_bloat:
    warning: 0.10    # Warn if > 10% dead tuples
    critical: 0.20

Use the config:

# Via command line
pg-health check -c "..." --config config.yaml

# Via environment variable
export PG_HEALTH_CONFIG=/path/to/config.yaml
pg-health check -c "..."

See config.example.yaml for a complete example.

Environment Variables

# Set connection string
DATABASE_URL=postgresql://user:password@host:5432/database

# Set config file path
PG_HEALTH_CONFIG=/path/to/config.yaml

API Usage

# JSON API (AI-friendly)
curl -X POST https://pg.indiekit.ai/api/check \
  -H "Content-Type: application/json" \
  -d '{"connection_string": "postgresql://user:pass@host:5432/db"}'

Response:

{
  "ok": true,
  "report": {
    "database_name": "mydb",
    "checks": [
      {"name": "Cache Hit Ratio", "severity": "ok", "message": "..."},
      {"name": "Replication Lag", "severity": "info", "message": "Not a replica"},
      {"name": "Lock Waits", "severity": "ok", "message": "0 waiting locks"},
      ...
    ],
    "unused_indexes": [...],
    "tables": [...],
    "slow_queries": [...],
    "vacuum_stats": [...]
  }
}

Note: Special characters in password (like @) are auto-encoded.

Health Checks

Check OK Warning Critical
Cache Hit Ratio > 95% 90-95% < 90%
Index Hit Ratio > 95% 90-95% < 90%
Connection Usage < 70% 70-90% > 90%
Replication Lag < 10s 10-60s > 60s
Lock Waits โ‰ค 5 6-20 > 20
Dead Tuples < 100k 100k-1M > 1M
Table Bloat < 10% dead 10-20% dead > 20% dead
Duplicate Indexes 0 1+ pairs -
FK Missing Indexes 0-3 4+ -
Transaction ID Age < 500M 500M-1B > 1B

All thresholds are configurable via the YAML config file.

Requirements

  • Python 3.11+
  • PostgreSQL 12+ (for pg_stat_statements)
  • PyYAML (for custom thresholds)

Cloud Database Support

Supabase

# Get connection string from Supabase Dashboard > Settings > Database
pg-health check -c "postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres"

# Or use direct connection (port 5432)
pg-health check -c "postgresql://postgres:[password]@db.[project-ref].supabase.co:5432/postgres"

Neon

# Get connection string from Neon Console
pg-health check -c "postgresql://[user]:[password]@[endpoint].neon.tech/[database]?sslmode=require"

AWS RDS / Aurora

pg-health check -c "postgresql://[user]:[password]@[instance].rds.amazonaws.com:5432/[database]"

Note: Some checks (like pg_stat_statements) may require enabling extensions or specific permissions on managed databases.

Privacy

Your connection string is never stored. All checks run in real-time and results are not saved on our servers.

Notifications

Get alerts when health checks find issues.

Telegram

# Set credentials
export PG_HEALTH_TELEGRAM_TOKEN="your-bot-token"
export PG_HEALTH_TELEGRAM_CHAT_ID="your-chat-id"

# Run check and notify
pg-health notify -c "postgresql://..." --provider telegram

# Only notify if there are issues (default)
pg-health notify -c "..." --only-issues

# Always notify (even if healthy)
pg-health notify -c "..." --always

Slack

export PG_HEALTH_SLACK_WEBHOOK="https://hooks.slack.com/services/..."
pg-health notify -c "..." --provider slack

Webhook (generic)

export PG_HEALTH_WEBHOOK_URL="https://your-server.com/webhook"
pg-health notify -c "..." --provider webhook

Webhook payload:

{
  "database": "mydb",
  "status": "warning",
  "has_issues": true,
  "checks": [...],
  "summary": {"total_checks": 20, "warnings": 2, "criticals": 0}
}

Cron Example

# Run every hour, only alert on issues
0 * * * * pg-health notify -c "$DATABASE_URL" --only-issues

Email

export PG_HEALTH_SMTP_HOST="smtp.gmail.com"
export PG_HEALTH_SMTP_PORT="587"
export PG_HEALTH_SMTP_USER="your@gmail.com"
export PG_HEALTH_SMTP_PASS="app-password"
export PG_HEALTH_EMAIL_FROM="your@gmail.com"
export PG_HEALTH_EMAIL_TO="alerts@yourcompany.com"

pg-health notify -c "..." --provider email

Historical Trends

Track health metrics over time with built-in SQLite storage.

# Run check and save to history
pg-health check -c "..." --save

# View history
pg-health history
pg-health history --database mydb --days 30

# List available metrics
pg-health trend mydb

# View specific metric trend
pg-health trend mydb --metric "Cache Hit Ratio.ratio"
pg-health trend mydb --metric "Connection Usage.usage_ratio" --days 30

Data is stored in ~/.pg-health/history.db (configurable via PG_HEALTH_DATA_DIR).

Available metrics include:

  • Cache/Index hit ratios
  • Connection counts
  • Lock waits
  • Background writer stats
  • Database size

Roadmap

  • Replication lag monitoring
  • Lock wait detection
  • Vacuum statistics
  • Configurable thresholds
  • JSON output mode
  • Status badges
  • Auto-suggest recommendations
  • Quick fix commands
  • Telegram notifications
  • Slack notifications
  • Webhook notifications
  • Email alerts (SMTP)
  • Historical trends (SQLite)
  • Supabase/Neon support (via connection string)
  • Scheduled checks (built-in daemon)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pg_health-2.0.0.tar.gz (55.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pg_health-2.0.0-py3-none-any.whl (50.0 kB view details)

Uploaded Python 3

File details

Details for the file pg_health-2.0.0.tar.gz.

File metadata

  • Download URL: pg_health-2.0.0.tar.gz
  • Upload date:
  • Size: 55.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pg_health-2.0.0.tar.gz
Algorithm Hash digest
SHA256 40db1de43c1035b68b633e024eeaa6eb4d6782e139aff0fa1e69489e2ec07c69
MD5 0fcbba50e3b9b8eb1913c985ea383b8f
BLAKE2b-256 a7435ae386eeab7771dc69ffa107c27a8e6108142943dff64a7df88be84659e9

See more details on using hashes here.

File details

Details for the file pg_health-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: pg_health-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 50.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pg_health-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2bbc4d6503fc7096c80470c64a8728e0348414b7df7212688c54632b1c533484
MD5 6f1d2be717949f8707a62b1f5fd46dfd
BLAKE2b-256 51aae213090a48bf50b2ff9c08ff81f353d8939226a58090581dea5adb3c046a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page