@yawlabs/postgres-mcp 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -21
- package/README.md +28 -28
- package/dist/index.js +11 -11
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -322,7 +322,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
322
322
|
writes executed by `EXPLAIN ANALYZE`. Previously the write ran inside a
|
|
323
323
|
`BEGIN; ... COMMIT` transaction, so `pg_explain { analyze: true, sql:
|
|
324
324
|
"INSERT ..." }` would actually insert the row. Now writes run inside a
|
|
325
|
-
`BEGIN; ... ROLLBACK` transaction
|
|
325
|
+
`BEGIN; ... ROLLBACK` transaction - the plan (with real row counts and
|
|
326
326
|
timing) comes back but the mutation is rolled back. This matches the user
|
|
327
327
|
expectation when asking for a plan, and the tool description has been
|
|
328
328
|
updated to reflect it.
|
|
@@ -354,7 +354,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
354
354
|
- `pg_list_roles` with `includeSystem: false` (the default) now actually
|
|
355
355
|
excludes built-in `pg_*` roles. The previous `LIKE 'pg\_%' ESCAPE '\\'`
|
|
356
356
|
filter ended up as SQL `ESCAPE '\\'` (two backslashes), which Postgres
|
|
357
|
-
rejects since `ESCAPE` requires a single character
|
|
357
|
+
rejects since `ESCAPE` requires a single character - so the whole filter
|
|
358
358
|
was silently being dropped. Replaced with `starts_with(rolname, 'pg_')`.
|
|
359
359
|
- `pg_describe_table` foreign-key `columns` and `foreign_columns` are now
|
|
360
360
|
proper JSON arrays. They were previously returned as the raw postgres
|
|
@@ -378,11 +378,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
378
378
|
## [0.3.0] - 2026-04-22
|
|
379
379
|
|
|
380
380
|
### Added
|
|
381
|
-
- `pg_list_views`
|
|
382
|
-
- `pg_list_functions`
|
|
383
|
-
- `pg_list_extensions`
|
|
384
|
-
- `pg_search_columns`
|
|
385
|
-
- `pg_top_queries`
|
|
381
|
+
- `pg_list_views` - list views and materialized views with SQL definitions.
|
|
382
|
+
- `pg_list_functions` - list functions, procedures, and aggregates with signatures.
|
|
383
|
+
- `pg_list_extensions` - list installed extensions (pgvector, postgis, etc.) with versions.
|
|
384
|
+
- `pg_search_columns` - find columns by name pattern across all user schemas.
|
|
385
|
+
- `pg_top_queries` - top N queries by total/mean execution time from
|
|
386
386
|
`pg_stat_statements`. Detects extension version and picks the right column
|
|
387
387
|
names (v1.8+ uses `total_exec_time`, older uses `total_time`). Returns clear
|
|
388
388
|
setup instructions if the extension is not installed.
|
|
@@ -404,23 +404,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
404
404
|
tool against a real Postgres instance. Gated on `POSTGRES_MCP_INTEGRATION=1`
|
|
405
405
|
so local `npm test` stays fast with no DB required. CI runs it on Linux via
|
|
406
406
|
a `postgres:16` service container with `pg_stat_statements` preloaded.
|
|
407
|
-
- `pg_inspect_locks`
|
|
407
|
+
- `pg_inspect_locks` - show current blocking locks (blocked PID, blocker PID,
|
|
408
408
|
relation, lock type, both queries). First tool to reach for when a session
|
|
409
409
|
hangs or the app feels stuck.
|
|
410
|
-
- `pg_list_roles`
|
|
410
|
+
- `pg_list_roles` - database roles with login/superuser/createdb/createrole
|
|
411
411
|
flags and inherited group memberships.
|
|
412
|
-
- `pg_table_privileges`
|
|
412
|
+
- `pg_table_privileges` - who has SELECT/INSERT/UPDATE/DELETE/etc. on a table,
|
|
413
413
|
or on all tables in a schema. Useful for pre-migration audits.
|
|
414
|
-
- `pg_seq_scan_tables`
|
|
414
|
+
- `pg_seq_scan_tables` - tables with heavy sequential scans relative to index
|
|
415
415
|
scans. Missing-index candidates.
|
|
416
|
-
- `pg_unused_indexes`
|
|
416
|
+
- `pg_unused_indexes` - non-unique, non-primary indexes with low/zero scan
|
|
417
417
|
counts. Drop candidates (each unused index costs write amplification).
|
|
418
|
-
- `pg_kill`
|
|
418
|
+
- `pg_kill` - cancel a running query or terminate a backend by PID. Requires
|
|
419
419
|
`ALLOW_WRITES=1` since it changes session state. Distinguishes `cancel`
|
|
420
420
|
(SIGINT-equivalent, graceful) from `terminate` (SIGTERM, forceful).
|
|
421
|
-
- `pg_table_bloat`
|
|
421
|
+
- `pg_table_bloat` - estimate dead tuples and vacuum-candidate tables from
|
|
422
422
|
`pg_stat_user_tables`. No extensions required.
|
|
423
|
-
- `pg_replication_status`
|
|
423
|
+
- `pg_replication_status` - replication slots, connected replicas with lag,
|
|
424
424
|
and current WAL position. Returns empty arrays on a standalone DB rather
|
|
425
425
|
than erroring, so it's safe to call unconditionally.
|
|
426
426
|
- New "What can an agent do with this?" README section with concrete example
|
|
@@ -452,12 +452,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
452
452
|
Initial release.
|
|
453
453
|
|
|
454
454
|
### Added
|
|
455
|
-
- `pg_query`
|
|
456
|
-
- `pg_list_schemas`
|
|
457
|
-
- `pg_list_tables`
|
|
458
|
-
- `pg_describe_table`
|
|
459
|
-
- `pg_explain`
|
|
460
|
-
- `pg_health`
|
|
455
|
+
- `pg_query` - run SQL with read-only-by-default safety. Writes opt in via `ALLOW_WRITES=1`.
|
|
456
|
+
- `pg_list_schemas` - list non-system schemas.
|
|
457
|
+
- `pg_list_tables` - list tables (and optionally views) with estimated row counts.
|
|
458
|
+
- `pg_describe_table` - columns, PK, FKs, indexes.
|
|
459
|
+
- `pg_explain` - `EXPLAIN` / `EXPLAIN ANALYZE` with text or JSON output.
|
|
460
|
+
- `pg_health` - server version, db size, connections, active queries, table count.
|
|
461
461
|
- Single-file bundled distribution (zero runtime deps) for fast `npx` cold starts.
|
|
462
462
|
- Result row truncation at `POSTGRES_MAX_ROWS` (default 1000).
|
|
463
463
|
- Parameterized queries via `params` on `pg_query` and `pg_explain`.
|
package/README.md
CHANGED
|
@@ -3,37 +3,37 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@yawlabs/postgres-mcp)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
**Query a PostgreSQL database from Claude Code, Cursor, and any MCP client.** Read-only by default
|
|
6
|
+
**Query a PostgreSQL database from Claude Code, Cursor, and any MCP client.** Read-only by default - writes opt in via a single env var - so an agent can't silently drop your tables.
|
|
7
7
|
|
|
8
8
|
Built and maintained by [Yaw Labs](https://yaw.sh).
|
|
9
9
|
|
|
10
10
|
## Backstory
|
|
11
11
|
|
|
12
|
-
Anthropic's reference Postgres MCP server, `@modelcontextprotocol/server-postgres`, was [archived in May 2025](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/postgres) and [marked deprecated on npm](https://www.npmjs.com/package/@modelcontextprotocol/server-postgres) in July 2025. Anthropic has not shipped a replacement. Despite the deprecation, the last published version (v0.6.2) is still pulled ~20,000 times per week
|
|
12
|
+
Anthropic's reference Postgres MCP server, `@modelcontextprotocol/server-postgres`, was [archived in May 2025](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/postgres) and [marked deprecated on npm](https://www.npmjs.com/package/@modelcontextprotocol/server-postgres) in July 2025. Anthropic has not shipped a replacement. Despite the deprecation, the last published version (v0.6.2) is still pulled ~20,000 times per week - a lot of agents are pointed at an unmaintained package.
|
|
13
13
|
|
|
14
14
|
That unmaintained package also has a known, [publicly documented stacked-query SQL injection](https://securitylabs.datadoghq.com/articles/mcp-vulnerability-case-study-SQL-injection-in-the-postgresql-mcp-server/) (Datadog Security Labs) that bypasses its `BEGIN READ ONLY` wrapper with input like `COMMIT; DROP SCHEMA public CASCADE;`. It has never been patched at npm.
|
|
15
15
|
|
|
16
16
|
A handful of community forks have appeared, but each fills a narrow slice:
|
|
17
17
|
|
|
18
|
-
- [`@zeddotdev/postgres-context-server`](https://www.npmjs.com/package/@zeddotdev/postgres-context-server)
|
|
19
|
-
- **Postgres MCP Pro** (Crystal DBA)
|
|
20
|
-
- **AWS Labs Postgres MCP**
|
|
18
|
+
- [`@zeddotdev/postgres-context-server`](https://www.npmjs.com/package/@zeddotdev/postgres-context-server) - Zed's fork, primarily a security patch on the original shape.
|
|
19
|
+
- **Postgres MCP Pro** (Crystal DBA) - focused on index tuning and hypothetical-index / buffer-cache diagnostics.
|
|
20
|
+
- **AWS Labs Postgres MCP** - tied to Aurora / RDS Data API + Secrets Manager.
|
|
21
21
|
|
|
22
22
|
None of them position themselves as a general-purpose daily driver you'd hand to Claude Code or Cursor against an arbitrary Postgres: modern introspection, perf helpers, role/privilege awareness, and a write-safety posture out of the box. That's the gap `@yawlabs/postgres-mcp` fills.
|
|
23
23
|
|
|
24
24
|
## Why this one?
|
|
25
25
|
|
|
26
|
-
- **Read-only by default**
|
|
27
|
-
- **Extended query protocol for all user SQL**
|
|
28
|
-
- **Parameterized queries**
|
|
29
|
-
- **Written from scratch, actively maintained**
|
|
30
|
-
- **Schema introspection built in**
|
|
31
|
-
- **`EXPLAIN` as a first-class tool**
|
|
32
|
-
- **Perf diagnostics the deprecated server never had**
|
|
33
|
-
- **Health snapshot**
|
|
34
|
-
- **Role and privilege awareness**
|
|
35
|
-
- **Instant startup**
|
|
36
|
-
- **Result truncation**
|
|
26
|
+
- **Read-only by default** - user SQL runs in a `BEGIN READ ONLY` transaction, so postgres itself (not string parsing) blocks writes. Opt in with `ALLOW_WRITES=1`.
|
|
27
|
+
- **Extended query protocol for all user SQL** - `pg_query` sends user input with `queryMode: 'extended'`, which restricts each request to a single statement. This closes the [stacked-query injection class](https://securitylabs.datadoghq.com/articles/mcp-vulnerability-case-study-SQL-injection-in-the-postgresql-mcp-server/) (`COMMIT; DROP SCHEMA x CASCADE;`) that defeated the reference server's `BEGIN READ ONLY` wrapper. Integration test asserts the rejection.
|
|
28
|
+
- **Parameterized queries** - `pg_query` takes a `params` array for `$1`, `$2`, etc. No string-interpolated SQL in our code path.
|
|
29
|
+
- **Written from scratch, actively maintained** - not a fork of the deprecated code. Unit + integration tests (`npm test`, `npm run test:integration`) run against a real Postgres; releases cut via `release.sh`.
|
|
30
|
+
- **Schema introspection built in** - `pg_list_schemas`, `pg_list_tables`, `pg_describe_table` return columns, primary keys, foreign keys, and indexes without the agent having to remember `pg_catalog` joins.
|
|
31
|
+
- **`EXPLAIN` as a first-class tool** - text or JSON format, with optional `ANALYZE`. ANALYZE for non-SELECT statements requires `ALLOW_WRITES=1` and always rolls back, so the plan is real but the write doesn't persist.
|
|
32
|
+
- **Perf diagnostics the deprecated server never had** - `pg_top_queries` (from `pg_stat_statements`), `pg_seq_scan_tables`, `pg_unused_indexes`, `pg_table_bloat`, `pg_inspect_locks`, `pg_replication_status`. Answer "why is this slow?" in one tool call.
|
|
33
|
+
- **Health snapshot** - `pg_health` returns version, db size, connection counts, and the 10 longest-running active queries in one call.
|
|
34
|
+
- **Role and privilege awareness** - `pg_list_roles` and `pg_table_privileges` for the common "who can touch what?" questions.
|
|
35
|
+
- **Instant startup** - ships as a single bundled file with zero runtime dependencies. No multi-minute `node_modules` install on every `npx` cold start.
|
|
36
|
+
- **Result truncation** - large result sets are capped at `POSTGRES_MAX_ROWS` (default 1000) with a `truncated: true` flag, so a stray `SELECT * FROM events` doesn't blow out the model context.
|
|
37
37
|
|
|
38
38
|
## Quick start
|
|
39
39
|
|
|
@@ -88,7 +88,7 @@ Read-only is the default. If you want the agent to be able to `INSERT`, `UPDATE`
|
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
Prefer scoping this to dev/test databases
|
|
91
|
+
Prefer scoping this to dev/test databases - for production, leave writes off and use migration tools out-of-band.
|
|
92
92
|
|
|
93
93
|
## What can an agent do with this?
|
|
94
94
|
|
|
@@ -103,7 +103,7 @@ Once connected, the agent picks tools automatically based on what you ask. A few
|
|
|
103
103
|
|
|
104
104
|
The bigger leverage is multi-tool reasoning. A few real workflows:
|
|
105
105
|
|
|
106
|
-
- **Unstick a hung app.** `pg_inspect_locks` returns blocked PID + blocking PID + the offending query, then `pg_kill` (`ALLOW_WRITES=1` required) cancels the blocker. The agent can run both in one turn
|
|
106
|
+
- **Unstick a hung app.** `pg_inspect_locks` returns blocked PID + blocking PID + the offending query, then `pg_kill` (`ALLOW_WRITES=1` required) cancels the blocker. The agent can run both in one turn - it's the fastest path from "the app is frozen" to "back up."
|
|
107
107
|
- **Chase a slow page.** `pg_top_queries` ranks the worst queries, `pg_explain` with `analyze: true` shows the plan for the top hit, `pg_seq_scan_tables` and `pg_unused_indexes` say whether the answer is "add an index here" or "drop a dead one there."
|
|
108
108
|
- **Oncall triage.** `pg_health` checks connectivity + active-query count + database size; `pg_inspect_locks` and `pg_replication_status` confirm whether contention or replication lag is in play before paging the on-call DBA.
|
|
109
109
|
|
|
@@ -122,12 +122,12 @@ The bigger leverage is multi-tool reasoning. A few real workflows:
|
|
|
122
122
|
| `pg_explain` | `EXPLAIN` or `EXPLAIN ANALYZE` for a SQL statement. Text or JSON output. Optional `hypothetical_indexes` (requires the [HypoPG](https://github.com/HypoPG/hypopg) extension) lets you ask "what would the plan be with these indexes?" without creating them on disk. |
|
|
123
123
|
| `pg_health` | Server version, database size, connection count, active queries, table count. |
|
|
124
124
|
| `pg_top_queries` | Top N queries by total/mean execution time. Requires the `pg_stat_statements` extension. |
|
|
125
|
-
| `pg_seq_scan_tables` | Tables with heavy sequential scans
|
|
126
|
-
| `pg_unused_indexes` | Non-unique, non-primary indexes with low scan counts
|
|
125
|
+
| `pg_seq_scan_tables` | Tables with heavy sequential scans - missing-index candidates. |
|
|
126
|
+
| `pg_unused_indexes` | Non-unique, non-primary indexes with low scan counts - drop candidates. |
|
|
127
127
|
| `pg_inspect_locks` | Who is blocking whom right now (blocked PID, blocker PID, lock type, queries). |
|
|
128
128
|
| `pg_list_roles` | Database roles with login/superuser/createdb flags and group memberships. |
|
|
129
129
|
| `pg_table_privileges` | Who has SELECT/INSERT/UPDATE/DELETE/etc. on a table or whole schema. |
|
|
130
|
-
| `pg_table_bloat` | Tables with high dead-tuple ratios
|
|
130
|
+
| `pg_table_bloat` | Tables with high dead-tuple ratios - VACUUM candidates. |
|
|
131
131
|
| `pg_replication_status` | Replication slots, connected replicas, and current WAL position. |
|
|
132
132
|
| `pg_advisor` | Rolled-up DBA lints in one call: sequence-exhaustion candidates, tables without a primary key, and (configurable) public tables with RLS disabled. The "what should I be looking at?" starting point. |
|
|
133
133
|
| `pg_kill` | Cancel a running query or terminate a backend connection. Requires `ALLOW_WRITES=1`. |
|
|
@@ -171,19 +171,19 @@ This disables certificate chain verification only -- the TCP connection is still
|
|
|
171
171
|
|
|
172
172
|
## Troubleshooting
|
|
173
173
|
|
|
174
|
-
**`DATABASE_URL is not set`**
|
|
174
|
+
**`DATABASE_URL is not set`** - Your MCP client is launching the server without the env var. On Windows especially, env vars set in bash / PowerShell profiles are not inherited by MCP servers launched via `cmd`. Put `DATABASE_URL` directly in the `env` block of `.mcp.json`.
|
|
175
175
|
|
|
176
|
-
**`password authentication failed`**
|
|
176
|
+
**`password authentication failed`** - Check the username, password, and that the user has `CONNECT` privilege on the database. URL-encode special characters in the password (`@` → `%40`, `#` → `%23`, `/` → `%2F`).
|
|
177
177
|
|
|
178
|
-
**`SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string`**
|
|
178
|
+
**`SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string`** - The password in your connection string is empty or became `null` after URL decoding. Re-check your connection string.
|
|
179
179
|
|
|
180
|
-
**`canceling statement due to statement timeout`**
|
|
180
|
+
**`canceling statement due to statement timeout`** - A single query exceeded `POSTGRES_STATEMENT_TIMEOUT_MS` (default 30s). Increase it, narrow the query with `WHERE`, or add an index. This is working as designed -- the timeout exists so a runaway query cannot hang the agent.
|
|
181
181
|
|
|
182
|
-
**`Write blocked: this server is in read-only mode`**
|
|
182
|
+
**`Write blocked: this server is in read-only mode`** - You asked the agent to write but `ALLOW_WRITES` is not set. Add `ALLOW_WRITES=1` to the `env` block of `.mcp.json` and restart your MCP client. Only do this for dev/test DBs.
|
|
183
183
|
|
|
184
|
-
**Connection pool exhaustion with PgBouncer transaction mode or pglite-socket**
|
|
184
|
+
**Connection pool exhaustion with PgBouncer transaction mode or pglite-socket** - These backends don't support concurrent queries on a single connection. Set `POSTGRES_POOL_MAX=1` in the env block.
|
|
185
185
|
|
|
186
|
-
**First query is slow, subsequent queries are fast**
|
|
186
|
+
**First query is slow, subsequent queries are fast** - Expected. The pg driver lazily establishes the first connection; subsequent queries reuse the pool.
|
|
187
187
|
|
|
188
188
|
## Development
|
|
189
189
|
|
package/dist/index.js
CHANGED
|
@@ -36350,7 +36350,7 @@ async function shutdown() {
|
|
|
36350
36350
|
var adminTools = [
|
|
36351
36351
|
{
|
|
36352
36352
|
name: "pg_inspect_locks",
|
|
36353
|
-
description: "Show current lock contention: which sessions are blocked and who is blocking them. Returns blocked PID, blocking PID, lock types, relation being contested, and the queries involved. Use this first when a tool call hangs or the app feels stuck
|
|
36353
|
+
description: "Show current lock contention: which sessions are blocked and who is blocking them. Returns blocked PID, blocking PID, lock types, relation being contested, and the queries involved. Use this first when a tool call hangs or the app feels stuck - it's the fastest way to identify a long-held transaction holding a lock.",
|
|
36354
36354
|
annotations: {
|
|
36355
36355
|
title: "Inspect blocking locks",
|
|
36356
36356
|
readOnlyHint: true,
|
|
@@ -36469,7 +36469,7 @@ var adminTools = [
|
|
|
36469
36469
|
},
|
|
36470
36470
|
{
|
|
36471
36471
|
name: "pg_kill",
|
|
36472
|
-
description: "Cancel a running query (SIGINT-equivalent) or terminate a backend connection (SIGTERM-equivalent) by PID. Find the PID via `pg_health` active_queries or `pg_inspect_locks`. Requires ALLOW_WRITES=1 since this changes database session state. The role in DATABASE_URL must have permission
|
|
36472
|
+
description: "Cancel a running query (SIGINT-equivalent) or terminate a backend connection (SIGTERM-equivalent) by PID. Find the PID via `pg_health` active_queries or `pg_inspect_locks`. Requires ALLOW_WRITES=1 since this changes database session state. The role in DATABASE_URL must have permission - cancelling another user's query needs the `pg_signal_backend` role or superuser. Cancel is graceful; terminate is forceful.",
|
|
36473
36473
|
annotations: {
|
|
36474
36474
|
title: "Cancel or terminate a backend",
|
|
36475
36475
|
readOnlyHint: false,
|
|
@@ -36499,7 +36499,7 @@ var adminTools = [
|
|
|
36499
36499
|
pid,
|
|
36500
36500
|
mode,
|
|
36501
36501
|
signaled,
|
|
36502
|
-
note: signaled ? `Sent ${mode === "terminate" ? "SIGTERM" : "SIGINT"} to backend ${pid}.` : `Signal returned false
|
|
36502
|
+
note: signaled ? `Sent ${mode === "terminate" ? "SIGTERM" : "SIGINT"} to backend ${pid}.` : `Signal returned false - PID ${pid} may not exist, may already be gone, or the current role lacks permission.`
|
|
36503
36503
|
}
|
|
36504
36504
|
};
|
|
36505
36505
|
}
|
|
@@ -36654,7 +36654,7 @@ var adminTools = [
|
|
|
36654
36654
|
},
|
|
36655
36655
|
{
|
|
36656
36656
|
name: "pg_table_bloat",
|
|
36657
|
-
description: "Estimate table bloat (dead tuples + free space) for tables in a schema. Returns live tuples, dead tuples, dead-tuple ratio, last_vacuum / last_autovacuum timestamps, and total relation size. A high dead_ratio with a stale last_autovacuum is a sign a table needs VACUUM. Cheap
|
|
36657
|
+
description: "Estimate table bloat (dead tuples + free space) for tables in a schema. Returns live tuples, dead tuples, dead-tuple ratio, last_vacuum / last_autovacuum timestamps, and total relation size. A high dead_ratio with a stale last_autovacuum is a sign a table needs VACUUM. Cheap - uses `pg_stat_user_tables`, no extensions required.",
|
|
36658
36658
|
annotations: {
|
|
36659
36659
|
title: "Estimate table bloat",
|
|
36660
36660
|
readOnlyHint: true,
|
|
@@ -36664,7 +36664,7 @@ var adminTools = [
|
|
|
36664
36664
|
},
|
|
36665
36665
|
inputSchema: external_exports.object({
|
|
36666
36666
|
schema: external_exports.string().min(1).max(63).optional().describe("Limit to one schema. If omitted, all user schemas are included."),
|
|
36667
|
-
minDeadRatio: external_exports.number().min(0).max(1).default(0.1).describe("Minimum dead-tuple fraction to include
|
|
36667
|
+
minDeadRatio: external_exports.number().min(0).max(1).default(0.1).describe("Minimum dead-tuple fraction to include - dead / (live + dead). Default 0.1 = 10%."),
|
|
36668
36668
|
limit: external_exports.number().int().min(1).max(200).default(50).describe("Max rows to return (default 50).")
|
|
36669
36669
|
}),
|
|
36670
36670
|
handler: async (input) => {
|
|
@@ -36755,7 +36755,7 @@ function buildHypopgHooks(indexes) {
|
|
|
36755
36755
|
var explainTools = [
|
|
36756
36756
|
{
|
|
36757
36757
|
name: "pg_explain",
|
|
36758
|
-
description: "Get the query plan for a SQL statement. By default, this uses plain EXPLAIN (no execution). Set `analyze: true` to run the query with EXPLAIN ANALYZE
|
|
36758
|
+
description: "Get the query plan for a SQL statement. By default, this uses plain EXPLAIN (no execution). Set `analyze: true` to run the query with EXPLAIN ANALYZE - for non-SELECT statements, ALLOW_WRITES=1 is required (since ANALYZE actually executes the statement). Writes executed during EXPLAIN ANALYZE are always rolled back, so you can inspect a plan for an INSERT/UPDATE/DELETE without persisting the mutation. Format is `text` (default) or `json`. Pass the raw SQL (not an EXPLAIN-prefixed statement). Set `hypothetical_indexes` to a list of `{table, columns, using?}` to ask the planner 'what would the plan be if these indexes existed?' -- requires the HypoPG extension (`CREATE EXTENSION hypopg`). The hypothetical indexes are torn down at the end of the call, never touching real disk.",
|
|
36759
36759
|
annotations: {
|
|
36760
36760
|
title: "Explain query plan",
|
|
36761
36761
|
readOnlyHint: false,
|
|
@@ -36800,7 +36800,7 @@ var explainTools = [
|
|
|
36800
36800
|
if (!check2.data?.[0]?.installed) {
|
|
36801
36801
|
return {
|
|
36802
36802
|
ok: false,
|
|
36803
|
-
error: "hypothetical_indexes requires the HypoPG extension. Install with `CREATE EXTENSION hypopg;` (a superuser-equivalent role usually). HypoPG is read-only at the disk level
|
|
36803
|
+
error: "hypothetical_indexes requires the HypoPG extension. Install with `CREATE EXTENSION hypopg;` (a superuser-equivalent role usually). HypoPG is read-only at the disk level - it lives entirely in shared memory."
|
|
36804
36804
|
};
|
|
36805
36805
|
}
|
|
36806
36806
|
}
|
|
@@ -36958,7 +36958,7 @@ var schemaTools = [
|
|
|
36958
36958
|
},
|
|
36959
36959
|
{
|
|
36960
36960
|
name: "pg_list_tables",
|
|
36961
|
-
description: "List tables (and optionally views) in a schema. Returns name, type (table/view/materialized view/foreign), and estimated row count (from `reltuples`; approximate
|
|
36961
|
+
description: "List tables (and optionally views) in a schema. Returns name, type (table/view/materialized view/foreign), and estimated row count (from `reltuples`; approximate - 0 until ANALYZE runs). Paginate via `limit`/`offset` on very large schemas.",
|
|
36962
36962
|
annotations: {
|
|
36963
36963
|
title: "List tables in a schema",
|
|
36964
36964
|
readOnlyHint: true,
|
|
@@ -37413,7 +37413,7 @@ var statsTools = [
|
|
|
37413
37413
|
},
|
|
37414
37414
|
{
|
|
37415
37415
|
name: "pg_seq_scan_tables",
|
|
37416
|
-
description: "Tables with high sequential-scan counts relative to index scans
|
|
37416
|
+
description: "Tables with high sequential-scan counts relative to index scans - the first place to look for missing-index candidates. Returns seq_scans, idx_scans, live tuples, and the ratio. A high ratio on a large table usually means a query is reading the whole table where an index would suffice. Pair with `pg_top_queries` to find which query is doing it.",
|
|
37417
37417
|
annotations: {
|
|
37418
37418
|
title: "Find tables with heavy sequential scans",
|
|
37419
37419
|
readOnlyHint: true,
|
|
@@ -37454,7 +37454,7 @@ var statsTools = [
|
|
|
37454
37454
|
},
|
|
37455
37455
|
{
|
|
37456
37456
|
name: "pg_unused_indexes",
|
|
37457
|
-
description: "Indexes that have never been scanned or have very low usage. Each unused index costs write amplification (every INSERT/UPDATE maintains it) and disk space. Excludes primary keys and unique constraints (which are load-bearing even with zero scans). Use this before adding new indexes
|
|
37457
|
+
description: "Indexes that have never been scanned or have very low usage. Each unused index costs write amplification (every INSERT/UPDATE maintains it) and disk space. Excludes primary keys and unique constraints (which are load-bearing even with zero scans). Use this before adding new indexes - sometimes the fix is to drop a dead one.",
|
|
37458
37458
|
annotations: {
|
|
37459
37459
|
title: "Find unused indexes",
|
|
37460
37460
|
readOnlyHint: true,
|
|
@@ -37510,7 +37510,7 @@ function compareVersions(a, b) {
|
|
|
37510
37510
|
}
|
|
37511
37511
|
|
|
37512
37512
|
// src/index.ts
|
|
37513
|
-
var version2 = true ? "0.5.
|
|
37513
|
+
var version2 = true ? "0.5.4" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
37514
37514
|
var subcommand = process.argv[2];
|
|
37515
37515
|
if (subcommand === "version" || subcommand === "--version") {
|
|
37516
37516
|
console.log(version2);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yawlabs/postgres-mcp",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "PostgreSQL MCP server
|
|
3
|
+
"version": "0.5.4",
|
|
4
|
+
"description": "PostgreSQL MCP server - query, schema introspection, explain, and health checks for AI assistants",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "YawLabs <contact@yaw.sh>",
|
|
7
7
|
"repository": {
|