@yawlabs/postgres-mcp 0.6.13 → 0.6.15

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 CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.15] - 2026-05-16
11
+
12
+ ### Docs
13
+ - `RunHooks` interface in `api.ts` now documents that `__pgmcp_sp` is a
14
+ reserved savepoint name. `runUserQueryBounded` opens and releases that
15
+ savepoint around the user SQL; a hook that opens a savepoint with the
16
+ same name would be unwound by the inner `RELEASE`. Today no hook collides
17
+ -- this is a contract note for future hook authors. Closes #8.
18
+
19
+ ## [0.6.14] - 2026-05-16
20
+
21
+ ### Security
22
+ - `paramValue` (used by `pg_query`, `pg_readonly`, `pg_explain` for positional
23
+ parameters) was unbounded recursive via `z.lazy`. A pathologically nested
24
+ request body could surface a `Maximum call stack size exceeded` RangeError
25
+ out of the request path instead of a clear validation error. The new
26
+ `paramsArray` schema wraps the top-level params array with an iterative
27
+ depth check capped at 32 levels (arrays and objects each count as one
28
+ level). Closes #7.
29
+
10
30
  ## [0.6.13] - 2026-05-16
11
31
 
12
32
  ### Fixed
package/dist/index.js CHANGED
@@ -36353,6 +36353,25 @@ async function shutdown() {
36353
36353
  var paramValue = external_exports.lazy(
36354
36354
  () => external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null(), external_exports.array(paramValue), external_exports.record(external_exports.string(), paramValue)])
36355
36355
  );
36356
+ var MAX_PARAM_DEPTH = 32;
36357
+ function isWithinDepth(value, maxDepth) {
36358
+ const stack = [[value, 0]];
36359
+ while (stack.length > 0) {
36360
+ const next = stack.pop();
36361
+ if (!next) break;
36362
+ const [v, d] = next;
36363
+ if (d > maxDepth) return false;
36364
+ if (Array.isArray(v)) {
36365
+ for (const item of v) stack.push([item, d + 1]);
36366
+ } else if (v !== null && typeof v === "object") {
36367
+ for (const item of Object.values(v)) stack.push([item, d + 1]);
36368
+ }
36369
+ }
36370
+ return true;
36371
+ }
36372
+ var paramsArray = external_exports.array(paramValue).refine((arr) => arr.every((v) => isWithinDepth(v, MAX_PARAM_DEPTH)), {
36373
+ message: `Parameter value exceeds maximum nesting depth of ${MAX_PARAM_DEPTH}`
36374
+ });
36356
36375
  var identSchema = external_exports.string().min(1).max(63).refine((v) => Buffer.byteLength(v, "utf8") <= 63, {
36357
36376
  message: "Identifier exceeds PostgreSQL's 63-byte NAMEDATALEN limit (multi-byte characters count as multiple bytes)."
36358
36377
  });
@@ -36833,7 +36852,7 @@ var explainTools = [
36833
36852
  sql: external_exports.string().min(1).max(1e6).describe("The SQL statement to explain. Do NOT prefix with EXPLAIN."),
36834
36853
  analyze: external_exports.boolean().default(false).describe("Run EXPLAIN ANALYZE (actually executes the query)."),
36835
36854
  format: external_exports.enum(["text", "json"]).default("text").describe("Output format."),
36836
- params: external_exports.array(paramValue).optional().describe("Positional parameters referenced as $1, $2, ... in the SQL."),
36855
+ params: paramsArray.optional().describe("Positional parameters referenced as $1, $2, ... in the SQL."),
36837
36856
  hypothetical_indexes: external_exports.array(hypotheticalIndex).optional().describe(
36838
36857
  "List of indexes the planner should pretend exist for this EXPLAIN. Requires the HypoPG extension. Indexes are session-scoped and reset at the end of the call."
36839
36858
  )
@@ -36985,7 +37004,7 @@ var queryTools = [
36985
37004
  },
36986
37005
  inputSchema: external_exports.object({
36987
37006
  sql: external_exports.string().min(1).max(1e6).describe("The SQL statement to execute. Hard cap of 1 MB."),
36988
- params: external_exports.array(paramValue).optional().describe("Positional parameters referenced as $1, $2, ... in the SQL.")
37007
+ params: paramsArray.optional().describe("Positional parameters referenced as $1, $2, ... in the SQL.")
36989
37008
  }),
36990
37009
  handler: async (input) => {
36991
37010
  const { sql, params } = input;
@@ -37005,7 +37024,7 @@ var queryTools = [
37005
37024
  },
37006
37025
  inputSchema: external_exports.object({
37007
37026
  sql: external_exports.string().min(1).max(1e6).describe("The SQL statement to execute. Hard cap of 1 MB."),
37008
- params: external_exports.array(paramValue).optional().describe("Positional parameters referenced as $1, $2, ... in the SQL.")
37027
+ params: paramsArray.optional().describe("Positional parameters referenced as $1, $2, ... in the SQL.")
37009
37028
  }),
37010
37029
  handler: async (input) => {
37011
37030
  const { sql, params } = input;
@@ -37591,7 +37610,7 @@ function compareVersions(a, b) {
37591
37610
  }
37592
37611
 
37593
37612
  // src/index.ts
37594
- var version2 = true ? "0.6.13" : (await null).createRequire(import.meta.url)("../package.json").version;
37613
+ var version2 = true ? "0.6.15" : (await null).createRequire(import.meta.url)("../package.json").version;
37595
37614
  var subcommand = process.argv[2];
37596
37615
  if (subcommand === "version" || subcommand === "--version") {
37597
37616
  console.log(version2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/postgres-mcp",
3
- "version": "0.6.13",
3
+ "version": "0.6.15",
4
4
  "mcpName": "io.github.YawLabs/postgres-mcp",
5
5
  "description": "PostgreSQL MCP server - query, schema introspection, explain, and health checks for AI assistants",
6
6
  "license": "MIT",