@yawlabs/postgres-mcp 0.5.2 → 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.
Files changed (4) hide show
  1. package/CHANGELOG.md +114 -21
  2. package/README.md +28 -28
  3. package/dist/index.js +1566 -731
  4. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,99 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.3] - 2026-05-14
11
+
12
+ ### Security
13
+ - Transitive deps patched via `npm audit fix`: in-range bumps to `hono`,
14
+ `fast-uri`, `express-rate-limit`, and `ip-address`. All four reach us
15
+ only through `@modelcontextprotocol/sdk`'s HTTP-transport path, which
16
+ this server doesn't use (stdio only) and which esbuild tree-shakes out
17
+ of `dist/index.js`. Practical exposure was already nil; this clears the
18
+ audit noise on future `npm install` runs.
19
+
20
+ ### Infrastructure
21
+ - `release.yml` smoke test now retries the `npx -y --version` call itself
22
+ (6 x 10s) instead of probing `npm view` once and trusting the result.
23
+ Registry propagation has two desynchronized CDN cache layers --
24
+ `npm view` (metadata) and `npx -y` (tarball) can land on different
25
+ edges and return inconsistent results. Observed on the v0.5.2 publish
26
+ (2026-05-14): `npm view` returned 0.5.2 immediately but the subsequent
27
+ `npx -y` got `ETARGET` and turned the Release workflow red despite a
28
+ successful publish. The retry now covers both layers.
29
+
30
+ ### Changed
31
+ - Dev dependencies bumped via Dependabot (`@biomejs/biome`, `zod`, plus
32
+ transitives) -- in-range, dev-only.
33
+
34
+ ## [0.5.2] - 2026-05-14
35
+
36
+ ### Fixed
37
+ - `pg_describe_table` now emits a `kind fetch failed, reported as "table"`
38
+ warning instead of silently defaulting to `"table"` when the kind query
39
+ fails. The other partial-failure fields already followed this pattern; the
40
+ `kind` field is reported at the top level so a silent default could
41
+ mislabel a view or materialized view as a regular table.
42
+ - `pg_advisor` `sequence_exhaustion` divides `last_value / max_value` in
43
+ `numeric` rather than `float8`. BIGINT sequences past 2^53 lose precision
44
+ in `float8`, and the danger zone (>= threshold) is exactly where the
45
+ reported `pct_used` needs to stay accurate. The WHERE filter and the
46
+ reported value both switched.
47
+ - `pg_explain` `hypothetical_indexes` handler defaults `using` to `"btree"`
48
+ defensively. The Zod schema already applies this default on the protocol
49
+ path, but a unit-test-style direct handler call bypasses Zod -- so a
50
+ missing `using` would render as `USING undefined` in the generated SQL and
51
+ hypopg_create_index would surface a confusing syntax error. New
52
+ integration regression test covers the omitted-`using` path against a real
53
+ HypoPG-installed database.
54
+
55
+ ### Changed
56
+ - `api.ts` `getPool()` docstring spells out the env-var snapshot semantics --
57
+ which vars are bake-on-first-call (`DATABASE_URL`,
58
+ `POSTGRES_STATEMENT_TIMEOUT_MS`, `POSTGRES_CONNECTION_TIMEOUT_MS`,
59
+ `POSTGRES_POOL_MAX`, `POSTGRES_SSL_REJECT_UNAUTHORIZED`) and which are
60
+ read per-request (`POSTGRES_MAX_ROWS`, `ALLOW_WRITES`). Previously a
61
+ hidden assumption.
62
+ - `api.ts` `typeNameCache` comment now explains the OID-wraparound staleness
63
+ bound: a `DROP TYPE` / `CREATE TYPE` in-session gets a new OID and the
64
+ miss-fill path picks it up; the dead entry under the old OID is wasted
65
+ memory, not a correctness bug.
66
+ - `scripts/wsl-pg-setup.sh` now carries DEV/CI ONLY warnings on the PG16
67
+ purge and on the `0.0.0.0/0 md5` pg_hba stanza. Both are safe inside WSL
68
+ but lethal as a production template -- the warnings prevent silent
69
+ copy-paste into a real host config.
70
+ - `release.sh` Verify step uses the same 5x5s retry loop as the CI
71
+ smoke test, instead of a one-shot `sleep 3 && npm view` that flaked on a
72
+ slow registry.
73
+
74
+ ### Infrastructure
75
+ - `release.yml` concurrency group locked to the literal string
76
+ `release-npm` instead of an interpolated `${{ github.workflow }}` /
77
+ `${{ github.ref }}`. The interpolated form fragmented across different
78
+ tags (each ref-name got its own queue, defeating the serialization the
79
+ group was meant to provide) and could silently re-fragment on a workflow
80
+ rename. The literal key serializes all release runs into one queue.
81
+ - `release.yml` integration build step de-duplicated -- the redundant
82
+ pre-publish `npm run build` was rebuilding the same artifact the
83
+ `prepublishOnly` hook would build moments later. Removed.
84
+ - `release.sh` now creates annotated tags (`git tag -a`) and the workflow
85
+ uses `git push --follow-tags`; lightweight tags are silently skipped by
86
+ `--follow-tags`, which previously left `release.yml` un-triggered on
87
+ manual tags.
88
+
89
+ ## [0.5.1] - 2026-05-05
90
+
91
+ ### Fixed
92
+ - Type-name lookup failures no longer drop the user's successful query
93
+ rows. `runReadOnly` / `runReadWrite` / `runReadWriteRollback` now route
94
+ catalog lookups through a `safeResolveTypeNames` wrapper that logs to
95
+ stderr and falls back to `{}` on a transient `pg_type` error, instead
96
+ of letting the catalog failure throw past the user's already-successful
97
+ result.
98
+ - `release.sh` WSL matrix step uses a sed regex to translate Git Bash
99
+ drive prefixes (`/c/`, `/d/`, ...) into the WSL form (`/mnt/c/`,
100
+ `/mnt/d/`, ...). The previous hardcoded `/c/` broke contributors
101
+ working from any other drive.
102
+
10
103
  ## [0.5.0] - 2026-05-04
11
104
 
12
105
  ### Changed
@@ -229,7 +322,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
229
322
  writes executed by `EXPLAIN ANALYZE`. Previously the write ran inside a
230
323
  `BEGIN; ... COMMIT` transaction, so `pg_explain { analyze: true, sql:
231
324
  "INSERT ..." }` would actually insert the row. Now writes run inside a
232
- `BEGIN; ... ROLLBACK` transaction the plan (with real row counts and
325
+ `BEGIN; ... ROLLBACK` transaction - the plan (with real row counts and
233
326
  timing) comes back but the mutation is rolled back. This matches the user
234
327
  expectation when asking for a plan, and the tool description has been
235
328
  updated to reflect it.
@@ -261,7 +354,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
261
354
  - `pg_list_roles` with `includeSystem: false` (the default) now actually
262
355
  excludes built-in `pg_*` roles. The previous `LIKE 'pg\_%' ESCAPE '\\'`
263
356
  filter ended up as SQL `ESCAPE '\\'` (two backslashes), which Postgres
264
- rejects since `ESCAPE` requires a single character so the whole filter
357
+ rejects since `ESCAPE` requires a single character - so the whole filter
265
358
  was silently being dropped. Replaced with `starts_with(rolname, 'pg_')`.
266
359
  - `pg_describe_table` foreign-key `columns` and `foreign_columns` are now
267
360
  proper JSON arrays. They were previously returned as the raw postgres
@@ -285,11 +378,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
285
378
  ## [0.3.0] - 2026-04-22
286
379
 
287
380
  ### Added
288
- - `pg_list_views` list views and materialized views with SQL definitions.
289
- - `pg_list_functions` list functions, procedures, and aggregates with signatures.
290
- - `pg_list_extensions` list installed extensions (pgvector, postgis, etc.) with versions.
291
- - `pg_search_columns` find columns by name pattern across all user schemas.
292
- - `pg_top_queries` top N queries by total/mean execution time from
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
293
386
  `pg_stat_statements`. Detects extension version and picks the right column
294
387
  names (v1.8+ uses `total_exec_time`, older uses `total_time`). Returns clear
295
388
  setup instructions if the extension is not installed.
@@ -311,23 +404,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
311
404
  tool against a real Postgres instance. Gated on `POSTGRES_MCP_INTEGRATION=1`
312
405
  so local `npm test` stays fast with no DB required. CI runs it on Linux via
313
406
  a `postgres:16` service container with `pg_stat_statements` preloaded.
314
- - `pg_inspect_locks` show current blocking locks (blocked PID, blocker PID,
407
+ - `pg_inspect_locks` - show current blocking locks (blocked PID, blocker PID,
315
408
  relation, lock type, both queries). First tool to reach for when a session
316
409
  hangs or the app feels stuck.
317
- - `pg_list_roles` database roles with login/superuser/createdb/createrole
410
+ - `pg_list_roles` - database roles with login/superuser/createdb/createrole
318
411
  flags and inherited group memberships.
319
- - `pg_table_privileges` who has SELECT/INSERT/UPDATE/DELETE/etc. on a table,
412
+ - `pg_table_privileges` - who has SELECT/INSERT/UPDATE/DELETE/etc. on a table,
320
413
  or on all tables in a schema. Useful for pre-migration audits.
321
- - `pg_seq_scan_tables` tables with heavy sequential scans relative to index
414
+ - `pg_seq_scan_tables` - tables with heavy sequential scans relative to index
322
415
  scans. Missing-index candidates.
323
- - `pg_unused_indexes` non-unique, non-primary indexes with low/zero scan
416
+ - `pg_unused_indexes` - non-unique, non-primary indexes with low/zero scan
324
417
  counts. Drop candidates (each unused index costs write amplification).
325
- - `pg_kill` cancel a running query or terminate a backend by PID. Requires
418
+ - `pg_kill` - cancel a running query or terminate a backend by PID. Requires
326
419
  `ALLOW_WRITES=1` since it changes session state. Distinguishes `cancel`
327
420
  (SIGINT-equivalent, graceful) from `terminate` (SIGTERM, forceful).
328
- - `pg_table_bloat` estimate dead tuples and vacuum-candidate tables from
421
+ - `pg_table_bloat` - estimate dead tuples and vacuum-candidate tables from
329
422
  `pg_stat_user_tables`. No extensions required.
330
- - `pg_replication_status` replication slots, connected replicas with lag,
423
+ - `pg_replication_status` - replication slots, connected replicas with lag,
331
424
  and current WAL position. Returns empty arrays on a standalone DB rather
332
425
  than erroring, so it's safe to call unconditionally.
333
426
  - New "What can an agent do with this?" README section with concrete example
@@ -359,12 +452,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
359
452
  Initial release.
360
453
 
361
454
  ### Added
362
- - `pg_query` run SQL with read-only-by-default safety. Writes opt in via `ALLOW_WRITES=1`.
363
- - `pg_list_schemas` list non-system schemas.
364
- - `pg_list_tables` list tables (and optionally views) with estimated row counts.
365
- - `pg_describe_table` columns, PK, FKs, indexes.
366
- - `pg_explain` `EXPLAIN` / `EXPLAIN ANALYZE` with text or JSON output.
367
- - `pg_health` server version, db size, connections, active queries, table count.
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.
368
461
  - Single-file bundled distribution (zero runtime deps) for fast `npx` cold starts.
369
462
  - Result row truncation at `POSTGRES_MAX_ROWS` (default 1000).
370
463
  - Parameterized queries via `params` on `pg_query` and `pg_explain`.
package/README.md CHANGED
@@ -3,37 +3,37 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@yawlabs/postgres-mcp)](https://www.npmjs.com/package/@yawlabs/postgres-mcp)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
 
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.
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 a lot of agents are pointed at an unmaintained package.
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) 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.
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** 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.
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 for production, leave writes off and use migration tools out-of-band.
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 it's the fastest path from "the app is frozen" to "back up."
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 missing-index candidates. |
126
- | `pg_unused_indexes` | Non-unique, non-primary indexes with low scan counts drop candidates. |
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 VACUUM candidates. |
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`** 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`.
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`** Check the username, password, and that the user has `CONNECT` privilege on the database. URL-encode special characters in the password (`@` → `%40`, `#` → `%23`, `/` → `%2F`).
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`** The password in your connection string is empty or became `null` after URL decoding. Re-check your connection 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`** 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.
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`** 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.
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** These backends don't support concurrent queries on a single connection. Set `POSTGRES_POOL_MAX=1` in the env block.
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** Expected. The pg driver lazily establishes the first connection; subsequent queries reuse the pool.
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