@yawlabs/postgres-mcp 0.11.0 → 0.11.1
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 -0
- package/README.md +17 -0
- package/bin/postgres-mcp.mjs +39 -4
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.11.1] - 2026-08-23
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Windows: the launcher no longer hard-kills the server on the first Ctrl-C.** There are no POSIX signals on Windows — `child.kill(sig)` ignores the name and calls `TerminateProcess`, an immediate hard kill (verified: a child with a `SIGTERM` handler never runs it and dies with `code=null`). The launcher forwarded anyway, on the stated assumption that this was a "no-op on Windows", so it aborted the graceful shutdown the console's own Ctrl-C had just started and skipped the server's `process.on("exit")` cleanup. The console already delivers the event to the whole process group, so on Windows the launcher now forwards nothing.
|
|
14
|
+
- **A wedged server no longer leaves the launcher hanging.** Forwarding was gated on `child.killed`, which records only that `kill()` was *called* — never that the child is gone — so every signal after the first was swallowed and there was no escape hatch. Escalation is now armed by a timer on the first signal: one press is enough, and a child still alive after a 2s grace window is killed. Using a timer rather than counting signals also stops the ordinary supervisor sequence (`SIGINT` then `SIGTERM` milliseconds apart) from being misread as impatience.
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
- **The README now carries a "What's new in 0.11.0" section.** The three
|
|
19
|
+
breaking changes lived only in this file, where someone upgrading from
|
|
20
|
+
0.10.x is unlikely to look before their first failing call -- and the
|
|
21
|
+
stats-envelope change fails at the call site rather than at install time,
|
|
22
|
+
since `data` becomes an object where it used to be an array.
|
|
23
|
+
- **Both WSL scripts and the README document the `MSYS_NO_PATHCONV=1` prefix
|
|
24
|
+
they require when invoked from Git Bash on Windows.** Without it, Git Bash
|
|
25
|
+
rewrites the `/mnt/c/...` argument into the Git install prefix before
|
|
26
|
+
`wsl.exe` sees it, and the script exits "No such file or directory" having
|
|
27
|
+
run no tests at all. Piping either script into `tail`/`head` is also called
|
|
28
|
+
out, because the pipeline's exit status is the last command's -- so a red
|
|
29
|
+
matrix reports success.
|
|
30
|
+
|
|
10
31
|
## [0.11.0] - 2026-08-23
|
|
11
32
|
|
|
12
33
|
### Added
|
package/README.md
CHANGED
|
@@ -11,6 +11,23 @@ Built and maintained by [Yaw Labs](https://yaw.sh).
|
|
|
11
11
|
|
|
12
12
|
One click adds this to your local Yaw MCP config so it's available in every Yaw Terminal session. Or install manually below.
|
|
13
13
|
|
|
14
|
+
## What's new in 0.11.0
|
|
15
|
+
|
|
16
|
+
PostgreSQL 18 support, a new I/O observability tool, and version-gated catalog queries. Full detail in the [CHANGELOG](CHANGELOG.md).
|
|
17
|
+
|
|
18
|
+
**Three breaking changes if you are upgrading from 0.10.x:**
|
|
19
|
+
|
|
20
|
+
1. **`pg_seq_scan_tables`, `pg_unused_indexes` and `pg_top_queries` return an envelope, not a bare row array.** Read `data.rows` where you used to read `data`. The envelope carries `stats_reset`, because a cumulative scan count means nothing without knowing when the counters were last reset -- if that happened an hour ago, every index looks unused, which is how a load-bearing index gets dropped.
|
|
21
|
+
2. **`pg_explain` with `analyze: true` now emits `BUFFERS`**, matching what PostgreSQL 18 does server-side. Plans get longer; pass `buffers: false` for the old output.
|
|
22
|
+
3. **Node 22 is the floor.** Node 20 reached end of life.
|
|
23
|
+
|
|
24
|
+
**Worth knowing even if you are not upgrading yet:**
|
|
25
|
+
|
|
26
|
+
- `pg_describe_table` now flags generated and identity columns. Previously a generated column's expression surfaced as `default_value` with nothing marking it, so an agent read the column as optional-with-a-default and wrote an `INSERT` that PostgreSQL rejects.
|
|
27
|
+
- New `pg_io_stats` exposes `pg_stat_io` (PG16+) plus in-flight async I/O from `pg_aios` and the active `io_method` (PG18+).
|
|
28
|
+
- `pg_advisor` checks multixact wraparound alongside transaction-ID wraparound. A lock-heavy workload can exhaust multixacts while `relfrozenxid` still looks healthy.
|
|
29
|
+
- Every version-dependent column is gated on `server_version_num`, so older servers get a thinner answer rather than an error.
|
|
30
|
+
|
|
14
31
|
## Backstory
|
|
15
32
|
|
|
16
33
|
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.
|
package/bin/postgres-mcp.mjs
CHANGED
|
@@ -268,16 +268,51 @@ if (mode === "node") {
|
|
|
268
268
|
void runInProcess();
|
|
269
269
|
});
|
|
270
270
|
|
|
271
|
-
// Forward termination so the server's own
|
|
272
|
-
//
|
|
273
|
-
//
|
|
271
|
+
// Forward termination so the server's own shutdown path runs in the child
|
|
272
|
+
// rather than the child being orphaned.
|
|
273
|
+
//
|
|
274
|
+
// Registering ANY handler for these suppresses Node's default
|
|
275
|
+
// terminate-on-signal, so the parent's exit has to be arranged explicitly.
|
|
276
|
+
// `child.killed` only records that kill() was CALLED, never that the child
|
|
277
|
+
// is gone, so gating on it swallows every signal after the first and wedges
|
|
278
|
+
// the launcher with no escape hatch.
|
|
279
|
+
//
|
|
280
|
+
// Escalation is driven by a TIMER, not by counting signals. Counting is
|
|
281
|
+
// ambiguous: a supervisor routinely sends SIGINT then SIGTERM milliseconds
|
|
282
|
+
// apart, and a terminal Ctrl-C reaches the whole process group, so reading
|
|
283
|
+
// "a second signal" as impatience hard-kills a child that is already
|
|
284
|
+
// shutting down cleanly. A timer makes the count irrelevant -- ONE press is
|
|
285
|
+
// enough, and a wedged child dies on schedule. setTimeout is monotonic, so
|
|
286
|
+
// a wall-clock step cannot mis-gate the window either.
|
|
287
|
+
//
|
|
288
|
+
// POSIX vs Windows, and why we do NOT forward on Windows.
|
|
289
|
+
// On POSIX child.kill(sig) delivers a real, catchable signal, so forwarding
|
|
290
|
+
// is what lets the child run its shutdown. On Windows there are no POSIX
|
|
291
|
+
// signals: child.kill IGNORES the name and calls TerminateProcess -- an
|
|
292
|
+
// immediate hard kill (verified: a child with a SIGTERM handler never runs
|
|
293
|
+
// it and dies with code=null, signal=SIGTERM). Forwarding there ABORTS the
|
|
294
|
+
// graceful shutdown the console's own Ctrl-C just started, skipping the
|
|
295
|
+
// child's process.on("exit") cleanup. The console has already notified the
|
|
296
|
+
// child, so on Windows the timer below is the only kill we issue.
|
|
297
|
+
const ESCALATE_AFTER_MS = 2000;
|
|
298
|
+
let escalation = null;
|
|
274
299
|
for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
275
300
|
process.on(sig, () => {
|
|
276
|
-
|
|
301
|
+
// No try/catch: kill() on an already-exited child returns false, it does
|
|
302
|
+
// not throw. It throws only for a signal the platform does not know,
|
|
303
|
+
// which SIGINT/SIGTERM/SIGKILL never are.
|
|
304
|
+
if (!isWin) child.kill(sig);
|
|
305
|
+
if (escalation) return; // already counting down; further signals are noise
|
|
306
|
+
escalation = setTimeout(() => {
|
|
307
|
+
// Still here after its grace window. Stop waiting on it.
|
|
308
|
+
child.kill("SIGKILL");
|
|
309
|
+
process.exit(128 + (constants.signals[sig] ?? 15));
|
|
310
|
+
}, ESCALATE_AFTER_MS);
|
|
277
311
|
});
|
|
278
312
|
}
|
|
279
313
|
|
|
280
314
|
child.on("exit", (code, signal) => {
|
|
315
|
+
if (escalation) clearTimeout(escalation);
|
|
281
316
|
// Mirror the child's fate: a signal death becomes 128+n so callers see a
|
|
282
317
|
// conventional shell exit status rather than a bare 0.
|
|
283
318
|
if (signal) {
|
package/dist/index.js
CHANGED
|
@@ -39017,7 +39017,7 @@ function compareVersions(a, b) {
|
|
|
39017
39017
|
}
|
|
39018
39018
|
|
|
39019
39019
|
// src/index.ts
|
|
39020
|
-
var version2 = true ? "0.11.
|
|
39020
|
+
var version2 = true ? "0.11.1" : await readPackageVersion();
|
|
39021
39021
|
var subcommand = process.argv[2];
|
|
39022
39022
|
if (subcommand === "version" || subcommand === "--version") {
|
|
39023
39023
|
console.log(version2);
|
package/package.json
CHANGED