PGAutoPilot
This server provides a model-agnostic MCP interface to PostgreSQL databases, combining schema-aware read/write access, multi-database support, safety controls, and backups.
Verify server/database status and health (pool stats, uptime, replication, vacuum, sequences, buffer cache, constraints, etc.)
List and switch between multiple configured databases
Explore tables: overview, full schema, table details, relationships, row counts, and sizes
Query data with filtering, sorting, pagination, column selection, and exact counts
Run aggregate/group-by analytics with count, sum, avg, min, and max
Explain and analyze SELECT query plans for optimization
Execute raw SELECT SQL with mandatory LIMIT, single-statement enforcement, and dangerous-function blocking
Insert, upsert, update, and delete rows with dry-run support, confirmation gates, sensitive-column stripping, and live schema validation
Create full SQL backups via pg_dump, locally or through Docker fallback
All actions pass through a shared safety model: redaction, blocked tables, read-only mode, timeouts, and pool limits
Provides tools to query, aggregate, insert, update, delete, and manage PostgreSQL databases using natural language, with schema-aware generation and safety controls.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@PGAutoPilot Show me customers that spent more than $500."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
PGAutoPilot
Model-agnostic PostgreSQL access for AI assistants, plus a hardened web dashboard.
PGAutoPilot lets any AI assistant safely explore, query, and manage a PostgreSQL database in natural language through an MCP server, and gives you a full management UI through an optional dashboard. Both entry points share the exact same safety model (redaction, blocked tables, read-only mode, the dangerous-function gate), so every action, whether from an editor or the web UI, is subject to the same guarantees.
Model-agnostic PostgreSQL-optimized Safe writes Read-only mode
Minimal config Single executable Docker Cloud databases
Connection pool Production-ready SSL Schema inspectionContents
Related MCP server: PostgreSQL MCP Server
What is PGAutoPilot?
Most database MCP servers expose raw SQL directly and leave destructive operations largely unguarded. PGAutoPilot takes the opposite stance:
Schema-aware: every identifier is validated against your live database.
Production-first: every write path is guarded by multiple configurable safety layers.
Model-agnostic: works identically with Claude, GPT-4o, Gemini, DeepSeek, Copilot, and open-source models.
Two surfaces, one safety model: the MCP server and the dashboard share the same redaction, blocked-tables, and read-only guarantees.
You: "Show me customers that spent more than $500."
-> db_aggregate(table="orders", by="customer_id", _sum="total",
orderBy={"_sum/total": "desc"}, take=5)
<- 23 customers foundRepository layout
The repo is a monorepo with two independent halves, the MCP server and the dashboard, each with its own toolchain, kept separate but unified under one repo and one shared safety model.
root/
│
├─ server/ MCP core, PostgreSQL MCP server (npm, single executable)
│ ├─ src/
│ │ ├─ index.ts MCP server entry point / initialization
│ │ ├─ config.ts Env loading + validation
│ │ ├─ db.ts PostgreSQL connection pool
│ │ ├─ schema.ts Live schema introspection (information_schema)
│ │ ├─ sqlBuilder.ts Parameterized, safe SQL builder
│ │ ├─ safety.ts Redaction, write access, warnings
│ │ ├─ toolDefinitions.ts Zod schemas for all 14 tools
│ │ ├─ toolHandlers.ts Tool implementations
│ │ ├─ sqlDump.ts Backups via pg_dump
│ │ └─ *test.ts Colocated vitest tests
│ ├─ dist/ Compiled + bundled artifact (pgautopilot.bundle.cjs)
│ ├─ config/ Ready-to-use MCP configs for every editor/CLI (one folder per client)
│ ├─ scripts/ bundle / sign / verify installers
│ ├─ docker-compose.yml PostgreSQL 16 + MCP server
│ └─ package.json npm package `pgautopilot`
│
└─ web/ Dashboard, pnpm/Turborepo workspace
├─ apps/
│ ├─ api/ Express API (auth, tool gateway, schema, migrations, snapshots)
│ └─ web/ React + Vite + Tailwind v4 single-page app
└─ packages/
├─ contracts/ Zod schemas + DTOs shared across the wire
├─ api-client/ Typed fetch client generated against contracts
├─ ui/ Presentational component library (design system)
├─ core/ HTTP-friendly port of the MCP tool/safety layer
└─ config/ Shared tsconfig + eslint presetsTwo halves, one safety model. The web/ workspace is isolated (its own
pnpm-lock.yaml, Turborepo, packages) and depends strictly inward
(apps/* -> packages/*; packages never depend on apps). web/packages/core
is an HTTP-friendly port of the MCP core's safety layer (server/src/safety.ts),
so both entry points enforce identical guarantees. A change to one safety layer
must be mirrored in the other, or the two surfaces diverge.
Each half has its own README stub pointing here, and each has its own verification gate, see Development and Verification before shipping.
The two entry points
Entry point | What it is | Built for |
MCP server ( | A single-executable MCP server ( | Running queries/tools from your editor (VS Code, Cursor, Claude Desktop, Zed, …) |
Dashboard ( | A hardened web UI (React + Express) with tables, tools, SQL editor, schema, migrations, snapshots | Managing the database in a browser, on top of the same safety layer |
The MCP server is the primary, fully self-contained artifact. The dashboard is an optional extension that connects to a database through the same gatekeeping logic.
Install
Requirements: Node.js 18+ · PostgreSQL 12+ (local, remote, Docker, or cloud) · any MCP-compatible client (for the server) or a modern browser (for the dashboard).
MCP server (server/)
npm:
npm install -g pgautopilotNo npm, one-line installer (clones, adds to PATH; re-run to update):
Platform | Command |
Linux/mac |
|
Windows |
|
Download & run: node pgautopilot.bundle.cjs
Clone & run: git clone https://github.com/cyberreinxy/pgautopilot.git && cd pgautopilot && node server/dist/pgautopilot.bundle.cjs
Uninstall:
Platform | Command |
Linux/mac |
|
Windows |
|
Dashboard (web/)
pnpm installInstalls are idempotent and signed; see Software Signing.
Quick Start
1. MCP server (AI assistants)
Create a .env anywhere on your machine (PGAutoPilot finds it automatically):
DATABASE_URL=postgresql://user:password@localhost:5432/yourdbConnect your AI assistant, identical config for VS Code, Cursor, Windsurf, Claude Desktop, Zed, JetBrains, Neovim, opencode, Cline, Kilo, Roo Code, Gemini CLI, Codex CLI, Copilot CLI, and more:
{ "mcpServers": { "postgres": { "command": "pgautopilot" } } }Ready-to-copy config files for every supported client live in
server/config/, each folder has a README showing where to
put the file in your project.
Then just ask: "Show me all tables." · "How many users signed up this month?" · "Find orders over $500 by customer." · "Add a product called 'Widget Pro' at $29.99."
2. Dashboard (web UI)
From web/:
pnpm install
cp .env.example .env # set DATABASE_URL, PORT, HOST, DASHBOARD_TOKEN
pnpm dev # API on 127.0.0.1:3000 + Vite on :5173 (hot reload)After pnpm build, the API also serves the built SPA directly, so the full app
runs monolithically on the API port.
Without Docker? Get a local database running in minutes with Run your own PostgreSQL (no Docker).
Architecture
AI Assistant
(Claude / Cursor / GPT / Gemini)
|
v
MCP Protocol
|
v
+----------------------------------+
| PGAutoPilot |
| |
| Schema Discovery |
| Identifier Validation |
| Safety Policy Engine <----+ |
| SQL Builder | |
| Connection Pool | |
| Tool Handlers (14 tools) | |
+---------------------------------+ |
| |
v |
PostgreSQL Everything passes through safetyEvery MCP request flows: natural language → MCP tool call → Zod parameter validation → live schema lookup → identifier validation → safety policy evaluation → parameterized SQL generation → time-limited execution → formatted response. Deterministic at every step. No hidden state, no side effects.
The dashboard follows the same flow through its API: browser → Express →
tool gateway → packages/core (a port of the same safety layer) → PostgreSQL.
Safety & Security
Threat model
PGAutoPilot runs on your machine and connects over the PostgreSQL wire
protocol. The AI assistant communicates only through MCP, and every request
passes the safety layer before reaching PostgreSQL. The DATABASE_URL
credential is the sole authentication boundary for the MCP server; the
dashboard adds optional bearer-token auth and binds to 127.0.0.1 by default.
The safety model is shared by both entry points (server/src/safety.ts and
web/packages/core/src/safety.ts); a change to one must be mirrored in the
other, or the two surfaces diverge.
Safety features
Threat | Protection |
SQL injection | Parameterized queries (never string interpolation) |
Accidental delete-all |
|
Full table update | Warning on >10 rows affected |
Secret exposure | Automatic redaction on read + strip on write |
Unknown table/column | Live schema validation before query build |
Slow queries | Configurable statement timeout (default 10s) |
Connection exhaustion | Configurable pool limit |
Arbitrary SQL |
|
Dangerous Postgres fns | Blocked: |
Bulk data loss | Dry-run support on every write tool |
Operational guarantees
Never logs
DATABASE_URL(hostname only in the startup banner)Never exposes redacted fields (passwords, tokens, keys →
***REDACTED***)Never runs multiple SQL statements in one call
Never
UPDATEs without live identifier validationNever
DELETEs all rows withoutconfirmAll: trueNever runs raw
INSERT/UPDATE/DELETE(use the structured tools)Never bypasses schema validation or exceeds the pool max
Sensitive columns
Columns matching password, token, secret, api_key, private_key, ssn,
credit_card, cvv (and variants) are auto-redacted on read and stripped on
write. Extend via SENSITIVE_COLUMNS.
Least-privilege database role
Do not connect with a superuser or the application's primary role. Create a
dedicated role per connection mode and point DATABASE_URL at it. The Postgres
role is the security boundary, not the client.
CREATE ROLE mcp_readonly LOGIN PASSWORD 'generate-a-strong-password';
GRANT CONNECT ON DATABASE yourdb TO mcp_readonly;
GRANT USAGE ON SCHEMA public TO mcp_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mcp_readonly;For a read/write connection, additionally grant INSERT, UPDATE, DELETE on
the tables the agent may touch. Never grant SUPERUSER. For non-loopback hosts,
prefer PGSSLMODE=verify-full.
Dashboard security
Localhost-first: the API binds to
127.0.0.1by default; setDASHBOARD_TOKENto requireAuthorization: Bearer <token>on all/api/*routes.Same safety model as the MCP server: every tool call passes through
packages/core.No CORS by default: the web app is served same-origin through the Vite proxy.
Masked errors: production mode returns generic errors so schema details never leak.
Rate limiting: tool execution, migrations, and failed authentication are throttled when configured.
Security
Responsible disclosure: report vulnerabilities privately via GitHub Security Advisories.
Signed releases: every release is SHA-256 checksummed and GPG-signed.
Zero runtime dependencies in the bundled MCP server build.
Logging policy: connection strings are never logged; per-request logs off in
--mode=production.
Tools (MCP server)
Read tools
Tool | Use when... | Returns |
| "What's in this database?" | All tables, row counts, relationships |
| "What columns does each table have?" | Full column/type/constraint map |
| "Is the connection working?" | Pool usage, uptime, latency |
| "Tell me about the orders table" | Columns, indexes, row estimates, size |
| "Show recent orders", "Find inactive users" | Filtered, sorted, paginated rows |
| "Get user with ID 42" | Single matching row |
| "How many users signed up this week?" | Row count (all or filtered) |
| "Total sales by category", "Average order value" | Grouped aggregates (count, sum, avg, min, max) |
| "I need a custom SELECT" | Raw query results (SELECT-only, limited to 5000 rows) |
Write tools
Tool | Use when... | Safety |
| "Add a new user" | Dry-run supported, schema-validated |
| "Create or update this product" | Dry-run supported, conflict-safe |
| "Update all shipped orders" | Warns on >10 rows, dry-run supported |
| "Delete old logs" | Warns on >10 rows, |
Maintenance tools
Tool | Use when... | Output |
| "Back up the database" | Full SQL dump via |
Examples
Find recent orders for a customer:
Prompt: "Show me the last 10 orders for customer 42"
Tool: db_find_many(table="orders", where={"customer_id": 42}, select=["id","total","status","created_at"], orderBy={"created_at":"desc"}, take=10)Count products by category:
Prompt: "How many products in each category?"
Tool: db_aggregate(table="products", by="category", _count="*", take=5, orderBy={"_count": "desc"})
-> Electronics: 142, Clothing: 89, Books: 54, ...Add a record (dry run first, then commit):
Prompt: "Add Jane Doe with email jane@example.com"
Tool: db_create(table="users", data={"email":"jane@example.com","name":"Jane Doe"}, dryRun=true)
-> "Dry run: valid. Proceed?" -> Row inserted with id 105Bulk delete with confirmation:
Prompt: "Delete logs from before 2025"
Tool: db_delete_many(table="logs", where={"created_at":{"lt":"2025-01-01"}}, dryRun=true)
-> "1,204 rows would be deleted. Confirm? [yes/no]"Every write tool is dry-run capable, and every query produces the exact parameterized SQL that runs, nothing touches your database silently.
API (dashboard)
All endpoints are mounted under /api. Token auth, rate limiting, and error masking apply when configured.
Method | Path | What it does |
GET |
| API + database diagnostics |
GET |
| List the safe MCP tools |
POST |
| Execute a tool with validated params |
GET |
| Live schema introspection |
GET |
| List applied + pending migrations |
POST |
| Apply all pending migrations |
POST |
| Apply specific migrations by version |
POST |
| Apply a single migration |
GET |
| Current safety/read-only/version state |
Tool execution passes through packages/core: sensitive-column redaction,
blocked-table checks, read-only enforcement, the raw-query dangerous-function
gate, and write confirmation requirements.
Migrations (dashboard)
Versioned SQL migrations live in web/apps/api/migrations (default
MIGRATIONS_DIR). Applied versions are tracked in the schema_migrations table.
File | What it does |
| Base schema: organizations, users, orders, invoices |
| Demo dataset for the dashboard views |
Apply pending migrations through the UI, the API (POST /api/migrations/apply),
or the migration runner in packages/core. In read-only mode, all apply
endpoints are blocked. 001_initial_schema.sql is idempotent; 002_seed_demo_data.sql
is not, so never assume both are safely re-runnable.
Docker
Run PGAutoPilot alongside a fresh PostgreSQL instance, or point it at a database you already have:
cd server
docker compose up --build # PostgreSQL 16 + MCP server together
docker run -e DATABASE_URL=... pgautopilot # Connect to an existing DBRun your own PostgreSQL (no Docker)
Don't use Docker for your database? Install PostgreSQL directly on your machine
and manage it yourself with psql or pgAdmin.
1. Install
Platform | How |
Windows | EDB installer. Note the superuser password it prompts for. |
macOS |
|
Linux (Debian/Ubuntu) |
|
Linux (Fedora) |
|
2. Set the postgres password
(Needed for TCP login; Windows/macOS set it at install time, so skip to step 3 if set):
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'mypass';"3. Create a database
createdb mydb (or CREATE DATABASE mydb; via psql).
4. Verify
pg_isready -h localhost -p 5432 should say accepting connections. Then:
DATABASE_URL=postgresql://postgres:mypass@localhost:5432/mydb5. Inspect with pgAdmin (optional)
New server: Host localhost · Port 5432 · Maintenance DB postgres · Username postgres · your password. Browse tables and run queries while PGAutoPilot works against the same database.
Backups need
pg_dump, bundled with PostgreSQL on Windows; install the client tools (postgresql-client/libpq) on macOS/Linux. See Troubleshooting.Tip: for production, create a dedicated least-privilege role for PGAutoPilot (see Safety & Security) and keep your
postgreslogin for pgAdmin/psql.
Configuration
MCP server (server/)
pgautopilot --readonly # block every write
pgautopilot --mode=production # suppress per-request logs
pgautopilot --readonly --mode=productionVariable | Default | What it does |
| (required) | PostgreSQL connection string |
| auto | SSL mode: |
|
| Maximum simultaneous database connections |
|
| Connection timeout (ms) |
|
| Idle-connection timeout (ms) |
|
| Max single-query time (ms) |
|
| Where |
| - | Docker container name for |
| - | Tables to block writes on (comma-separated) |
| - | Tables that warn but allow writes (comma-separated) |
| - | Extra columns to redact (comma-separated) |
| - | Set |
| - | Tools to disable entirely (comma-separated) |
|
| PostgreSQL schemas to introspect (comma-separated) |
|
|
|
Dashboard API (web/apps/api)
Variable | Default | What it does |
| (required) | PostgreSQL connection string |
|
| Port the API binds to |
|
| Bind address (localhost by default) |
| - | Bearer token required on all |
|
| Allow production start without a token |
|
| Max time for a single query (ms) |
|
| Directory holding versioned SQL migrations |
|
| Where |
| - | Docker container name for |
| - | Tables to block writes on (comma-separated) |
| - | Tables that warn but allow writes (comma-separated) |
| - | Extra columns to redact (comma-separated) |
| - | Set |
| - | Tools to disable entirely (comma-separated) |
|
|
|
|
|
|
| - | Requests per window for tool/migration endpoints |
|
| Rate-limit window |
|
| Failed-auth throttle when token auth is on |
|
| Failed-auth throttle window |
| - | Proxy trust when deployed behind a reverse proxy |
Dashboard Web (web/apps/web)
Variable | Default | What it does |
|
| Base URL for the API (same-origin proxy by default) |
| - | Bearer token sent on every request (match the API token) |
Connection string examples:
Where | URL |
Localhost |
|
Remote server |
|
Docker (port-mapped) |
|
Neon |
|
Supabase |
|
AWS RDS |
|
Render |
|
Cloud SSL: cloud providers (Neon, Supabase, RDS, Render) need SSL; set
PGSSLMODE=require (stronger: verify-full); auto-detected for most providers.
Performance
PGAutoPilot adds minimal overhead over a direct connection, latency depends on your database and network, with a zero-dependency single-file executable for the MCP server and configurable pool sizes and statement timeouts. Full benchmarks will be published once the project reaches a stable release.
Compatibility
Platform | Support |
Windows | Yes (native) |
macOS | Yes (native) |
Linux | Yes (native) |
Docker | Yes |
WSL | Yes |
ARM64 | Yes |
x64 | Yes |
Node 18 | Yes |
Node 20 | Yes |
Node 22 | Yes |
Software Signing
Method | How to verify |
SHA-256 hashes |
|
GPG signature |
|
cd server
npm run sign:gpg # signs dist/checksums.txt -> dist/checksums.txt.sig
npm run verify:gpg # verifies the signature + all checksumsInstall scripts verify checksums.txt automatically after cloning. On mismatch,
installation aborts. Bypass with --skip-verify (not recommended).
FAQ
Do I need to restart after schema changes? No, identifiers are validated against the live schema on every request.
Can PGAutoPilot modify my database automatically? Only through explicit tool calls; every write is deliberate, and dry-run-before-write is the default.
Does it work with Supabase? Yes, use the Supabase connection string and PGSSLMODE=require.
Does it require npm? No, npm, the one-line installer, or the single-file bundle all work.
Does it support SSL? Yes, auto-detected or via PGSSLMODE.
Can I disable writes entirely? Yes, pgautopilot --readonly.
Can I expose it publicly? No, the MCP server is designed for local/private network use (no auth layer or HTTP server); the dashboard binds to 127.0.0.1 by default.
Is it safe for production? Yes, every write path is guarded. See Safety & Security.
Troubleshooting
Error | Likely cause | How to verify | Fix |
|
|
| Create |
| PostgreSQL not running or wrong URL |
| Check host/port, Docker port mapping |
| Cloud DB requires SSL | Check provider docs | Set |
| Typo or wrong schema | Run | Use exact names from schema |
| Using | N/A | Use |
|
|
| Install |
Development
MCP server (server/), source layout
server/src/
index.ts Entry point, MCP server initialization
config.ts Environment variable loading and validation
db.ts PostgreSQL connection pool management
schema.ts Live schema introspection via information_schema
sqlBuilder.ts Parameterized, safe SQL query builder
safety.ts Redaction engine, write access control, warnings
toolDefinitions.ts Zod schemas for all 14 tools
toolHandlers.ts Tool implementations, one handler per toolCommand | What it does |
| Start the dev server with hot-reload |
| Compile TypeScript and bundle into a single executable |
| Run the compiled version |
| Full TypeScript type checking |
| TypeScript type-check + ESLint |
| Unit tests (vitest) |
| Auto-format source files with Prettier |
Dashboard (web/)
Command | What it does |
| Free conflicting ports, then run API + web with hot reload (Turborepo) |
| Check the API/web ports and kill processes using them |
| Build all workspace packages |
| Build, then run the bundled API ( |
| Full TypeScript type checking across the workspace |
| ESLint across the workspace |
| Unit tests across the workspace |
| Playwright E2E smoke test (from |
| Format all source with Prettier |
Dashboard source layout:
web/apps/api/src/ Express API (routes, middleware, services)
web/apps/web/src/ React SPA (app, components, features, routes)
web/packages/contracts/ zod schemas + DTOs
web/packages/core/ HTTP-friendly port of the MCP safety layer
web/packages/ui/ design system components
web/packages/config/ shared tsconfig + eslint presetsVerification before shipping
Run the full gate before merging or publishing:
# MCP core
cd server
npm run typecheck && npm run lint && npm run test && npm run build && npm run verify:gpg
# Dashboard
cd ../web
pnpm typecheck && pnpm lint && pnpm test && pnpm build
cd apps/web && pnpm test:e2eThe E2E smoke test (web/apps/web/e2e/smoke.spec.ts) boots the app and asserts
the tool runner renders. Run a full integration test against a live API +
Postgres if you need deeper coverage, since the smoke test starts only the web
server.
Roadmap
Authentication plugins / session management UI (API key, JWT)
SQL editor with syntax highlighting and safe-query checks
Backups UI (trigger and download
pg_dumpoutput)AI-assisted workflows (natural language to safe SQL, explain/optimize)
Role-based access control and audit log
Realtime monitoring (pool stats, slow queries, active sessions)
Contributing & License
See server/CONTRIBUTING.md for contribution guidelines.
MIT © 2026 Cyber Reinxy
Available Tools
18 toolsdb_aggregateAggregate / Group ByARead-onlyIdempotent
Groups rows by one or more columns and computes aggregate functions (count, sum, avg, min, max) on each group. This is the tool for analytical queries like "total sales by category", "average order value by month", or "count of users per country". Results are sorted by the aggregate by default.
When to use:
"How many products in each category?" (by=category, _count="*")
"Total revenue by region" (by=region, _sum="amount")
"Average order value by status" (by=status, _avg="total")
"Min and max prices per category" (by=category, _min="price", _max="price")
Parameter guidance:
by: comma-separated column names to group by (required). Example: "category, region"
where: optional JSON filter applied before grouping
orderBy: JSON object for sorting results. Use "_count", "_sum", "_avg", "_min", "_max" as the key. Example: {"_count": "desc"}
sum/avg/min/max: comma-separated numeric columns to aggregate
take: max groups to return (default 50)
Behavioral notes:
All aggregations run in a read-only transaction with a configurable timeout.
Results are returned as an array of group objects with the computed aggregates.
Groups with zero rows are excluded from the results.
Inputs: by (required), table (required), avg, max, +6 more.
db_backupBackup DatabaseARead-only
Creates a full SQL dump of the database using pg_dump and saves it to the configured backup directory (default: ./backups). The backup includes the full schema and data. When the local pg_dump binary is not available and DOCKER_CONTAINER is set, this tool falls back to running pg_dump inside the specified Docker container.
When to use:
Before running risky migrations or bulk operations
Periodic backups as part of maintenance routines
When the user asks to back up or export the database
Parameter guidance:
label: optional label for the backup filename (e.g., "pre-migration"). The final filename includes a timestamp: backups/db_backup_pre-migration_2026-09-01T120000.sql
confirmed: set to true to confirm the backup operation
Behavioral notes:
Requires pg_dump to be available either locally or via Docker.
The backup file is a plain SQL dump, not a binary format.
Large databases may take significant time and disk space.
This tool is idempotent — running it multiple times creates separate backup files.
The backup directory is created automatically if it does not exist.
Returns: path to the created backup file and its size.
Inputs: label, database, confirmed.
db_countCount RowsARead-onlyIdempotent
Returns the exact number of rows in a table, optionally filtered. This runs an exact COUNT(*) query — not an estimate. Use this when you need a precise count for reporting, validation, or before performing bulk operations.
When to use:
"How many users signed up this week?"
"Count all orders with status pending"
Before bulk deletes, to confirm the scope of the operation
Parameter guidance:
table: the table name (required)
where: optional JSON filter object (same syntax as db_find_many)
Behavioral notes:
Exact COUNT(*) on large tables (millions of rows) may be slow.
On large tables, consider using db_aggregate with a group-by for approximate breakdowns.
Returns an integer count, not a row object.
Inputs: table (required), where, database.
db_createCreate RowADestructive
Inserts a new row into the specified table. All columns are validated against the live schema before execution — typos in column names or type mismatches are caught early. Sensitive columns (passwords, tokens, API keys, etc.) are automatically stripped from the input to prevent accidental credential storage. Use dry_run=true to preview the insert without actually writing to the database.
When to use:
"Add a new user named Jane with email jane@example.com"
"Create an order for customer 42 with total $99.99"
Any single-row INSERT operation
Parameter guidance:
table: the target table name (required)
data: JSON object of column-value pairs to insert (required). Example: {"name": "Jane Doe", "email": "jane@example.com", "role": "admin"}
dry_run: set to true to validate without writing (default: false)
Behavioral notes:
The table name and all column names are validated against the live schema.
The INSERT runs in a transaction — if any constraint is violated, the entire operation rolls back with a clear error message.
On success, returns the inserted row including any auto-generated values (e.g., id).
Sensitive columns in the input are silently stripped before execution.
For multiple inserts, call this tool once per row or use db_raw_query with an INSERT ... VALUES statement (requires confirmed=true and ALLOW_RAW_WRITES).
Inputs: data (required), table (required), dryRun, database.
db_delete_manyDelete RowsADestructive
Deletes all rows matching the given filter. This is a destructive operation — deleted data cannot be recovered unless a backup exists. When the filter is empty ('{}'), ALL rows in the table would be deleted — this requires confirmAll=true as a safety gate. A warning is issued when more than 10 rows are affected. Use dry_run=true to preview the deletion scope before committing.
When to use:
"Delete all logs older than 2025"
"Remove user with email test@test.com"
"Purge expired sessions"
Parameter guidance:
table: the target table name (required)
where: JSON filter selecting rows to delete (required). Example: {"expired": true} Use '{}' with confirmAll=true to delete ALL rows (dangerous!).
dry_run: set to true to preview without deleting (default: false). STRONGLY recommended — always dry-run first to see how many rows would be affected.
confirmAll: REQUIRED when where='{}' to confirm deleting all rows
Behavioral notes:
A warning is emitted when more than 10 rows would be affected.
Empty filter with confirmAll=false returns an error requiring explicit confirmation.
The delete runs in a transaction — all rows are deleted atomically.
Returns the count of deleted rows.
ALWAYS use dry_run=true first to verify the scope before committing.
Inputs: table (required), where (required), dryRun, database, +1 more.
db_explainExplain Query PlanARead-onlyIdempotent
Runs EXPLAIN ANALYZE on a SQL SELECT query to analyze its execution plan, performance characteristics, and bottlenecks. Returns the full execution plan with timing, buffer usage, row estimates, and actionable analysis. This tool helps you understand how PostgreSQL processes a query and identifies optimization opportunities like missing indexes, sequential scans, or high-cost operations.
When to use:
"Why is this query slow?"
"Help me optimize this query"
"What indexes would improve this query?"
Before creating indexes, to understand current plan
Parameter guidance:
query: the SQL SELECT query to analyze (required). Example: "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON o.user_id = u.id WHERE o.created_at > '2025-01-01' GROUP BY u.name ORDER BY COUNT(o.id) DESC LIMIT 10"
analyze: set to true for actual timing (default: true)
buffers: set to true to include buffer usage stats (default: true)
Behavioral notes:
Only SELECT queries are accepted — EXPLAIN on writes is not supported.
The query is NOT executed — only the plan is analyzed.
Results include: execution plan (JSON), total cost, actual time, rows, buffers, and analysis text.
Analysis highlights: sequential scans on large tables, low selectivity filters, missing index opportunities, high-cost joins.
No extensions required — uses built-in PostgreSQL EXPLAIN.
Inputs: query (required), analyze, buffers, database.
db_find_firstFind First RowARead-onlyIdempotent
Finds a single row matching the given filter. Returns the first matching row or null if no rows match. Use this instead of db_find_many when you expect exactly one result and want a single object rather than an array. Sensitive columns are automatically redacted.
When to use:
"Get user with ID 42", "Find the order with this tracking number"
Lookups by unique identifier (primary key or unique constraint)
When you need exactly one row, not a list
Parameter guidance:
where: JSON filter object (required). Must be specific enough to target one row. Example: {"id": 42} or {"email": "user@example.com"}
select: optional JSON array of column names to return
Behavioral notes:
Returns a single JSON object, not an array.
Returns null (not an error) when no row matches the filter.
For queries that should return multiple rows, use db_find_many instead.
Inputs: table (required), where (required), select, database.
db_find_manyFind Many RowsARead-onlyIdempotent
Queries rows from a table with flexible filtering, column selection, sorting, and pagination. This is the primary tool for reading data. All parameters are optional except 'table' — omitting filters returns all rows (up to the limit). The default limit is 50 rows; the maximum is 500. Sensitive columns (passwords, tokens, keys) are automatically redacted in the output.
When to use:
"Show me recent orders", "Find users with email containing gmail"
"List products sorted by price", "Get page 2 of customers"
Any read query that needs filtering, sorting, or pagination
Parameter guidance:
where: JSON filter object. Supports operators: eq, neq, gt, gte, lt, lte, contains, startsWith, endsWith, in, notIn. Example: {"status": "active", "age": {"gt": 18}}
select: JSON array of column names to return. Example: ["id", "name", "email"]
orderBy: JSON object with column name and direction. Example: {"created_at": "desc"}
take: max rows to return (default 50, max 500)
skip: rows to skip for offset pagination
Behavioral notes:
All queries run in a read-only transaction with a configurable timeout (default 10s).
The table name is validated against the live schema before query execution.
Results include pagination metadata (total count when available).
On error, returns a clear message explaining what went wrong.
Inputs: table (required), skip, take, where, +3 more.
db_healthDatabase HealthARead-onlyIdempotent
Checks database connectivity, connection pool statistics, server uptime, and total request count. In multi-database mode, omit the database parameter to see health status for all configured databases simultaneously. Also runs PostgreSQL system checks: vacuum health (TXID wraparound risk), replication lag, index usage, sequence exhaustion, buffer cache hit rate, and invalid constraints.
When to use:
At the start of a session to verify the database is responsive.
When the user asks about connection health or pool utilization.
When troubleshooting slow responses or connection errors.
Periodically during long sessions to check pool exhaustion.
To check vacuum/replication/sequence health in production.
Behavioral notes:
In single-database mode, returns stats for the one connected database.
In multi-database mode, returns an array of health entries for all databases.
'idle' connections are available; 'active' connections are in use.
If pool utilization is high (>80%), consider increasing PGPOOL_MAX.
Vacuum checks query pg_stat_user_tables — no extensions required.
Replication checks query pg_stat_replication — returns empty on standalone instances.
Buffer cache checks pg_stat_database for hit rate below 90%.
Constraint checks pg_constraint for invalid (unvalidated) constraints.
Returns: JSON with connected (boolean), pool stats (total, idle, active), uptime seconds, total requests served, database connection summary, and health checks (vacuum, replication, indexes, sequences, bufferCache, constraints).
Inputs: database.
db_list_databasesList DatabasesARead-onlyIdempotent
Lists all databases configured in pgautopilot.json for multi-database mode, including each database's connection status (connected, not connected, or error), host, port, schema, and read-only setting. This tool is only meaningful in multi-database mode — in single-database mode it returns a message indicating only one database is configured.
When to use:
As the first call when the user wants to work with a specific named database.
To discover which databases are available before calling db_use_database.
To verify that all configured databases are reachable.
Returns: an array of database entries with name, connection summary, status (connected | not connected | error), and readonly flag. Databases that failed to connect at startup will show 'not connected' and will be lazily connected on first use.
Inputs: no named parameters.
db_overviewDatabase OverviewARead-onlyIdempotent
Provides a high-level overview of the connected PostgreSQL database: all tables with approximate row counts, foreign key relationships between tables, the server mode (read-only or read-write), and active safety rules (blocked tables, high-risk tables, sensitive columns). Use this as your first call when exploring an unfamiliar database to understand its structure before querying specific tables.
When to use:
At the start of a session to understand what tables exist and how they relate.
When the user asks "What's in this database?" or "Show me the tables."
Before writing queries, to confirm table names and relationships.
Behavioral notes:
Row counts are estimates from pg_stat (not exact COUNT(*)) for performance.
Only tables in the configured schemas are shown (default: public).
The overview includes safety metadata so the agent knows what operations are allowed.
Returns: JSON with tables array (name, estimated rows, column count), foreign keys, server mode, and safety configuration.
Inputs: database.
db_raw_queryRaw SQL QueryARead-onlyIdempotent
Executes a raw SQL SELECT statement with a mandatory LIMIT clause. This is the escape hatch for queries that cannot be expressed with the structured tools (complex JOINs, CTEs, window functions, subqueries, etc.). All queries run inside a read-only, single-statement transaction with a configurable timeout (default 10 seconds).
When to use:
Complex JOINs across multiple tables
CTEs, window functions, or subqueries
Custom aggregations not supported by db_aggregate
Exploratory queries during development
Parameter guidance:
sql: the raw SQL statement (required). Must be a SELECT and MUST include a LIMIT clause. Example: "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON o.user_id = u.id GROUP BY u.name ORDER BY COUNT(o.id) DESC LIMIT 10"
confirmed: set to true to acknowledge the raw query (currently informational)
Behavioral notes:
ONLY SELECT statements are allowed. INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, GRANT, and other DDL/DML are rejected.
The LIMIT clause is mandatory — queries without LIMIT are rejected.
Only single-statement queries are allowed (no semicolons separating multiple statements).
Dangerous functions (pg_read_file, COPY, pg_sleep, etc.) are blocked.
Results are capped at 5000 rows to prevent memory exhaustion.
Sensitive columns are automatically redacted in the output.
For write operations, use the structured tools (db_create, db_update_many, etc.).
Inputs: sql (required), database, confirmed.
db_schemaDatabase SchemaARead-onlyIdempotent
Returns the full database schema with column-level detail: every table's columns with their data types, nullability, defaults, constraints (primary key, unique, check), indexes, and foreign key relationships presented as a relationship diagram. This is introspected live from PostgreSQL's information_schema, so it always reflects the current state of the database.
When to use:
When you need column-level detail beyond what db_overview provides.
Before constructing queries with specific columns, to verify column names and types.
When the user asks about table structure, constraints, or relationships.
When debugging query errors related to column types or constraints.
Behavioral notes:
Schema is fetched fresh on every call (not cached) to catch DDL changes.
Only schemas listed in PG_SCHEMAS are included (default: public).
The relationship diagram shows foreign keys between tables, useful for JOIN queries.
Returns: JSON with tables (each containing columns with type, nullable, default, constraints), indexes, and a relationships array.
Inputs: database.
db_table_infoTable InformationARead-onlyIdempotent
Returns detailed information about a single table: exact row count (via COUNT(*)), all columns with their types and nullability, all indexes with their columns and uniqueness, foreign key relationships, and approximate table size on disk. Use this when you need specifics about one table that go beyond the overview.
When to use:
After db_overview, to drill into a specific table's details.
When the user asks "Tell me about the orders table" or "What indexes does users have?"
Before writing performance-sensitive queries, to understand available indexes.
When debugging issues related to a specific table.
Behavioral notes:
Row count is exact (uses COUNT(*)), which may be slow on very large tables.
The table name must exist in the database — a typo returns a clear error with suggestions from the schema.
Table size is approximate, based on pg_table_size().
Returns: JSON with row_count, columns array, indexes array, foreign_keys, and size_bytes.
Inputs: table (required), database.
db_update_manyUpdate RowsADestructiveIdempotent
Updates all rows matching the given filter. Every column and value is validated against the live schema before execution. When the filter is empty ('{}'), ALL rows in the table would be updated — this requires confirmAll=true as a safety gate. A warning is issued when more than 10 rows are affected. Use dry_run=true to preview the update without actually writing.
When to use:
"Mark all pending orders as shipped"
"Update user 42's email to new@example.com"
"Set all products in category X as discontinued"
Parameter guidance:
table: the target table name (required)
where: JSON filter selecting rows to update (required). Example: {"status": "pending"} Use '{}' with confirmAll=true to update ALL rows (dangerous!).
data: JSON object of column-value pairs to set (required). Example: {"status": "shipped", "shipped_at": "2026-09-01"}
dry_run: set to true to preview without writing (default: false)
confirmAll: REQUIRED when where='{}' to confirm updating all rows
Behavioral notes:
A warning is emitted when more than 10 rows would be affected.
Empty filter with confirmAll=false returns an error requiring explicit confirmation.
The update runs in a transaction — all rows are updated atomically.
Returns the count of affected rows.
Inputs: data (required), table (required), where (required), dryRun, +2 more.
db_upsertUpsert RowADestructiveIdempotent
Inserts a new row or updates an existing one using PostgreSQL's ON CONFLICT mechanism. The 'where' filter's columns must match a unique constraint or primary key on the table — this is how PostgreSQL determines whether to insert or update. If a matching row exists, only the columns specified in 'update' are changed. If no match exists, a new row is created with the values from 'create'. Use dry_run=true to preview.
When to use:
"Create this user if they don't exist, otherwise update their last_login"
"Upsert product SKU-123 with price $29.99"
Idempotent insert-or-update operations
Parameter guidance:
table: the target table name (required)
where: JSON filter identifying the conflict target (required). Columns must match a unique constraint or primary key. Example: {"email": "jane@example.com"}
create: JSON object of column-value pairs for the INSERT case (required)
update: JSON object of column-value pairs for the UPDATE case (optional). If omitted, no update occurs on conflict — the existing row is returned unchanged.
dry_run: set to true to validate without writing (default: false)
Behavioral notes:
The where columns MUST match a unique constraint or primary key — the tool validates this against the schema and returns an error if no matching constraint exists.
Returns the final row (either newly inserted or updated).
This is idempotent — calling it multiple times with the same data has the same effect as calling it once.
Inputs: table (required), where (required), create (required), dryRun, +2 more.
db_use_databaseSwitch Default DatabaseARead-onlyIdempotent
Switches the default database for subsequent tool calls. After switching, all tools that accept an optional 'database' parameter will use this database when the parameter is omitted. The target database must be listed in pgautopilot.json. This is an idempotent operation — calling it with the same database name has no effect.
When to use:
After calling db_list_databases, when the user wants to work with a specific database.
When switching between production and analytics databases mid-session.
Behavioral notes:
The target database is lazily connected on first use if not already connected.
If the target database is unreachable, the switch succeeds but subsequent tools will return a connection error with guidance.
This does NOT affect other MCP sessions or connections — only the current session.
Returns: confirmation with the new default database name and its connection details.
Inputs: database (required).
mcp_statusMCP Server StatusARead-onlyIdempotent
Reports whether the MCP server is ready to run database tools and which database it is connected to. Use this tool first if you suspect a configuration problem. When the server is not configured (e.g. DATABASE_URL is missing or invalid), this tool returns a detailed explanation of exactly what to fix, including the expected .env file location and connection string format. In multi-database mode, it lists every configured database with its connection status. This tool never modifies anything — it is purely informational.
When to use:
At the start of a session to verify the server is connected and healthy.
When any other tool returns a connection error, to diagnose the root cause.
When the user asks about the current server configuration or connected database.
Returns: a JSON object with 'status' (ready|not_configured), 'database' (connection summary), 'mode' (read-only or read-write), and 'readonly' flag. Errors include actionable guidance.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 18 tools
Every tool targets a distinct resource or action: introspective tools (schema, overview, table_info, health) are cleanly separated by granularity, read tools (find_many, find_first, count, aggregate) by return shape and purpose, and write tools (create, upsert, update_many, delete_many) by operation semantics. Even potentially adjacent tools like db_explain and db_raw_query are clearly differentiated. No two tools appear to do the same thing.
The overwhelming majority of tools follow a consistent db_ verb_noun snake_case pattern (db_find_many, db_update_many, db_backup, db_upsert). The only exception is mcp_status, which breaks the prefix convention but is still readable and logically separate (server health vs. database operations).
At 18 tools, this is on the heavier side but each tool serves a legitimate role in a comprehensive PostgreSQL access surface: discovery, monitoring, structured reads, analytics, raw SQL escape hatch, backup, and write operations. The count is justified by the breadth of the domain, though it slightly exceeds the ideal 3-15 sweet spot.
The tool set provides full CRUD coverage (create, read via find_many/find_first, update via update_many, delete via delete_many), plus upsert for idempotent writes. It also covers schema discovery, database switching, health monitoring, query explanation, aggregation, raw SQL escape hatch, and backups—no critical lifecycle dead ends. The intentional exclusion of DDL and raw write tools fits the read-safe design.
Maintenance
Related MCP Connectors
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
Query Postgres, MySQL, SQL Server, Oracle, BigQuery, ClickHouse and Redshift from your AI client.
Deterministic safety, correctness & cost gate that vets Postgres SQL before your AI agent runs it.
Related MCP Servers
- AlicenseAqualityDmaintenancePostgres Pro is an open source Model Context Protocol (MCP) server built to support you and your AI agents throughout the entire development process—from initial coding, through testing and deployment, and to production tuning and maintenance.93,460MIT
- AlicenseAqualityDmaintenanceEnables AI assistants to interact with PostgreSQL databases using natural language queries, providing secure read-only access to database schemas and SQL translation capabilities.68 npmISC
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to safely explore, analyze, and maintain PostgreSQL databases with read-only mode by default, SQL injection prevention, query performance analysis, and optional write operations.87 npmApache 2.0
- AlicenseNot gradedqualityNot gradedmaintenanceProvides AI assistants with safe, controlled access to PostgreSQL databases with read-only defaults, granular permissions, query safety features, and schema introspection capabilities.1-