GitHub - corporatepiyush/mcp-pg-rust: Postgresql MCP server written in Rust

13 min read Original article ↗

mcp-postgres

Crates.io License Rust

mcp-postgres is a high-performance MCP server that brings PostgreSQL into Claude Desktop and any MCP-compatible AI tool. 135 PostgreSQL tools, lock-free connection pooling, sub-10ms latency.

MCP suite. One of four high-performance MCP servers written in Rust — mcp-postgres · mcp-filesystem · mcp-memory · mcp-web-search. All implement MCP protocol revision 2025-11-25.

Quick Start

Install

# From crates.io
cargo install mcp-postgres

# Or from Homebrew (macOS)
brew tap corporatepiyush/mcp-postgres
brew install mcp-postgres

Run

# Stdio mode (for Claude Desktop)
mcp-postgres --database-url "postgres://user:pass@localhost:5432/mydb" --stdio

# TCP server (port 3000)
mcp-postgres --database-url "postgres://user:pass@localhost:5432/mydb"

# HTTP/2 server (port 3001)
mcp-postgres --database-url "postgres://user:pass@localhost:5432/mydb" --http-port 3001

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "postgres": {
      "command": "mcp-postgres",
      "args": ["--database-url", "postgres://user:pass@localhost:5432/mydb", "--stdio"]
    }
  }
}

Complete setup guide →


Why mcp-postgres?

Feature mcp-postgres DIY / psql
135 purpose-built tools Schema inspection, DDL, monitoring, replication, batch ops, security audit, text search, extensions, maintenance, and more You build every query from scratch
Lock-free connection pool Zero-mutex crossbeam::ArrayQueue — pure CAS loops, no kernel overhead Deadpool or manual Mutex<VecDeque>
Dual-protocol TCP (3000) + HTTP/2 (3001) + stdio — one binary, three transports Multiple servers to wire up
Sub-10ms latency Allocated for AI interactivity — hot path is allocation-free Unpredictable
SQL injection prevention Every identifier validated, quote_ident sanitization, structured predicates Manual parameterization
PG version-aware Queries verified against PG 16–18 docs, graceful fallbacks for version differences Version-specific failures

Command-Line Options

Usage: mcp-postgres [OPTIONS]

Options:
  -d, --database-url <URL>       PostgreSQL connection string
  -H, --host <HOST>              TCP server host             [127.0.0.1]
  -p, --port <PORT>              TCP server port             [3000]
      --http-port <PORT>         HTTP/2 server port          [3001]
      --min-connections <N>      Min pool connections        [5]
      --max-connections <N>      Max pool connections        [20]
      --log-level <LEVEL>        Log level                   [info]
      --enable-metrics           Prometheus /metrics endpoint
      --metrics-port <PORT>      Metrics port                [9090]
      --stdio                    Stdio mode (Claude Desktop)
      --access-mode <MODE>       unrestricted, restricted    [unrestricted]
      --tls-cert <PATH>          PEM cert chain to serve HTTP over TLS (HTTPS)
      --tls-key <PATH>           PEM private key matching --tls-cert
  -h, --help                     Print help
  -V, --version                  Print version

TLS (HTTPS)

The HTTP/2 transport can be served over TLS (rustls, ring provider — the same provider used for sslmode-gated PostgreSQL connections, no OpenSSL/aws-lc). Provide a PEM certificate chain and private key via --tls-cert/--tls-key or the MCP_TLS_CERT/MCP_TLS_KEY environment variables and the HTTP server speaks HTTPS instead of plaintext. The two must be supplied together or startup is refused; when neither is set the HTTP transport stays plaintext (the default). The raw TCP transport is unaffected.

mcp-postgres --http-port 3001 --tls-cert ./cert.pem --tls-key ./key.pem

MCP Compliance

Implements the Model Context Protocol revision 2025-11-25 over JSON-RPC 2.0, via TCP, HTTP/2, or stdio.

Area Support
Transports stdio, TCP (3000), HTTP/2 (3001)
Protocol version 2025-11-25, negotiates down to 2025-06-18 / 2025-03-26 / 2024-11-05
initialize ✅ version negotiation + instructions
tools/list, tools/call ✅ (135 tools)
CallToolResult content[] + structuredContent + isError
Capabilities advertised tools only — nothing is advertised that isn't implemented
resources · prompts · logging · completion ❌ roadmap — see MIGRATION.md

Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": { "name": "list_tables", "arguments": {} },
  "id": 1
}

Result — a spec-compliant CallToolResult. The payload is available as a machine-readable structuredContent object and as serialized text; tool failures come back with isError: true (not as JSON-RPC protocol errors) so the model can self-correct:

{
  "content": [{ "type": "text", "text": "{\"tables\":[\"users\",\"orders\"]}" }],
  "structuredContent": { "tables": ["users", "orders"] },
  "isError": false
}

Upgrading from 4.x? The result shape changed — see MIGRATION.md.


Tools (135 Total)

⚡ Query Execution (8) — execute_query, execute_insert, execute_update, execute_delete, explain_query, async_execute_insert, async_execute_update, async_execute_delete

Fire off raw SQL, run parameterized inserts/updates/deletes with automatic type coercion, and peek under the hood with EXPLAIN ANALYZE plans. Async variants let you fire-and-forget long-running operations without blocking your AI workflow.

🪄 Key moves: execute_query for ad-hoc SQL, explain_query to spot missing indexes or seq-scans, async_execute_* for bulk writes that outlive the request.

🔍 Schema Inspection (8) — list_tables, describe_table, list_schemas, list_indexes, list_triggers, show_constraints, list_partitions, get_object_details

Peel back the layers of your database: list every table across schemas, drill into column types and nullability, inspect index definitions, trigger functions, check constraints, and navigate partitioned table hierarchies.

🪄 Key moves: describe_table is your go-to for column metadata, get_object_details shows DDL + stats in one shot, list_partitions maps your partitioning tree.

🏗️ DDL Operations (15) — create/drop table, view, schema, sequence, index, partition, alter_view, backup_table

Full DDL surface for schema evolution. Spin up tables with typed columns, create views to simplify complex queries, generate sequences for auto-increment IDs, build indexes for performance, and partition large tables for manageability.

🪄 Key moves: backup_table snapshots a table before risky DDL, alter_view redefines without dropping, create_partition attaches new ranges to existing partition trees.

📦 Batch Operations (4) — async_batch_insert, async_batch_update, async_batch_delete, async_batch_insert_copy

Move mountains of data in a single call. Insert thousands of rows with structured arrays, run bulk updates and deletes with filtered predicates, or use async_batch_insert_copy leveraging PostgreSQL's COPY protocol for wire-speed ingestion.

🪄 Key moves: async_batch_insert_copy is 2-3x faster than row-by-row INSERT for 10K+ rows, async_batch_update handles conditional multi-row updates atomically.

📊 Database Monitoring (10) — table/index stats, database/table size, cache hit ratio, vacuum, analyze, pg_stat_statements, reset_statistics

See how your database is really doing. Track table and index usage stats, measure disk consumption per database or table, calculate cache hit ratios, kick off VACUUM and ANALYZE, and query pg_stat_statements to find your top CPU-hungry queries.

🪄 Key moves: get_cache_hit_ratio tells you if your shared_buffers are sized right, get_pg_stat_statements surfaces slow queries by total time, analyze_table refreshes planner stats on-demand.

🔌 Connection Management (4) — list_connections, show_current_user, show_running_queries, show_connection_summary

See who's connected, what they're running, and how connections are distributed. Diagnose connection bloat, find runaway queries, and identify which application is hogging the pool.

🪄 Key moves: show_running_queries catches long-running queries in-flight, show_connection_summary groups by state/application for a bird's-eye view.

🔐 Security & Users (5) — list_users, user/role privileges, database privileges, session_info

Audit your security posture: enumerate database roles and their login capabilities, inspect table-level and schema-level GRANTs, check role membership chains, and review database-level ACLs.

🪄 Key moves: list_user_privileges surfaces exactly what each user can SELECT/INSERT/UPDATE/DELETE, list_role_memberships reveals privilege escalation paths through role inheritance.

⚙️ Configuration (5) — all_settings, get_setting, memory/performance/log_settings

Navigate the sprawling world of postgresql.conf without grepping. View every GUC parameter, look up specific settings by name, and filter by category to zero in on memory tuning, performance knobs, or logging config.

🪄 Key moves: show_memory_settings pulls shared_buffers, work_mem, maintenance_work_mem in one shot, get_setting for a quick SHOW of any parameter.

🔄 Transaction Monitoring (7) — active_transactions, locks, waiting_locks, isolation, deadlocks, autocommit, transaction_timeout

Dive into the transaction machinery: detect long-running idle-in-transaction sessions, map lock contention chains with pg_blocking_pids, identify deadlocks, check isolation levels, and monitor transaction age to prevent bloat.

🪄 Key moves: show_waiting_locks shows who's blocked by whom, show_deadlocks queries pg_stat_activity for blocked processes, show_transaction_timeout checks idle_in_transaction_session_timeout.

📋 Replication (5) — replication_status, replication_slots, standby_servers, wal_info, base_backup_progress

Keep your replicas healthy. Monitor streaming replication lag in bytes and time, inspect replication slots for WAL accumulation, list standby servers with their flush/replay positions, and track pg_stat_progress_basebackup for ongoing backups.

🪄 Key moves: show_replication_status shows sender/receiver pairs with lag, list_replication_slots helps you spot slots that are consuming too much WAL.

🏥 Database Health (4) — analyze_db_health, unused/duplicate indexes, vacuum_progress

Get a comprehensive wellness check: scan for unused indexes that waste write IO, detect duplicate indexes that bloat storage, monitor VACUUM progress across all databases, and get a health score with actionable recommendations.

🪄 Key moves: list_unused_indexes finds indexes with zero scans since stats reset, show_vacuum_progress tracks autovacuum workers in real-time.

🗑️ Maintenance (1) — truncate_table

Safely and quickly remove all rows from a table while preserving the table structure. Performs privilege validation before executing, and supports RESTART IDENTITY for serial column reset.

🪄 Key moves: Blows away table data faster than DELETE — ideal for staging tables and temp data cleanup.

🧠 Index Advisor (1) — suggest_indexes

Analyzes your query workload from pg_stat_statements and suggests index candidates based on WHERE clauses, JOIN conditions, and ORDER BY patterns. Each suggestion includes the estimated impact and DDL to create it.

🪄 Key moves: Run after capturing a representative workload — the suggestions get smarter the more queries pg_stat_statements has sampled.

🔬 Performance Audit (2) — audit_performance, analyze_query_performance

Deep-dive query forensics with buffer-level analysis. Inspect shared hits vs reads, execution time breakdowns by plan node, row estimate accuracy, and temp file spill detection. audit_performance runs a full sweep across your top queries.

🪄 Key moves: analyze_query_performance with EXPLAIN (ANALYZE, BUFFERS) catches seq-scans on large tables, misestimated row counts, and sort spills to disk.

🛡️ Security Audit (3) — audit_security, audit_user_permissions, audit_role_hierarchy

Run a comprehensive security posture review: find users with superuser privileges, detect excessive schema-level GRANTs, map role inheritance chains that could lead to privilege escalation, and audit columns containing sensitive-sounding names.

🪄 Key moves: audit_security produces an executive summary with severity ratings, audit_role_hierarchy visualizes role grant chains that might bypass intended restrictions.

🔎 Text Search (6) — search_vector, levenshtein_search, trigram_search, soundex_search, metaphone_search, full_text_config

Unlock fuzzy and phonetic search across your text data. Search by vector similarity via pgvector, find approximate matches with Levenshtein distance, use trigram similarity for flexible substring matching, or try Soundex/Metaphone for phonetic lookups when you don't know the spelling.

🪄 Key moves: trigram_search handles typos and partial matches gracefully, soundex_search finds names that sound alike, full_text_config lets you inspect tsvector configuration.

🧩 Extension Management (5) — list_extensions, install_extension, remove_extension, update_extension, extension_details

Manage your PostgreSQL extension ecosystem. List installed extensions with versions, install new extensions from available packages, upgrade to latest versions, remove unused ones, and inspect extension dependencies to understand what cascade effects to expect.

🪄 Key moves: extension_details shows extension-to-extension dependency chains, install_extension supports CASCADE for dependency auto-install.

📐 Schema Alter (7) — add/drop/alter_column, add/drop_constraint, set_default, drop_default

Evolve your schema surgically without writing raw DDL. Add nullable or NOT NULL columns with default values, change column types (when castable), drop obsolete columns, manage CHECK and UNIQUE constraints, and set or remove column defaults.

🪄 Key moves: add_column with NOT NULL + default backfills existing rows, drop_default stops future inserts from getting the default without touching existing data.

🕒 Session Management (3) — set_session_setting, show_session_settings, reset_session_setting

Twist session-level config knobs without editing postgresql.conf. Uses SET LOCAL scoped to the transaction for safety — settings auto-revert on commit/rollback so you never accidentally leave a modified config behind.

🪄 Key moves: set_session_setting for per-query statement_timeout or work_mem tweaks, reset_session_setting reverts a single setting to its cluster default.

👤 User Management (3) — create_user, alter_user, drop_user

Manage database role lifecycle with safety guards. Create users with password and login privileges, alter existing roles (rename, change password, toggle superuser/createrole flags), and drop users with ownership reassignment and dependency checks.

🪄 Key moves: drop_user refuses if the user owns objects (prevents accidental CASCADE carnage), alter_user can set per-role GUC defaults like statement_timeout.

💾 Data Tools (10) — export_table, import_table, show_table_data, search_data, compare_data, data_profile, find_duplicates, find_orphans, show_pg_stat_user_indexes, show_table_bloat

Explore, profile, and clean your data. Export tables to structured results, search across columns with pattern matching, compare two datasets row-by-row, profile column distributions and null ratios, find duplicate rows and orphaned foreign keys, and estimate table bloat to plan VACUUMs.

🪄 Key moves: data_profile gives you min/max/null_counts/approx_distinct per column in one shot, find_orphans detects FK violations where parent rows went missing, show_table_bloat estimates wasted space.

⚗️ Migration Helpers (7) — generate_migration, apply_migration, rollback_migration, list_migrations, show_migration_status, migration_history, validate_migration

End-to-end schema migration workflow. Generate timestamped migration files from DDL templates, apply pending migrations in order, rollback the last applied migration, track which migrations have been run, and validate the current schema state against the migration log.

🪄 Key moves: validate_migration detects schema drift (manual changes that skipped the migration system), rollback_migration reverts safely using generated down-scripts.

📈 Vector Database (1) — search_similarity

pgvector-powered similarity search for embeddings and vector data. Search by L2 distance, inner product, or cosine similarity with optional metadata filters. Supports indexing (IVFFlat, HNSW) for fast approximate nearest-neighbor lookups.

🪄 Key moves: Perfect for RAG workflows — feed in your embedding vectors and retrieve the top-K most semantically similar results with a single call.

📆 Time-Series (1) — analyze_timescale

TimescaleDB integration for time-series workloads. Inspect hypertable chunks, analyze compression ratios, review retention policies, and get recommendations for chunk interval tuning based on your ingestion patterns.

🪄 Key moves: Run analyze_timescale to check if your compression policy is keeping up with ingestion volume, or if chunk intervals need resizing.

📂 Data I/O (6) — import_csv, export_csv, import_json, export_json, show_table_data_paginated, bulk_load_from_file

Move data in and out of PostgreSQL in every common format. Import CSV/JSON with automatic type detection and error handling, export query results to CSV/JSON, paginate through large result sets with keyset pagination, and bulk-load from server-side files.

🪄 Key moves: show_table_data_paginated handles million-row tables without blowing up your context, bulk_load_from_file uses server-side COPY for zero-network-overhead ingestion.


Performance

Lock-Free Connection Pool (v4.0.0)

No mutexes. No semaphores. Just CAS loops.

┌─────────────┐     ┌──────────────────┐     ┌─────────────┐
│  Task 1     │     │  crossbeam::     │     │  Task 2     │
│  (acquire)  │────▶│  ArrayQueue      │◀────│  (release)  │
└─────────────┘     │  CachePadded     │     └─────────────┘
                    │  Head/Tail       │
                    │  (CAS only)      │
                    └──────────────────┘
Metric Deadpool (v3.x) LockFreePool (v4.0.0)
Lock acquisitions per acquire+release 3+ (Mutex + Semaphore) 0
Allocation on hot path Yes (VecDeque growth) Zero (pre-allocated)
False sharing Likely (adjacent fields) Cache-padded atomics
Inlining Cross-crate, opaque Monomorphic, LTO-friendly
Dependencies 2 (deadpool + deadpool-postgres) 0 external (crossbeam already in tree)

Sub-10ms latency is guaranteed by design: zero allocation on the hot path, monomorphic dispatch, cache-line-isolated atomics, and a single-pointer CAS for connection handoff.

Architecture

┌─────────────────┐         ┌─────────────────┐
│   TCP Client    │         │   HTTP Client   │
│  (port 3000)    │         │  (port 3001)    │
└────────┬────────┘         └────────┬────────┘
         └─────────────┬─────────────┘
                       │
              ┌────────┴────────┐
              │   JSON-RPC 2.0  │
              │  (MCP Protocol) │
              └────────┬────────┘
                       │
          ┌────────────┴────────────┐
          │   Tool Dispatcher       │
          │   (135 tools)           │
          └────────────┬────────────┘
                       │
          ┌────────────┴────────────┐
          │   Connection Pool       │
          │   (lock-free, CAS-only) │
          │   Min: 5, Max: 20       │
          └────────────┬────────────┘
                       │
              ┌────────┴────────┐
              │  PostgreSQL DB  │
              └─────────────────┘

Key Design Principles

  • Stateless HTTP — Each request is independent. Transaction state isolated per-connection.
  • Lock-free poolingcrossbeam::ArrayQueue with CachePadded atomics. Zero mutex acquisitions.
  • Input validation at the boundary — SQL capped at 10K chars, identifiers at 255, batch rows at 1K. SQL injection prevention via quote_ident.
  • PG version-aware queries — Verified against PG 16–18. Graceful fallbacks when views/columns differ across versions.

Versioning & Compatibility

Follows Semantic Versioning. The current line is 5.x, which targets MCP revision 2025-11-25. The 5.0.0 release changed the tools/call result shape to be spec-compliant — see MIGRATION.md and the CHANGELOG.

mcp-postgres MCP revision (default) Negotiates
5.x 2025-11-25 2025-06-18, 2025-03-26, 2024-11-05
≤ 4.x 2024-11-05

License

Apache-2.0

Support

GitHub Issues