@yawlabs/postgres-mcp 0.3.2 → 0.3.3

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/README.md CHANGED
@@ -2,23 +2,38 @@
2
2
 
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
- [![CI](https://github.com/YawLabs/postgres-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/YawLabs/postgres-mcp/actions/workflows/ci.yml) [![Release](https://github.com/YawLabs/postgres-mcp/actions/workflows/release.yml/badge.svg)](https://github.com/YawLabs/postgres-mcp/actions/workflows/release.yml)
6
5
 
7
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.
8
7
 
9
8
  Built and maintained by [Yaw Labs](https://yaw.sh).
10
9
 
11
- ## Why this one?
10
+ ## Backstory
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.
13
+
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
+
16
+ A handful of community forks have appeared, but each fills a narrow slice:
12
17
 
13
- The official Anthropic `@modelcontextprotocol/server-postgres` was [deprecated on npm](https://www.npmjs.com/package/@modelcontextprotocol/server-postgres) in early 2026. This one picks up where it left off, with a few extras:
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
+
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
+
24
+ ## Why this one?
14
25
 
15
- - **Read-only by default** — user SQL runs in a `BEGIN READ ONLY` transaction, so postgres itself (not string parsing) blocks writes. Opt in to writes with `ALLOW_WRITES=1`.
16
- - **Schema introspection built in** — `pg_list_schemas`, `pg_list_tables`, `pg_describe_table` return columns, primary keys, foreign keys, and indexes without you having to remember the `pg_catalog` joins.
17
- - **`EXPLAIN` as a first-class tool** — text or JSON format, with optional `ANALYZE`. ANALYZE for non-SELECT statements requires `ALLOW_WRITES=1` since it actually runs the query.
18
- - **Health snapshot** — `pg_health` returns version, db size, connection counts, and the 10 longest-running active queries in one call. Use it as a connection smoke-test and to catch runaway queries.
19
- - **Instant startup** — ships as a single bundled file with zero runtime dependencies. No multi-minute `node_modules` install on every npx cold start.
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.
20
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.
21
- - **Parameterized queries** — `pg_query` accepts a `params` array for `$1`, `$2`, etc. No string-interpolated SQL.
22
37
 
23
38
  ## Quick start
24
39
 
package/dist/index.js CHANGED
@@ -35324,7 +35324,7 @@ async function runReadOnly(sql, params = []) {
35324
35324
  const maxRows = getMaxRows();
35325
35325
  try {
35326
35326
  await client.query("BEGIN READ ONLY");
35327
- const result = await client.query(sql, params);
35327
+ const result = await client.query({ text: sql, values: params, queryMode: "extended" });
35328
35328
  await client.query("ROLLBACK");
35329
35329
  return { ok: true, data: toQueryResult(result, maxRows) };
35330
35330
  } catch (err) {
@@ -35348,7 +35348,7 @@ async function runReadWrite(sql, params = []) {
35348
35348
  const maxRows = getMaxRows();
35349
35349
  try {
35350
35350
  await client.query("BEGIN");
35351
- const result = await client.query(sql, params);
35351
+ const result = await client.query({ text: sql, values: params, queryMode: "extended" });
35352
35352
  await client.query("COMMIT");
35353
35353
  return { ok: true, data: toQueryResult(result, maxRows) };
35354
35354
  } catch (err) {
@@ -35372,7 +35372,7 @@ async function runReadWriteRollback(sql, params = []) {
35372
35372
  const maxRows = getMaxRows();
35373
35373
  try {
35374
35374
  await client.query("BEGIN");
35375
- const result = await client.query(sql, params);
35375
+ const result = await client.query({ text: sql, values: params, queryMode: "extended" });
35376
35376
  await client.query("ROLLBACK");
35377
35377
  return { ok: true, data: toQueryResult(result, maxRows) };
35378
35378
  } catch (err) {
@@ -36283,7 +36283,7 @@ function compareVersions(a, b) {
36283
36283
  }
36284
36284
 
36285
36285
  // src/index.ts
36286
- var version2 = true ? "0.3.2" : (await null).createRequire(import.meta.url)("../package.json").version;
36286
+ var version2 = true ? "0.3.3" : (await null).createRequire(import.meta.url)("../package.json").version;
36287
36287
  var subcommand = process.argv[2];
36288
36288
  if (subcommand === "version" || subcommand === "--version") {
36289
36289
  console.log(version2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/postgres-mcp",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
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>",
@@ -38,12 +38,11 @@
38
38
  "lint:fix": "biome check --write src/",
39
39
  "prepublishOnly": "npm run build"
40
40
  },
41
- "dependencies": {},
42
41
  "devDependencies": {
43
42
  "@biomejs/biome": "^2.4.12",
44
43
  "@modelcontextprotocol/sdk": "^1.29.0",
45
44
  "@types/node": "^25.6.0",
46
- "@types/pg": "^8.11.10",
45
+ "@types/pg": "^8.20.0",
47
46
  "esbuild": "^0.28.0",
48
47
  "pg": "^8.13.0",
49
48
  "typescript": "^6.0.3",