pg-admin-mcp 0.0.0-stage → 0.1.0

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 (130) hide show
  1. package/README.md +608 -2
  2. package/dist/config/env.js +67 -0
  3. package/dist/config/env.js.map +1 -0
  4. package/dist/config/profiles.js +257 -0
  5. package/dist/config/profiles.js.map +1 -0
  6. package/dist/core/audit-context.js +21 -0
  7. package/dist/core/audit-context.js.map +1 -0
  8. package/dist/core/audit.js +91 -0
  9. package/dist/core/audit.js.map +1 -0
  10. package/dist/core/brands.js +24 -0
  11. package/dist/core/brands.js.map +1 -0
  12. package/dist/core/classifier.js +279 -0
  13. package/dist/core/classifier.js.map +1 -0
  14. package/dist/core/errors.js +197 -0
  15. package/dist/core/errors.js.map +1 -0
  16. package/dist/core/format.js +258 -0
  17. package/dist/core/format.js.map +1 -0
  18. package/dist/core/guards.js +513 -0
  19. package/dist/core/guards.js.map +1 -0
  20. package/dist/core/identifiers.js +431 -0
  21. package/dist/core/identifiers.js.map +1 -0
  22. package/dist/core/limits.js +79 -0
  23. package/dist/core/limits.js.map +1 -0
  24. package/dist/core/logger.js +34 -0
  25. package/dist/core/logger.js.map +1 -0
  26. package/dist/core/paginate.js +195 -0
  27. package/dist/core/paginate.js.map +1 -0
  28. package/dist/core/paths.js +392 -0
  29. package/dist/core/paths.js.map +1 -0
  30. package/dist/core/policy.js +98 -0
  31. package/dist/core/policy.js.map +1 -0
  32. package/dist/core/shutdown.js +10 -0
  33. package/dist/core/shutdown.js.map +1 -0
  34. package/dist/core/stdout-guard.js +25 -0
  35. package/dist/core/stdout-guard.js.map +1 -0
  36. package/dist/db/catalog.js +1006 -0
  37. package/dist/db/catalog.js.map +1 -0
  38. package/dist/db/pool.js +158 -0
  39. package/dist/db/pool.js.map +1 -0
  40. package/dist/db/session.js +258 -0
  41. package/dist/db/session.js.map +1 -0
  42. package/dist/db/types.js +2 -0
  43. package/dist/db/types.js.map +1 -0
  44. package/dist/index.js +91 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/resources/index.js +88 -0
  47. package/dist/resources/index.js.map +1 -0
  48. package/dist/runtime/binaries.js +356 -0
  49. package/dist/runtime/binaries.js.map +1 -0
  50. package/dist/runtime/env-file.js +62 -0
  51. package/dist/runtime/env-file.js.map +1 -0
  52. package/dist/runtime/environment.js +34 -0
  53. package/dist/runtime/environment.js.map +1 -0
  54. package/dist/server.js +106 -0
  55. package/dist/server.js.map +1 -0
  56. package/dist/snapshot/capture.js +189 -0
  57. package/dist/snapshot/capture.js.map +1 -0
  58. package/dist/snapshot/diff.js +254 -0
  59. package/dist/snapshot/diff.js.map +1 -0
  60. package/dist/snapshot/migrate.js +1596 -0
  61. package/dist/snapshot/migrate.js.map +1 -0
  62. package/dist/snapshot/model.js +80 -0
  63. package/dist/snapshot/model.js.map +1 -0
  64. package/dist/tools/_archive_meta.js +138 -0
  65. package/dist/tools/_archive_meta.js.map +1 -0
  66. package/dist/tools/_batch_shared.js +73 -0
  67. package/dist/tools/_batch_shared.js.map +1 -0
  68. package/dist/tools/_export_format.js +107 -0
  69. package/dist/tools/_export_format.js.map +1 -0
  70. package/dist/tools/_object_kinds.js +97 -0
  71. package/dist/tools/_object_kinds.js.map +1 -0
  72. package/dist/tools/_relation_detail.js +55 -0
  73. package/dist/tools/_relation_detail.js.map +1 -0
  74. package/dist/tools/_shared.js +17 -0
  75. package/dist/tools/_shared.js.map +1 -0
  76. package/dist/tools/_snapshot_io.js +238 -0
  77. package/dist/tools/_snapshot_io.js.map +1 -0
  78. package/dist/tools/admin/_shared.js +18 -0
  79. package/dist/tools/admin/_shared.js.map +1 -0
  80. package/dist/tools/admin/extensions.js +80 -0
  81. package/dist/tools/admin/extensions.js.map +1 -0
  82. package/dist/tools/admin/grants.js +207 -0
  83. package/dist/tools/admin/grants.js.map +1 -0
  84. package/dist/tools/admin/index.js +138 -0
  85. package/dist/tools/admin/index.js.map +1 -0
  86. package/dist/tools/admin/maintenance.js +106 -0
  87. package/dist/tools/admin/maintenance.js.map +1 -0
  88. package/dist/tools/admin/roles.js +144 -0
  89. package/dist/tools/admin/roles.js.map +1 -0
  90. package/dist/tools/admin/sessions.js +104 -0
  91. package/dist/tools/admin/sessions.js.map +1 -0
  92. package/dist/tools/admin/stats.js +63 -0
  93. package/dist/tools/admin/stats.js.map +1 -0
  94. package/dist/tools/backup.js +288 -0
  95. package/dist/tools/backup.js.map +1 -0
  96. package/dist/tools/connection_status.js +138 -0
  97. package/dist/tools/connection_status.js.map +1 -0
  98. package/dist/tools/copy.js +355 -0
  99. package/dist/tools/copy.js.map +1 -0
  100. package/dist/tools/delete_batch.js +73 -0
  101. package/dist/tools/delete_batch.js.map +1 -0
  102. package/dist/tools/describe_object.js +87 -0
  103. package/dist/tools/describe_object.js.map +1 -0
  104. package/dist/tools/execute_sql.js +395 -0
  105. package/dist/tools/execute_sql.js.map +1 -0
  106. package/dist/tools/explain.js +134 -0
  107. package/dist/tools/explain.js.map +1 -0
  108. package/dist/tools/export.js +215 -0
  109. package/dist/tools/export.js.map +1 -0
  110. package/dist/tools/get_schema.js +148 -0
  111. package/dist/tools/get_schema.js.map +1 -0
  112. package/dist/tools/index.js +40 -0
  113. package/dist/tools/index.js.map +1 -0
  114. package/dist/tools/insert_batch.js +212 -0
  115. package/dist/tools/insert_batch.js.map +1 -0
  116. package/dist/tools/list_objects.js +160 -0
  117. package/dist/tools/list_objects.js.map +1 -0
  118. package/dist/tools/prepare_migration.js +112 -0
  119. package/dist/tools/prepare_migration.js.map +1 -0
  120. package/dist/tools/restore.js +301 -0
  121. package/dist/tools/restore.js.map +1 -0
  122. package/dist/tools/schema_diff.js +79 -0
  123. package/dist/tools/schema_diff.js.map +1 -0
  124. package/dist/tools/schema_snapshot.js +57 -0
  125. package/dist/tools/schema_snapshot.js.map +1 -0
  126. package/dist/tools/select.js +162 -0
  127. package/dist/tools/select.js.map +1 -0
  128. package/dist/tools/update_batch.js +154 -0
  129. package/dist/tools/update_batch.js.map +1 -0
  130. package/package.json +47 -5
@@ -0,0 +1,104 @@
1
+ import { z } from 'zod';
2
+ import { PgMcpError } from '../../core/errors.js';
3
+ import { requireCapability } from '../../core/policy.js';
4
+ import { getBackendInfo, getBackendPid, listSessions } from '../../db/catalog.js';
5
+ import { withSession } from '../../db/session.js';
6
+ import { reportList } from './_shared.js';
7
+ /**
8
+ * `sessions` sub-operation. `sessions_list` (read-only, no capability) reads
9
+ * `pg_stat_activity`.
10
+ *
11
+ * `sessions_cancel`/`sessions_terminate` require `admin` and both refuse to
12
+ * target the caller's own backend or a non-`client backend` process. Neither
13
+ * goes through `runGuardedMutation`'s dry-run/confirm hash ceremony:
14
+ * `pg_cancel_backend`/`pg_terminate_backend` send an OS-level signal the
15
+ * instant they run — that effect is NOT undone by a transaction rollback, so
16
+ * a "dry run" executed inside `runGuardedMutation`'s rolled-back transaction
17
+ * would still really cancel/terminate the target while claiming nothing
18
+ * happened. That is exactly the dishonesty the phase brief forbids, so
19
+ * `sessions_terminate` instead uses a plain `confirm: true` gate with no
20
+ * hash/expect token — there is nothing transactional to preview. `cancel` is
21
+ * lower stakes still (the target connection survives; only its in-flight
22
+ * statement fails) and executes immediately once identity checks pass.
23
+ */
24
+ export const sessionsListSchema = z.object({
25
+ op: z.literal('sessions_list'),
26
+ allDatabases: z.boolean().optional().default(false).describe('false (default) = this profile\'s database only.'),
27
+ });
28
+ export const sessionsCancelSchema = z.object({
29
+ op: z.literal('sessions_cancel'),
30
+ pid: z.number().int().positive(),
31
+ });
32
+ export const sessionsTerminateSchema = z.object({
33
+ op: z.literal('sessions_terminate'),
34
+ pid: z.number().int().positive(),
35
+ confirm: z
36
+ .boolean()
37
+ .optional()
38
+ .describe('Must be true to actually terminate. No dry run is offered: pg_terminate_backend signals immediately and is not undone by a rolled-back transaction.'),
39
+ });
40
+ export async function handleSessionsList(profile, args) {
41
+ const sessions = await withSession(profile, (client) => listSessions(client, args.allDatabases), { operation: 'admin_sessions_list' });
42
+ return reportList(profile, sessions);
43
+ }
44
+ async function checkTargetable(profile, pid) {
45
+ requireCapability('admin', profile);
46
+ return withSession(profile, async (client) => {
47
+ const backendPid = await getBackendPid(client);
48
+ if (pid === backendPid) {
49
+ throw new PgMcpError({
50
+ code: 'PG_ERROR',
51
+ message: 'refusing to target the connection postgres_admin is using to run this call',
52
+ hint: 'terminating/cancelling your own backend would abort this very request; target a different pid',
53
+ details: { pid },
54
+ });
55
+ }
56
+ const info = await getBackendInfo(client, pid);
57
+ if (info === null) {
58
+ throw new PgMcpError({
59
+ code: 'IDENTIFIER_UNKNOWN',
60
+ message: `no backend with pid ${pid} is currently connected`,
61
+ hint: 'run sessions_list to see current backends',
62
+ details: { pid },
63
+ });
64
+ }
65
+ if (info.backendType !== 'client backend') {
66
+ throw new PgMcpError({
67
+ code: 'PG_ERROR',
68
+ message: `refusing to target pid ${pid}: backend_type is "${info.backendType}", not a client backend`,
69
+ hint: 'only ordinary client connections can be targeted through postgres_admin; a background/system process needs operator intervention outside this tool',
70
+ details: { pid, backendType: info.backendType },
71
+ });
72
+ }
73
+ return { backendPid, usename: info.usename };
74
+ }, { operation: 'admin_sessions_check' });
75
+ }
76
+ export async function handleSessionsCancel(profile, args) {
77
+ await checkTargetable(profile, args.pid);
78
+ const ok = await withSession(profile, async (client) => {
79
+ const { rows } = await client.query({ text: 'SELECT pg_cancel_backend($1) AS ok', values: [args.pid], queryMode: 'extended' });
80
+ return rows[0]?.ok ?? false;
81
+ }, { operation: 'admin_sessions_cancel' });
82
+ return { kind: 'mutation', rowCount: ok ? 1 : 0, committed: ok };
83
+ }
84
+ export async function handleSessionsTerminate(profile, args) {
85
+ const target = await checkTargetable(profile, args.pid);
86
+ if (args.confirm !== true) {
87
+ throw new PgMcpError({
88
+ code: 'GUARD_CONFIRM_REQUIRED',
89
+ message: `terminating pid ${args.pid} (${target.usename ?? 'unknown user'}) requires confirm:true`,
90
+ hint: 'pg_terminate_backend sends an immediate, non-transactional signal, so no dry run is offered — re-run with confirm:true once you are sure',
91
+ details: { profile: profile.alias, pid: args.pid, usename: target.usename },
92
+ });
93
+ }
94
+ const ok = await withSession(profile, async (client) => {
95
+ const { rows } = await client.query({ text: 'SELECT pg_terminate_backend($1) AS ok', values: [args.pid], queryMode: 'extended' });
96
+ return rows[0]?.ok ?? false;
97
+ }, { operation: 'admin_sessions_terminate' });
98
+ return { kind: 'mutation', rowCount: ok ? 1 : 0, committed: ok };
99
+ }
100
+ export const SESSIONS_DESCRIPTION = '`sessions_list` (read-only, no capability) reads pg_stat_activity. `sessions_cancel`/`sessions_terminate` require `admin` and refuse to ' +
101
+ "target your own backend or a non-client-backend process. Neither offers a dry run: pg_cancel_backend/pg_terminate_backend signal " +
102
+ 'immediately and are not transactional, so a rolled-back "dry run" would still really act. `sessions_terminate` needs `confirm: true`; ' +
103
+ '`sessions_cancel` (the connection survives, only its in-flight statement fails) does not.';
104
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/tools/admin/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+ import { resolveSchemaScope } from '../../core/identifiers.js';
3
+ import { getBlockedQueries, getIndexStats, getLongRunningQueries, getTableStats } from '../../db/catalog.js';
4
+ import { withSession } from '../../db/session.js';
5
+ import { reportList } from './_shared.js';
6
+ /**
7
+ * `stats` sub-operation — table/index statistics (with a dead-tuple-ratio
8
+ * bloat *estimate*, not a `pgstattuple`-precision bloat number — that
9
+ * extension is not assumed installed), cache hit info (`heapBlksRead`/
10
+ * `heapBlksHit`, `idxBlksRead`/`idxBlksHit` are in the row data itself, ratio
11
+ * left to the caller), long-running queries, and blocked/blocking pairs.
12
+ * Entirely read-only, no capability beyond connecting — matches every other
13
+ * pure-listing tool in this server.
14
+ */
15
+ export const statsTablesSchema = z.object({
16
+ op: z.literal('stats_tables'),
17
+ schemas: z.array(z.string().min(1)).optional().describe('Defaults to every schema in scope for this profile.'),
18
+ namePattern: z.string().optional().describe('SQL ILIKE pattern to filter table names.'),
19
+ });
20
+ export const statsIndexesSchema = z.object({
21
+ op: z.literal('stats_indexes'),
22
+ schemas: z.array(z.string().min(1)).optional(),
23
+ namePattern: z.string().optional(),
24
+ });
25
+ export const statsActivitySchema = z.object({
26
+ op: z.literal('stats_activity'),
27
+ longRunningThresholdMs: z.number().int().min(0).optional().default(5000),
28
+ });
29
+ export async function handleStatsTables(profile, args) {
30
+ const stats = await withSession(profile, async (client) => {
31
+ const schemas = await resolveSchemaScope(client, profile, args.schemas);
32
+ return getTableStats(client, schemas, args.namePattern);
33
+ }, { operation: 'admin_stats_tables' });
34
+ return reportList(profile, stats.map((s) => ({
35
+ ...s,
36
+ deadTupleRatioEstimate: bloatEstimate(s.liveTuples, s.deadTuples),
37
+ })));
38
+ }
39
+ function bloatEstimate(live, dead) {
40
+ const liveN = Number(live);
41
+ const deadN = Number(dead);
42
+ const total = liveN + deadN;
43
+ return total > 0 ? Math.round((deadN / total) * 10000) / 10000 : null;
44
+ }
45
+ export async function handleStatsIndexes(profile, args) {
46
+ const stats = await withSession(profile, async (client) => {
47
+ const schemas = await resolveSchemaScope(client, profile, args.schemas);
48
+ return getIndexStats(client, schemas, args.namePattern);
49
+ }, { operation: 'admin_stats_indexes' });
50
+ return reportList(profile, stats);
51
+ }
52
+ export async function handleStatsActivity(profile, args) {
53
+ const [longRunning, blocked] = await withSession(profile, async (client) => {
54
+ const lr = await getLongRunningQueries(client, args.longRunningThresholdMs);
55
+ const bl = await getBlockedQueries(client);
56
+ return [lr, bl];
57
+ }, { operation: 'admin_stats_activity' });
58
+ return { kind: 'report', data: { longRunning, blocked } };
59
+ }
60
+ export const STATS_DESCRIPTION = 'Read-only, no capability beyond connecting. `stats_tables`/`stats_indexes` report pg_stat_user_tables/pg_stat_user_indexes plus IO cache ' +
61
+ 'counters and total size; `deadTupleRatioEstimate` on stats_tables is a heuristic (n_dead_tup / (n_live_tup + n_dead_tup)), NOT a ' +
62
+ 'pgstattuple-precision bloat number. `stats_activity` reports long-running queries and blocked/blocking backend pairs from pg_locks.';
63
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/tools/admin/stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,288 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readdir, readFile, rename, rm, stat } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { Transform } from 'node:stream';
5
+ import { pipeline } from 'node:stream/promises';
6
+ import { z } from 'zod';
7
+ import { loadProfiles } from '../config/profiles.js';
8
+ import { PgMcpError, toToolResult } from '../core/errors.js';
9
+ import { formatOutcome } from '../core/format.js';
10
+ import { renderHostPath, resolveWorkspacePath, writeFileAtomic } from '../core/paths.js';
11
+ import { requireCapability } from '../core/policy.js';
12
+ import { assertClientCoversServer, connectionArgs, requireBinary, runBinary } from '../runtime/binaries.js';
13
+ import { ARCHIVE_META_VERSION, serverVersionOf, sidecarPathFor } from './_archive_meta.js';
14
+ import { defineTool, profileParam } from './_shared.js';
15
+ /**
16
+ * `postgres_backup` — `pg_dump` of a database, of named schemas, or of named
17
+ * tables, into the workspace.
18
+ *
19
+ * **It runs outside the guard, and says so.** `core/guards.ts#runGuardedMutation`
20
+ * previews a mutation by running it inside a transaction this server opened and
21
+ * then rolling back. `pg_dump` is a separate process holding its own
22
+ * connections; there is no transaction here to roll back, no row count to
23
+ * compare, and nothing to preview — a "dry run" of a backup would either be a
24
+ * lie or a second full dump. It is also not destructive to the database: it
25
+ * only reads, and the one thing it writes is a file, whose accidental
26
+ * overwrite is refused by `writeFileAtomic` unless `overwrite: true`. So the
27
+ * gate is the `backup` capability (`PG_<ALIAS>_ALLOW_BACKUP`) plus the workspace
28
+ * path rules, and that is the whole gate. Same shape, and same reasoning, as
29
+ * `tools/admin/maintenance.ts`'s VACUUM: not possible, rather than defeated.
30
+ *
31
+ * **`statement_timeout` is never applied to the child** (design.md risk 6). It
32
+ * is a different process, and a legitimate backup runs far longer than any
33
+ * per-statement limit a profile sets for interactive queries. The child gets
34
+ * its own wall-clock deadline instead, `timeoutMs`, clamped to the profile's
35
+ * own `PG_<ALIAS>_BACKUP_TIMEOUT_MAX_MS` (1 hour by default), and is killed
36
+ * when it fires — a `pg_dump` left running holds a server connection open.
37
+ *
38
+ * **The version pre-check** (design.md risk 2) reads the server's version and
39
+ * compares it against `pg_dump --version` *before* spawning anything, so a
40
+ * client older than the server fails with an actionable message naming the
41
+ * image tag rather than with `pg_dump`'s own "server version mismatch" on
42
+ * stderr.
43
+ *
44
+ * **Nothing reaches stdout.** stdout is the MCP transport. For the three
45
+ * single-file formats `pg_dump` writes its archive to *its* stdout, which is a
46
+ * pipe this module streams into `core/paths.ts#writeFileAtomic` — the same
47
+ * `<name>.partial`-then-publish path `postgres_export` uses, so a killed
48
+ * `pg_dump` leaves no plausible truncated archive under the real name, and the
49
+ * `wx` flag on the partial file refuses a pre-planted symlink. Directory format
50
+ * cannot come down a pipe, so that one branch uses `pg_dump --file=<dir>.partial`
51
+ * into a directory this module created with `mkdir` (which fails `EEXIST`
52
+ * rather than following a symlink) and renames on success.
53
+ *
54
+ * **The sidecar** (`<path>.meta.json`, see `tools/_archive_meta.ts`) records
55
+ * the client major, the server version, the scope and a checksum, so
56
+ * `postgres_restore` can refuse a forward-only archive before spawning
57
+ * anything (design.md risk 10).
58
+ */
59
+ const FORMATS = {
60
+ custom: 'custom',
61
+ directory: 'directory',
62
+ tar: 'tar',
63
+ plain: 'plain',
64
+ };
65
+ const inputSchema = z
66
+ .object({
67
+ ...profileParam,
68
+ path: z
69
+ .string()
70
+ .min(1)
71
+ .describe('Destination. Relative paths resolve against the profile\'s first allowed directory; absolute paths must still land inside one. For format:"directory" this is the directory pg_dump fills.'),
72
+ format: z
73
+ .enum(['custom', 'directory', 'tar', 'plain'])
74
+ .optional()
75
+ .describe('custom (default) — compressed, selective restore, the only format postgres_restore prefers. directory — one file per table, restorable in parallel. tar. plain — SQL text, which pg_restore CANNOT read (feed it to psql).'),
76
+ schemas: z.array(z.string().min(1)).optional().describe('Dump only these schemas (pg_dump --schema). Omit for the whole database.'),
77
+ tables: z.array(z.string().min(1)).optional().describe('Dump only these tables (pg_dump --table), optionally schema-qualified.'),
78
+ schemaOnly: z.boolean().optional().describe('DDL only, no rows.'),
79
+ dataOnly: z.boolean().optional().describe('Rows only, no DDL.'),
80
+ noOwner: z.boolean().optional().describe('Omit ownership commands, so the archive restores as whoever runs the restore.'),
81
+ overwrite: z.boolean().optional().describe('Required (true) to replace an existing file at path. Defaults to false.'),
82
+ timeoutMs: z
83
+ .number()
84
+ .int()
85
+ .min(1)
86
+ .optional()
87
+ .describe('Wall-clock ceiling for the pg_dump child, clamped to PG_<ALIAS>_BACKUP_TIMEOUT_MAX_MS (1 h by default). The profile\'s statement_timeout is deliberately NOT applied to a backup.'),
88
+ })
89
+ .refine((args) => !(args.schemaOnly === true && args.dataOnly === true), {
90
+ message: 'schemaOnly and dataOnly are mutually exclusive — pg_dump refuses both at once',
91
+ });
92
+ /**
93
+ * Every option in `--opt=value` form, deliberately: a schema or table pattern
94
+ * beginning with `-` is then a value, not a flag. Nothing here is interpolated
95
+ * into a shell — {@link runBinary} spawns with an argv array and
96
+ * `shell: false` — and the password is not in this list at all (it reaches the
97
+ * child through `PGPASSWORD` on its env).
98
+ */
99
+ export function dumpArgs(profile, args, format, fileTarget) {
100
+ return [
101
+ ...connectionArgs(profile),
102
+ `--dbname=${profile.database}`,
103
+ `--format=${FORMATS[format]}`,
104
+ ...(fileTarget === undefined ? [] : [`--file=${fileTarget}`]),
105
+ ...(args.schemas ?? []).map((s) => `--schema=${s}`),
106
+ ...(args.tables ?? []).map((t) => `--table=${t}`),
107
+ ...(args.schemaOnly === true ? ['--schema-only'] : []),
108
+ ...(args.dataOnly === true ? ['--data-only'] : []),
109
+ ...(args.noOwner === true ? ['--no-owner'] : []),
110
+ ];
111
+ }
112
+ /** Hashes the archive in the same pass the bytes already make on their way to disk — the checksum costs no second read. */
113
+ function hashingPassThrough(digest) {
114
+ return new Transform({
115
+ transform(chunk, _encoding, callback) {
116
+ digest.update(chunk);
117
+ callback(null, chunk);
118
+ },
119
+ });
120
+ }
121
+ async function directorySize(dir) {
122
+ const entries = await readdir(dir, { withFileTypes: true });
123
+ let total = 0;
124
+ for (const entry of entries) {
125
+ const full = path.join(dir, entry.name);
126
+ if (entry.isDirectory()) {
127
+ total += await directorySize(full);
128
+ continue;
129
+ }
130
+ total += (await stat(full)).size;
131
+ }
132
+ return total;
133
+ }
134
+ /**
135
+ * Directory format cannot be streamed: `pg_dump -Fd` writes one file per table
136
+ * into a directory it opens itself. `mkdir` of the `.partial` directory is what
137
+ * replaces `writeFileAtomic`'s `wx` flag here — it fails `EEXIST` on anything
138
+ * already at that name, including a symlink, rather than following it out of
139
+ * the allow-list — and the whole directory is removed if `pg_dump` fails, so a
140
+ * half-written dump is never published.
141
+ */
142
+ async function dumpToDirectory(profile, args, destination, command, timeoutMs, overwrite) {
143
+ const exists = await stat(destination).then(() => true, () => false);
144
+ if (exists && !overwrite) {
145
+ throw new PgMcpError({
146
+ code: 'PATH_DENIED',
147
+ message: `"${destination}" already exists`,
148
+ hint: 'pass overwrite: true to replace it, or choose a different destination path',
149
+ details: { path: destination },
150
+ });
151
+ }
152
+ const partial = `${destination}.partial`;
153
+ try {
154
+ await mkdir(partial);
155
+ }
156
+ catch (err) {
157
+ throw new PgMcpError({
158
+ code: 'PATH_DENIED',
159
+ message: `"${partial}" could not be created`,
160
+ hint: `something already exists there — if a killed pg_dump left it behind, delete ${partial} and re-run`,
161
+ details: { path: partial },
162
+ cause: err,
163
+ });
164
+ }
165
+ try {
166
+ await runBinary('pg_dump', command, dumpArgs(profile, args, 'directory', partial), {
167
+ timeoutMs,
168
+ password: profile.password(),
169
+ operation: 'backup',
170
+ });
171
+ // The TOC is the one file every directory archive has and the one
172
+ // `pg_restore` reads first, so it is what identifies this dump. Hashing
173
+ // every data file would double the I/O of the backup itself.
174
+ const toc = await readFile(path.join(partial, 'toc.dat'));
175
+ const checksum = createHash('sha256').update(toc).digest('hex');
176
+ const bytes = await directorySize(partial);
177
+ // `rename` onto an existing directory fails rather than merging, so an
178
+ // approved overwrite has to clear the old one first. Done only after the
179
+ // dump succeeded — a failed backup never destroys the previous one.
180
+ if (overwrite)
181
+ await rm(destination, { recursive: true, force: true });
182
+ await rename(partial, destination);
183
+ return { bytes, checksum, checksumOf: 'toc.dat' };
184
+ }
185
+ catch (err) {
186
+ await rm(partial, { recursive: true, force: true });
187
+ throw err;
188
+ }
189
+ }
190
+ /** custom / tar / plain all come down `pg_dump`'s stdout, which goes straight into the atomic-write partial file. */
191
+ async function dumpToFile(profile, args, destination, format, command, timeoutMs, overwrite) {
192
+ const digest = createHash('sha256');
193
+ const bytes = await writeFileAtomic(destination, async (dest) => {
194
+ await runBinary('pg_dump', command, dumpArgs(profile, args, format, undefined), {
195
+ timeoutMs,
196
+ password: profile.password(),
197
+ operation: 'backup',
198
+ // `pipeline`, never a manual data loop: pg_dump only fills its pipe as
199
+ // fast as the file drains it. Piped to the destination file handle —
200
+ // never to process.stdout, which is the MCP transport.
201
+ sink: (stdout) => pipeline(stdout, hashingPassThrough(digest), dest),
202
+ });
203
+ }, { overwrite });
204
+ return { bytes, checksum: digest.digest('hex'), checksumOf: 'archive' };
205
+ }
206
+ export const backupTool = defineTool('postgres_backup', {
207
+ title: 'Back up a database, schema or table with pg_dump',
208
+ description: 'Runs pg_dump into a file inside the profile\'s allowed directories. Requires PG_<ALIAS>_ALLOW_BACKUP=true. ' +
209
+ 'Scope: the whole database (default), or only the named `schemas` / `tables`. Formats: custom (default, and the ' +
210
+ 'one postgres_restore wants), directory, tar, plain (plain is SQL TEXT — pg_restore cannot read it). Writes ' +
211
+ '<path>.partial and renames on success, so a killed pg_dump never leaves a plausible truncated archive, and ' +
212
+ 'writes a <path>.meta.json sidecar recording the client major, the server version, the scope and a checksum — ' +
213
+ 'postgres_restore reads it to refuse an archive newer than the local pg_restore. There is no dry run: pg_dump is ' +
214
+ 'a separate process with no transaction to roll back and nothing to preview. The profile\'s statement_timeout is ' +
215
+ 'NOT applied to the child; use timeoutMs (clamped to PG_<ALIAS>_BACKUP_TIMEOUT_MAX_MS) instead.',
216
+ inputSchema,
217
+ // Writes a file, never the database. Idempotent in the sense that matters
218
+ // here: re-running produces the same artifact for the same scope, and an
219
+ // accidental clobber is refused unless `overwrite: true` — unlike
220
+ // `postgres_copy`, whose other direction really does mutate a table.
221
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
222
+ }, async (args) => {
223
+ try {
224
+ const profile = loadProfiles().get(args.profile);
225
+ requireCapability('backup', profile);
226
+ const format = args.format ?? 'custom';
227
+ const timeoutMs = Math.min(args.timeoutMs ?? profile.backupTimeoutMaxMs, profile.backupTimeoutMaxMs);
228
+ const destination = await resolveWorkspacePath(args.path, profile.allowedDirs);
229
+ const overwrite = args.overwrite === true;
230
+ // Both lazy, both before any child is spawned: the binary probe is what
231
+ // raises BINARY_MISSING at the tool boundary, and the version comparison
232
+ // is design.md risk 2 — an older client would otherwise fail with raw
233
+ // stderr after the destination file already existed.
234
+ const pgDump = await requireBinary('pg_dump');
235
+ const server = await serverVersionOf(profile, 'backup');
236
+ assertClientCoversServer(pgDump, server.num, server.version, profile.alias);
237
+ const written = format === 'directory'
238
+ ? await dumpToDirectory(profile, args, destination, pgDump.command, timeoutMs, overwrite)
239
+ : await dumpToFile(profile, args, destination, format, pgDump.command, timeoutMs, overwrite);
240
+ const meta = {
241
+ metaVersion: ARCHIVE_META_VERSION,
242
+ createdAt: new Date().toISOString(),
243
+ clientVersion: pgDump.version,
244
+ clientMajor: pgDump.major,
245
+ serverVersion: server.version,
246
+ serverVersionNum: server.num,
247
+ database: profile.database,
248
+ format,
249
+ scope: {
250
+ schemas: [...(args.schemas ?? [])],
251
+ tables: [...(args.tables ?? [])],
252
+ schemaOnly: args.schemaOnly === true,
253
+ dataOnly: args.dataOnly === true,
254
+ },
255
+ bytes: written.bytes,
256
+ checksum: written.checksum,
257
+ checksumOf: written.checksumOf,
258
+ };
259
+ const sidecarPath = sidecarPathFor(destination);
260
+ await writeFileAtomic(sidecarPath, async (dest) => {
261
+ dest.write(`${JSON.stringify(meta, null, 2)}\n`);
262
+ },
263
+ // Always overwrite: the sidecar belongs to the archive that was just
264
+ // published, and a stale one left over from a previous dump at the same
265
+ // path would describe an archive that no longer exists.
266
+ { overwrite: true });
267
+ const hostPath = renderHostPath(destination, profile.allowedDirs);
268
+ return formatOutcome({
269
+ kind: 'file',
270
+ path: destination,
271
+ bytes: written.bytes,
272
+ ...(hostPath === undefined ? {} : { hostPath }),
273
+ meta: {
274
+ format,
275
+ clientVersion: pgDump.version,
276
+ clientMajor: pgDump.major,
277
+ serverVersion: server.version,
278
+ checksum: `sha256:${written.checksum} (${written.checksumOf})`,
279
+ sidecarPath,
280
+ restoreWith: `pg-admin-mcp:pg${pgDump.major} or newer — an archive can never be read by an older pg_restore`,
281
+ },
282
+ });
283
+ }
284
+ catch (err) {
285
+ return toToolResult(err);
286
+ }
287
+ });
288
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/tools/backup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ import { z } from 'zod';
2
+ import { loadProfiles } from '../config/profiles.js';
3
+ import { PgMcpError, enrichUnreachable, fromPgError, toToolResult } from '../core/errors.js';
4
+ import { formatOutcome } from '../core/format.js';
5
+ import { getCurrentRoleInfo } from '../db/catalog.js';
6
+ import { withSession } from '../db/session.js';
7
+ import { PG_BINARIES, requireBinary } from '../runtime/binaries.js';
8
+ import { runtimeReport } from '../runtime/environment.js';
9
+ import { defineTool } from './_shared.js';
10
+ /**
11
+ * The first real tool: `postgres_connection_status`.
12
+ *
13
+ * Reports on one named profile, or every configured profile when `profile` is
14
+ * omitted — deliberately not `_shared.ts#profileParam`, whose docstring says
15
+ * "defaults to PG_MCP_DEFAULT_PROFILE"; this tool's omitted-profile meaning is
16
+ * "report on all of them", not "pick one default".
17
+ *
18
+ * **A broken profile is reported, never thrown.** Two distinct kinds of
19
+ * "broken" exist and are both handled without ever failing the whole call:
20
+ * - a profile that failed to *load* (bad/missing `PG_<ALIAS>_URL`, etc.) —
21
+ * `config/profiles.ts#loadProfiles` already isolates these into
22
+ * `ProfileRegistry.errors`, so there is no `Profile` object for it at all;
23
+ * - a profile that loaded fine but the actual connection attempt fails —
24
+ * caught around the connect-and-probe step and run through
25
+ * `enrichUnreachable` for the container-loopback remediation.
26
+ * Either way the tool call itself never throws for one bad profile among
27
+ * several good ones.
28
+ */
29
+ const inputSchema = z.object({
30
+ profile: z
31
+ .string()
32
+ .optional()
33
+ .describe('Connection profile alias to report on. Omit to report on every configured profile.'),
34
+ });
35
+ /** Never touches the password — `Profile.password()` is not called anywhere in this module. */
36
+ async function probeConnection(profile) {
37
+ return withSession(profile, async (client) => {
38
+ const info = await getCurrentRoleInfo(client);
39
+ if (info === null) {
40
+ throw new Error('server did not return a version or current-role row');
41
+ }
42
+ return {
43
+ serverVersion: info.serverVersion,
44
+ role: {
45
+ role: info.role,
46
+ isSuperuser: info.isSuperuser,
47
+ canCreateRole: info.canCreateRole,
48
+ canCreateDb: info.canCreateDb,
49
+ canReplicate: info.canReplicate,
50
+ bypassRls: info.bypassRls,
51
+ },
52
+ };
53
+ }, { operation: 'connection_status' });
54
+ }
55
+ /**
56
+ * Client binary versions, as design.md's "Networking" section requires this
57
+ * tool to report — so an agent can tell *before* calling `postgres_backup`
58
+ * whether the binaries exist and whether their major covers the servers it is
59
+ * pointed at. Resolution is lazy and cached (`runtime/binaries.ts`), and a
60
+ * missing binary is reported here rather than thrown: this tool's whole job is
61
+ * to describe breakage without failing.
62
+ */
63
+ async function binaryReport() {
64
+ const entries = await Promise.all(PG_BINARIES.map(async (name) => {
65
+ try {
66
+ return [name, (await requireBinary(name)).version];
67
+ }
68
+ catch (err) {
69
+ return [name, `not available — ${err instanceof PgMcpError ? err.hint : String(err)}`];
70
+ }
71
+ }));
72
+ return Object.fromEntries(entries);
73
+ }
74
+ function hostRewriteOf(rewrittenFrom) {
75
+ return { applied: rewrittenFrom !== undefined, ...(rewrittenFrom === undefined ? {} : { from: rewrittenFrom }) };
76
+ }
77
+ async function buildStatus(alias, profile, loadError) {
78
+ const runtime = runtimeReport().runtime;
79
+ if (profile === undefined) {
80
+ const err = loadError ?? new PgMcpError({ code: 'PROFILE_UNKNOWN', message: `no profile named "${alias}"`, hint: 'check PG_MCP_PROFILES' });
81
+ return { alias, ok: false, runtime, error: { code: err.code, message: err.message, hint: err.hint } };
82
+ }
83
+ const redacted = profile.redacted();
84
+ const known = {
85
+ envClass: redacted.envClass,
86
+ host: redacted.host,
87
+ port: redacted.port,
88
+ database: redacted.database,
89
+ user: redacted.user,
90
+ hostRewrite: hostRewriteOf(redacted.rewrittenFrom),
91
+ capabilities: profile.capabilities,
92
+ };
93
+ try {
94
+ const connection = await probeConnection(profile);
95
+ return { alias, ok: true, runtime, ...known, connection };
96
+ }
97
+ catch (err) {
98
+ const enriched = enrichUnreachable(err, { alias: profile.alias, host: profile.host, port: profile.port });
99
+ return { alias, ok: false, runtime, ...known, error: { code: enriched.code, message: enriched.message, hint: enriched.hint } };
100
+ }
101
+ }
102
+ export const connectionStatusTool = defineTool('postgres_connection_status', {
103
+ title: 'PostgreSQL connection status',
104
+ description: 'Reports resolved configuration, reachability, server version, current-role privileges and the pg_dump/pg_restore/psql ' +
105
+ 'client binary versions (which postgres_backup and postgres_restore need) for one named profile ' +
106
+ 'or every configured profile (when `profile` is omitted). A profile that fails to load or connect is reported ' +
107
+ 'with its error and hint, never thrown — the other profiles still report normally. Never returns a password.',
108
+ inputSchema,
109
+ annotations: {
110
+ readOnlyHint: true,
111
+ destructiveHint: false,
112
+ idempotentHint: true,
113
+ openWorldHint: true,
114
+ },
115
+ }, async (args) => {
116
+ try {
117
+ const registry = loadProfiles();
118
+ const aliases = args.profile !== undefined
119
+ ? [args.profile]
120
+ : Array.from(new Set([...registry.list().map((p) => p.alias), ...registry.errors.keys()]));
121
+ const statuses = await Promise.all(aliases.map(async (alias) => {
122
+ const loadError = registry.errors.get(alias);
123
+ if (loadError !== undefined)
124
+ return buildStatus(alias, undefined, loadError);
125
+ try {
126
+ return await buildStatus(alias, registry.get(alias), undefined);
127
+ }
128
+ catch (err) {
129
+ return buildStatus(alias, undefined, err instanceof PgMcpError ? err : fromPgError(err));
130
+ }
131
+ }));
132
+ return formatOutcome({ kind: 'report', data: { binaries: await binaryReport(), profiles: statuses } });
133
+ }
134
+ catch (err) {
135
+ return toToolResult(err);
136
+ }
137
+ });
138
+ //# sourceMappingURL=connection_status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection_status.js","sourceRoot":"","sources":["../../src/tools/connection_status.ts"],"names":[],"mappings":"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"}