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.
- package/README.md +608 -2
- package/dist/config/env.js +67 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/profiles.js +257 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/core/audit-context.js +21 -0
- package/dist/core/audit-context.js.map +1 -0
- package/dist/core/audit.js +91 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/brands.js +24 -0
- package/dist/core/brands.js.map +1 -0
- package/dist/core/classifier.js +279 -0
- package/dist/core/classifier.js.map +1 -0
- package/dist/core/errors.js +197 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/format.js +258 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/guards.js +513 -0
- package/dist/core/guards.js.map +1 -0
- package/dist/core/identifiers.js +431 -0
- package/dist/core/identifiers.js.map +1 -0
- package/dist/core/limits.js +79 -0
- package/dist/core/limits.js.map +1 -0
- package/dist/core/logger.js +34 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/paginate.js +195 -0
- package/dist/core/paginate.js.map +1 -0
- package/dist/core/paths.js +392 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/policy.js +98 -0
- package/dist/core/policy.js.map +1 -0
- package/dist/core/shutdown.js +10 -0
- package/dist/core/shutdown.js.map +1 -0
- package/dist/core/stdout-guard.js +25 -0
- package/dist/core/stdout-guard.js.map +1 -0
- package/dist/db/catalog.js +1006 -0
- package/dist/db/catalog.js.map +1 -0
- package/dist/db/pool.js +158 -0
- package/dist/db/pool.js.map +1 -0
- package/dist/db/session.js +258 -0
- package/dist/db/session.js.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/index.js +88 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/runtime/binaries.js +356 -0
- package/dist/runtime/binaries.js.map +1 -0
- package/dist/runtime/env-file.js +62 -0
- package/dist/runtime/env-file.js.map +1 -0
- package/dist/runtime/environment.js +34 -0
- package/dist/runtime/environment.js.map +1 -0
- package/dist/server.js +106 -0
- package/dist/server.js.map +1 -0
- package/dist/snapshot/capture.js +189 -0
- package/dist/snapshot/capture.js.map +1 -0
- package/dist/snapshot/diff.js +254 -0
- package/dist/snapshot/diff.js.map +1 -0
- package/dist/snapshot/migrate.js +1596 -0
- package/dist/snapshot/migrate.js.map +1 -0
- package/dist/snapshot/model.js +80 -0
- package/dist/snapshot/model.js.map +1 -0
- package/dist/tools/_archive_meta.js +138 -0
- package/dist/tools/_archive_meta.js.map +1 -0
- package/dist/tools/_batch_shared.js +73 -0
- package/dist/tools/_batch_shared.js.map +1 -0
- package/dist/tools/_export_format.js +107 -0
- package/dist/tools/_export_format.js.map +1 -0
- package/dist/tools/_object_kinds.js +97 -0
- package/dist/tools/_object_kinds.js.map +1 -0
- package/dist/tools/_relation_detail.js +55 -0
- package/dist/tools/_relation_detail.js.map +1 -0
- package/dist/tools/_shared.js +17 -0
- package/dist/tools/_shared.js.map +1 -0
- package/dist/tools/_snapshot_io.js +238 -0
- package/dist/tools/_snapshot_io.js.map +1 -0
- package/dist/tools/admin/_shared.js +18 -0
- package/dist/tools/admin/_shared.js.map +1 -0
- package/dist/tools/admin/extensions.js +80 -0
- package/dist/tools/admin/extensions.js.map +1 -0
- package/dist/tools/admin/grants.js +207 -0
- package/dist/tools/admin/grants.js.map +1 -0
- package/dist/tools/admin/index.js +138 -0
- package/dist/tools/admin/index.js.map +1 -0
- package/dist/tools/admin/maintenance.js +106 -0
- package/dist/tools/admin/maintenance.js.map +1 -0
- package/dist/tools/admin/roles.js +144 -0
- package/dist/tools/admin/roles.js.map +1 -0
- package/dist/tools/admin/sessions.js +104 -0
- package/dist/tools/admin/sessions.js.map +1 -0
- package/dist/tools/admin/stats.js +63 -0
- package/dist/tools/admin/stats.js.map +1 -0
- package/dist/tools/backup.js +288 -0
- package/dist/tools/backup.js.map +1 -0
- package/dist/tools/connection_status.js +138 -0
- package/dist/tools/connection_status.js.map +1 -0
- package/dist/tools/copy.js +355 -0
- package/dist/tools/copy.js.map +1 -0
- package/dist/tools/delete_batch.js +73 -0
- package/dist/tools/delete_batch.js.map +1 -0
- package/dist/tools/describe_object.js +87 -0
- package/dist/tools/describe_object.js.map +1 -0
- package/dist/tools/execute_sql.js +395 -0
- package/dist/tools/execute_sql.js.map +1 -0
- package/dist/tools/explain.js +134 -0
- package/dist/tools/explain.js.map +1 -0
- package/dist/tools/export.js +215 -0
- package/dist/tools/export.js.map +1 -0
- package/dist/tools/get_schema.js +148 -0
- package/dist/tools/get_schema.js.map +1 -0
- package/dist/tools/index.js +40 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/insert_batch.js +212 -0
- package/dist/tools/insert_batch.js.map +1 -0
- package/dist/tools/list_objects.js +160 -0
- package/dist/tools/list_objects.js.map +1 -0
- package/dist/tools/prepare_migration.js +112 -0
- package/dist/tools/prepare_migration.js.map +1 -0
- package/dist/tools/restore.js +301 -0
- package/dist/tools/restore.js.map +1 -0
- package/dist/tools/schema_diff.js +79 -0
- package/dist/tools/schema_diff.js.map +1 -0
- package/dist/tools/schema_snapshot.js +57 -0
- package/dist/tools/schema_snapshot.js.map +1 -0
- package/dist/tools/select.js +162 -0
- package/dist/tools/select.js.map +1 -0
- package/dist/tools/update_batch.js +154 -0
- package/dist/tools/update_batch.js.map +1 -0
- package/package.json +47 -5
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { pipeline } from 'node:stream/promises';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { loadProfiles } from '../config/profiles.js';
|
|
5
|
+
import { planStatements } from '../core/classifier.js';
|
|
6
|
+
import { toToolResult } from '../core/errors.js';
|
|
7
|
+
import { formatOutcome } from '../core/format.js';
|
|
8
|
+
import { columnIdentFromCatalog, resolveColumn, resolveRelation } from '../core/identifiers.js';
|
|
9
|
+
import { renderHostPath, resolveWorkspacePath, writeFileAtomic } from '../core/paths.js';
|
|
10
|
+
import { getColumns } from '../db/catalog.js';
|
|
11
|
+
import { withTx } from '../db/session.js';
|
|
12
|
+
import { assertNoObviousWrite } from './execute_sql.js';
|
|
13
|
+
import { wherePredicate } from './select.js';
|
|
14
|
+
import { createExportTransform } from './_export_format.js';
|
|
15
|
+
import { defineTool, profileParam } from './_shared.js';
|
|
16
|
+
/**
|
|
17
|
+
* `postgres_export` — streams a query result straight to a file (CSV/JSON/JSONL).
|
|
18
|
+
*
|
|
19
|
+
* **Response cap vs. file cap.** Every other read tool's *response* is bounded
|
|
20
|
+
* by `core/limits.ts#resolveLimits`/`truncateRows` — the row/byte cap that
|
|
21
|
+
* protects the MCP transport and the caller's context window. That module is
|
|
22
|
+
* deliberately never consulted here: the artifact this tool produces is a
|
|
23
|
+
* *file*, not a tool response (the response is a one-line "File written: …"
|
|
24
|
+
* confirmation), and a file the caller explicitly asked to export is not
|
|
25
|
+
* something this server should silently truncate. The only cap on the file
|
|
26
|
+
* itself is disk space and `timeoutMs`.
|
|
27
|
+
*
|
|
28
|
+
* **Streaming, not buffering.** The source is read through a server-side
|
|
29
|
+
* cursor (`DECLARE … CURSOR FOR <query>` + repeated `FETCH FORWARD n`) opened
|
|
30
|
+
* on the one client `withTx` checks out, so at most one `FETCH_BATCH_SIZE`
|
|
31
|
+
* batch of rows is ever in memory. That batch source feeds
|
|
32
|
+
* `tools/_export_format.ts#createExportTransform` and a destination file
|
|
33
|
+
* through `node:stream/promises#pipeline` — never a manual
|
|
34
|
+
* `while (true) { write(...) }` loop — so a slow disk (or a slow downstream
|
|
35
|
+
* consumer, for `postgres_copy`'s sibling design) applies real backpressure:
|
|
36
|
+
* `Readable.from`'s generator is not resumed, and so the next `FETCH` round
|
|
37
|
+
* trip is not issued, until the write side has drained.
|
|
38
|
+
*
|
|
39
|
+
* **`timeoutMs` — raises, then clamps.** Unlike `postgres_execute_sql` (which
|
|
40
|
+
* can only *tighten* the profile's `statementTimeoutMs`), an export is
|
|
41
|
+
* expected to legitimately run long (design.md risk 6), so a caller may raise
|
|
42
|
+
* it — but never past `Profile.exportTimeoutMaxMs`, the operator-set ceiling.
|
|
43
|
+
* Caveat worth knowing: PostgreSQL's `statement_timeout` bounds *each*
|
|
44
|
+
* statement, and every `FETCH` here is its own statement — so this bounds how
|
|
45
|
+
* long any single round trip (including the first, which is where the
|
|
46
|
+
* underlying query's execution actually starts) may take, not the export's
|
|
47
|
+
* total wall-clock time. A query that returns one slow-to-produce row per
|
|
48
|
+
* `FETCH` is not what this guards against; a stuck query or a stuck row is.
|
|
49
|
+
*
|
|
50
|
+
* **Two sources, one policy.** `source.kind === 'table'` is fully
|
|
51
|
+
* catalog-resolved (`resolveRelation`/`resolveColumn`, exactly like
|
|
52
|
+
* `postgres_select`) — never string-interpolated user identifiers.
|
|
53
|
+
* `source.kind === 'sql'` reuses `postgres_execute_sql`'s own
|
|
54
|
+
* `assertNoObviousWrite` + `planStatements` (single statement, params always
|
|
55
|
+
* bound) and, like that tool, ultimately relies on PostgreSQL's own
|
|
56
|
+
* `READ ONLY` transaction (SQLSTATE 25006) as the actual write fence.
|
|
57
|
+
*/
|
|
58
|
+
const FETCH_BATCH_SIZE = 1000;
|
|
59
|
+
const CURSOR_NAME = 'pg_admin_mcp_export_cursor';
|
|
60
|
+
const tableSource = z.object({
|
|
61
|
+
kind: z.literal('table'),
|
|
62
|
+
schema: z.string().min(1),
|
|
63
|
+
table: z.string().min(1),
|
|
64
|
+
columns: z.array(z.string().min(1)).min(1).optional().describe('Columns to export, in this order. Omit for every column, in ordinal order.'),
|
|
65
|
+
where: z.array(wherePredicate).optional().describe('Filter predicates, ANDed together — same shape as postgres_select.'),
|
|
66
|
+
});
|
|
67
|
+
const sqlSource = z.object({
|
|
68
|
+
kind: z.literal('sql'),
|
|
69
|
+
sql: z.string().min(1).describe('A single SELECT (or other row-returning) statement. Never string-interpolate values — use params.'),
|
|
70
|
+
params: z.array(z.unknown()).optional(),
|
|
71
|
+
});
|
|
72
|
+
const csvOptionsSchema = z.object({
|
|
73
|
+
delimiter: z.string().length(1).optional().describe('Defaults to ",".'),
|
|
74
|
+
header: z.boolean().optional().describe('Defaults to true.'),
|
|
75
|
+
nullValue: z
|
|
76
|
+
.string()
|
|
77
|
+
.optional()
|
|
78
|
+
.describe('Text written for SQL NULL, always unquoted — defaults to "". An actual empty string, and a real value whose text equals this sentinel, are both written quoted so none of the three collide.'),
|
|
79
|
+
});
|
|
80
|
+
const inputSchema = z.object({
|
|
81
|
+
...profileParam,
|
|
82
|
+
source: z.discriminatedUnion('kind', [tableSource, sqlSource]).describe('A structured table+where selection, or a raw SQL source.'),
|
|
83
|
+
path: z.string().min(1).describe('Destination file path. Relative paths resolve against the profile\'s first allowed directory; absolute paths must still land inside an allowed directory.'),
|
|
84
|
+
format: z.enum(['csv', 'json', 'jsonl']),
|
|
85
|
+
overwrite: z.boolean().optional().describe('Required (true) to replace an existing file at path. Defaults to false.'),
|
|
86
|
+
timeoutMs: z
|
|
87
|
+
.number()
|
|
88
|
+
.int()
|
|
89
|
+
.min(1)
|
|
90
|
+
.optional()
|
|
91
|
+
.describe('Per-call statement timeout — may raise the profile\'s default, clamped to PG_<ALIAS>_EXPORT_TIMEOUT_MAX_MS.'),
|
|
92
|
+
csv: csvOptionsSchema.optional(),
|
|
93
|
+
});
|
|
94
|
+
async function prepareTableQuery(client, rel, source) {
|
|
95
|
+
const columnInfos = await getColumns(client, rel);
|
|
96
|
+
const knownColumns = columnInfos.map((c) => c.name);
|
|
97
|
+
const selectColumns = source.columns === undefined
|
|
98
|
+
? columnInfos.map((c) => ({ name: c.name, ident: columnIdentFromCatalog(c.name) }))
|
|
99
|
+
: source.columns.map((name) => ({ name, ident: resolveColumn(source.schema, source.table, name, knownColumns) }));
|
|
100
|
+
let paramIndex = 1;
|
|
101
|
+
const whereParts = [];
|
|
102
|
+
const params = [];
|
|
103
|
+
for (const pred of source.where ?? []) {
|
|
104
|
+
const ident = resolveColumn(source.schema, source.table, pred.column, knownColumns);
|
|
105
|
+
if (pred.op === 'IS NULL' || pred.op === 'IS NOT NULL') {
|
|
106
|
+
whereParts.push(`${ident} ${pred.op}`);
|
|
107
|
+
}
|
|
108
|
+
else if (pred.op === 'IN' || pred.op === 'NOT IN') {
|
|
109
|
+
const placeholders = pred.value.map(() => `$${paramIndex++}`).join(', ');
|
|
110
|
+
whereParts.push(`${ident} ${pred.op} (${placeholders})`);
|
|
111
|
+
params.push(...pred.value);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
whereParts.push(`${ident} ${pred.op} $${paramIndex++}`);
|
|
115
|
+
params.push(pred.value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const whereClause = whereParts.length === 0 ? '' : ` WHERE ${whereParts.join(' AND ')}`;
|
|
119
|
+
return {
|
|
120
|
+
sql: `SELECT ${selectColumns.map((c) => c.ident).join(', ')} FROM ${rel.schema}.${rel.name}${whereClause}`,
|
|
121
|
+
params,
|
|
122
|
+
columns: selectColumns.map((c) => c.name),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function prepareSqlQuery(profile, source) {
|
|
126
|
+
const params = source.params ?? [];
|
|
127
|
+
const statements = planStatements(source.sql, { allowMultiple: false, hasParams: params.length > 0 });
|
|
128
|
+
assertNoObviousWrite(statements, profile.alias);
|
|
129
|
+
return { sql: statements[0] ?? '', params };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Everything that needs its **own** pool checkout, done **before** `withTx`
|
|
133
|
+
* takes one — `resolveRelation` runs `withSession` internally, and a second
|
|
134
|
+
* checkout acquired while the first is still held is a deadlock, not a slow
|
|
135
|
+
* call: at `poolMax: 1` it can never be satisfied, and at any `poolMax` that
|
|
136
|
+
* many concurrent exports wedge the profile until
|
|
137
|
+
* `PG_MCP_CONNECT_TIMEOUT_MS` fires. `tools/select.ts` resolves before its own
|
|
138
|
+
* `withTx` for exactly this reason.
|
|
139
|
+
*
|
|
140
|
+
* What comes back is a closure over the already-resolved relation; only
|
|
141
|
+
* `getColumns`, which runs on the caller's client, stays inside the
|
|
142
|
+
* transaction.
|
|
143
|
+
*/
|
|
144
|
+
async function prepareSource(profile, source) {
|
|
145
|
+
if (source.kind === 'sql') {
|
|
146
|
+
const prepared = prepareSqlQuery(profile, source);
|
|
147
|
+
return () => Promise.resolve(prepared);
|
|
148
|
+
}
|
|
149
|
+
const rel = await resolveRelation(profile, source.schema, source.table);
|
|
150
|
+
return (client) => prepareTableQuery(client, rel, source);
|
|
151
|
+
}
|
|
152
|
+
export const exportTool = defineTool('postgres_export', {
|
|
153
|
+
title: 'Export a query result to a file',
|
|
154
|
+
description: 'Streams a table selection or a SELECT statement to a CSV/JSON/JSONL file on disk. Never buffers the full ' +
|
|
155
|
+
'result in memory (a server-side cursor feeds the file in batches), and the file itself is never truncated by ' +
|
|
156
|
+
'the row/byte caps that bound an ordinary tool response. Refuses to overwrite an existing file unless ' +
|
|
157
|
+
'overwrite:true. path resolves against the profile\'s allowed directories (see postgres_connection_status).',
|
|
158
|
+
inputSchema,
|
|
159
|
+
// Same annotation as `postgres_copy`, deliberately: both tools write a file
|
|
160
|
+
// and both replace an existing one under `overwrite: true`, so both are
|
|
161
|
+
// destructive and neither is idempotent (a second identical call either
|
|
162
|
+
// refuses, or replaces a file whose contents may differ because the table
|
|
163
|
+
// moved underneath it).
|
|
164
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
165
|
+
}, async (args) => {
|
|
166
|
+
try {
|
|
167
|
+
const registry = loadProfiles();
|
|
168
|
+
const profile = registry.get(args.profile);
|
|
169
|
+
const resolvedPath = await resolveWorkspacePath(args.path, profile.allowedDirs);
|
|
170
|
+
const timeoutMs = Math.min(args.timeoutMs ?? profile.statementTimeoutMs, profile.exportTimeoutMaxMs);
|
|
171
|
+
const csvOpts = {
|
|
172
|
+
delimiter: args.csv?.delimiter ?? ',',
|
|
173
|
+
header: args.csv?.header ?? true,
|
|
174
|
+
nullValue: args.csv?.nullValue ?? '',
|
|
175
|
+
};
|
|
176
|
+
const format = args.format;
|
|
177
|
+
const prepare = await prepareSource(profile, args.source);
|
|
178
|
+
const bytes = await withTx(profile, async (client) => {
|
|
179
|
+
const prepared = await prepare(client);
|
|
180
|
+
// `queryMode: 'extended'`, not a bare params array: `pg` only selects
|
|
181
|
+
// the extended protocol when `values.length > 0`, and a raw-SQL source
|
|
182
|
+
// with no params would otherwise DECLARE in simple-query mode. See
|
|
183
|
+
// `tools/execute_sql.ts`'s header. `planStatements` already refused a
|
|
184
|
+
// `;`-list; this is the structural half of the same fence.
|
|
185
|
+
await client.query({
|
|
186
|
+
text: `DECLARE ${CURSOR_NAME} NO SCROLL CURSOR FOR ${prepared.sql}`,
|
|
187
|
+
values: prepared.params,
|
|
188
|
+
queryMode: 'extended',
|
|
189
|
+
});
|
|
190
|
+
// The first FETCH is also where a raw-SQL source's column list comes
|
|
191
|
+
// from (its `RowDescription` is populated even when zero rows come
|
|
192
|
+
// back) — a table source already knows its columns from the catalog.
|
|
193
|
+
const first = await client.query(`FETCH FORWARD ${FETCH_BATCH_SIZE} FROM ${CURSOR_NAME}`);
|
|
194
|
+
const columns = prepared.columns ?? first.fields.map((f) => f.name);
|
|
195
|
+
async function* batches() {
|
|
196
|
+
let batch = first.rows;
|
|
197
|
+
for (;;) {
|
|
198
|
+
if (batch.length > 0)
|
|
199
|
+
yield batch;
|
|
200
|
+
if (batch.length < FETCH_BATCH_SIZE)
|
|
201
|
+
return;
|
|
202
|
+
const next = await client.query(`FETCH FORWARD ${FETCH_BATCH_SIZE} FROM ${CURSOR_NAME}`);
|
|
203
|
+
batch = next.rows;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return writeFileAtomic(resolvedPath, (dest) => pipeline(Readable.from(batches()), createExportTransform(format, columns, csvOpts), dest), { overwrite: args.overwrite === true });
|
|
207
|
+
}, { readOnly: true, operation: 'export', statementTimeoutMs: timeoutMs });
|
|
208
|
+
const hostPath = renderHostPath(resolvedPath, profile.allowedDirs);
|
|
209
|
+
return formatOutcome({ kind: 'file', path: resolvedPath, bytes, ...(hostPath === undefined ? {} : { hostPath }) });
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
return toToolResult(err);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/tools/export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { loadProfiles } from '../config/profiles.js';
|
|
3
|
+
import { toToolResult } from '../core/errors.js';
|
|
4
|
+
import { formatOutcome } from '../core/format.js';
|
|
5
|
+
import { relationRefFromCatalog, resolveSchemaScope } from '../core/identifiers.js';
|
|
6
|
+
import { resolveLimits, truncateRows } from '../core/limits.js';
|
|
7
|
+
import { withSession } from '../db/session.js';
|
|
8
|
+
import { gatherObjects, OBJECT_KINDS, RELATION_KINDS } from './_object_kinds.js';
|
|
9
|
+
import { buildRelationOverview, buildRelationOverviewBatch } from './_relation_detail.js';
|
|
10
|
+
import { defineTool, profileParam } from './_shared.js';
|
|
11
|
+
/**
|
|
12
|
+
* `postgres_get_schema` — a structured snapshot of a database (or a filtered
|
|
13
|
+
* scope), composed from the same `db/catalog.ts` listings `list_objects`
|
|
14
|
+
* uses. Two detail levels, not a pagination cursor: this tool answers "what
|
|
15
|
+
* does this database look like", so the size control that matters is "start
|
|
16
|
+
* cheap, then narrow or drill down" rather than "walk every page."
|
|
17
|
+
*
|
|
18
|
+
* - `summary` (default): name + kind only, one flat sorted list across every
|
|
19
|
+
* schema in scope. Cheap enough to run first against an unknown, possibly
|
|
20
|
+
* huge database.
|
|
21
|
+
* - `full`: adds columns, primary key, outbound foreign keys, and indexes
|
|
22
|
+
* for every table-like object (`RELATION_KINDS`) — the "full-columns"
|
|
23
|
+
* level. Sequences/functions/enums already carry their compact
|
|
24
|
+
* parameters/labels at the `details` field regardless of level, since
|
|
25
|
+
* those come for free from the listing query with no extra round trip.
|
|
26
|
+
*
|
|
27
|
+
* Output size is the real risk here (a 500-table schema at `full` detail
|
|
28
|
+
* would blow context budget), so this routes through `core/limits.ts` twice:
|
|
29
|
+
* once as a row-count pre-cap *before* the (relatively expensive, one
|
|
30
|
+
* catalog round trip per relation) `full`-detail enrichment step, so an
|
|
31
|
+
* oversized request never pays to enrich objects it will just discard, and
|
|
32
|
+
* again as a byte-cap pass (`truncateRows`) over the already-enriched set.
|
|
33
|
+
* Either cap produces the same explicit, actionable truncation message:
|
|
34
|
+
* what was kept, what was cut, and how to narrow the scope.
|
|
35
|
+
*/
|
|
36
|
+
const RELATION_KIND_SET = new Set(RELATION_KINDS);
|
|
37
|
+
const inputSchema = z.object({
|
|
38
|
+
...profileParam,
|
|
39
|
+
schemas: z
|
|
40
|
+
.array(z.string().min(1))
|
|
41
|
+
.optional()
|
|
42
|
+
.describe('Restrict to these schemas. Omit for every schema in scope (Profile.schemas, or every non-system schema).'),
|
|
43
|
+
kinds: z
|
|
44
|
+
.array(z.enum(OBJECT_KINDS))
|
|
45
|
+
.optional()
|
|
46
|
+
.describe(`Restrict to these object kinds (${OBJECT_KINDS.join(', ')}). Omit for every kind.`),
|
|
47
|
+
namePattern: z
|
|
48
|
+
.string()
|
|
49
|
+
.max(200)
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('SQL LIKE pattern (% and _ wildcards, \\ escapes a literal % or _), matched case-insensitively against the object name via ILIKE.'),
|
|
52
|
+
detail: z
|
|
53
|
+
.enum(['summary', 'full'])
|
|
54
|
+
.optional()
|
|
55
|
+
.describe('summary (default): name and kind only — cheap, start here on an unknown or large database. ' +
|
|
56
|
+
'full: adds columns, primary key, outbound foreign keys, and indexes for tables/views/materialized views.'),
|
|
57
|
+
maxObjects: z
|
|
58
|
+
.number()
|
|
59
|
+
.int()
|
|
60
|
+
.positive()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('Caps the number of objects included before truncation. Defaults to the profile row limit.'),
|
|
63
|
+
});
|
|
64
|
+
/** `summary` detail: names and kinds only, per the tool's own stated minimum. */
|
|
65
|
+
export function toSummaryEntry(o) {
|
|
66
|
+
return { schema: o.schema, kind: o.kind, name: o.name };
|
|
67
|
+
}
|
|
68
|
+
/** `full` detail baseline (pre-enrichment): everything `summary` has plus owner/comment/kind-specific details. */
|
|
69
|
+
export function toFullEntry(o) {
|
|
70
|
+
return { schema: o.schema, kind: o.kind, name: o.name, owner: o.owner, comment: o.comment, details: o.details };
|
|
71
|
+
}
|
|
72
|
+
/** An explicit, actionable truncation message: what was kept, what was cut, how to narrow. */
|
|
73
|
+
export function truncationHint(kept, total, cutAt) {
|
|
74
|
+
const at = cutAt === undefined ? 'the cap' : `${cutAt.schema}.${cutAt.name} (${cutAt.kind})`;
|
|
75
|
+
return (`showing ${kept} of ${total} objects; truncated at ${at} — narrow the scope with schemas/kinds/namePattern, ` +
|
|
76
|
+
'or keep detail: "summary" for a cheaper listing of everything');
|
|
77
|
+
}
|
|
78
|
+
export const getSchemaTool = defineTool('postgres_get_schema', {
|
|
79
|
+
title: 'Get a PostgreSQL schema snapshot',
|
|
80
|
+
description: 'Structured snapshot of a database, or a filtered scope (schemas, kinds, name pattern), composed from the ' +
|
|
81
|
+
'same catalog listings postgres_list_objects uses. detail:"summary" (default) returns names and kinds only — ' +
|
|
82
|
+
'cheap enough for a large database. detail:"full" adds columns, primary key, outbound foreign keys, and ' +
|
|
83
|
+
'indexes for tables/views/materialized views. An oversized result is truncated with an explicit count and a ' +
|
|
84
|
+
'hint for narrowing scope, never silently cut. The "enum" kind lists enum types only — domain and composite ' +
|
|
85
|
+
'types are not covered by any kind here.',
|
|
86
|
+
inputSchema,
|
|
87
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
88
|
+
}, async (args) => {
|
|
89
|
+
try {
|
|
90
|
+
const registry = loadProfiles();
|
|
91
|
+
const profile = registry.get(args.profile);
|
|
92
|
+
const detail = args.detail ?? 'summary';
|
|
93
|
+
// `gatherObjects` already returns a sorted, name-filtered (ILIKE, bound
|
|
94
|
+
// as `$N` — see `_object_kinds.ts`) set; no further JS-side filtering or
|
|
95
|
+
// re-sorting needed here.
|
|
96
|
+
const filtered = await withSession(profile, async (client) => {
|
|
97
|
+
const schemaNames = await resolveSchemaScope(client, profile, args.schemas);
|
|
98
|
+
return gatherObjects(client, schemaNames, args.kinds, args.namePattern);
|
|
99
|
+
}, { operation: 'get_schema' });
|
|
100
|
+
const limits = resolveLimits(profile.maxRows, args.maxObjects === undefined ? {} : { maxRows: args.maxObjects }, process.env);
|
|
101
|
+
// Pre-cap by object COUNT before the (per-relation catalog round trip)
|
|
102
|
+
// full-detail enrichment step, so a request that would enrich
|
|
103
|
+
// thousands of relations never pays for the objects it will just
|
|
104
|
+
// discard — this bounds real DB work, not only output size.
|
|
105
|
+
const preCapped = filtered.slice(0, limits.maxRows);
|
|
106
|
+
// Q4: one batched `oid = ANY($1)` pass (4 catalog queries total, via
|
|
107
|
+
// `buildRelationOverviewBatch`) for every table-like object in
|
|
108
|
+
// `preCapped`, instead of `buildRelationOverview` once per relation —
|
|
109
|
+
// the previous per-relation shape cost ~4 round trips per relation
|
|
110
|
+
// (166 queries measured for 40 objects; ~2000 at a default 500-row
|
|
111
|
+
// cap), unbounded by anything except `maxRows`.
|
|
112
|
+
const enrichedEntries = detail === 'summary'
|
|
113
|
+
? preCapped.map((o) => ({ ...toSummaryEntry(o) }))
|
|
114
|
+
: await withSession(profile, async (client) => {
|
|
115
|
+
const baseline = preCapped.map(toFullEntry);
|
|
116
|
+
const rels = preCapped
|
|
117
|
+
.filter((o) => o.oid !== null && RELATION_KIND_SET.has(o.kind))
|
|
118
|
+
.map((o) => relationRefFromCatalog(o.oid, o.schema, o.name, o.kind));
|
|
119
|
+
const overviews = await buildRelationOverviewBatch(client, rels);
|
|
120
|
+
return baseline.map((entry, i) => {
|
|
121
|
+
const source = preCapped[i];
|
|
122
|
+
const overview = source?.oid === null || source?.oid === undefined ? undefined : overviews.get(source.oid);
|
|
123
|
+
return overview === undefined ? entry : { ...entry, overview };
|
|
124
|
+
});
|
|
125
|
+
}, { operation: 'get_schema' });
|
|
126
|
+
const byBytes = truncateRows(enrichedEntries, limits, (entry) => JSON.stringify(entry));
|
|
127
|
+
const kept = byBytes.rows;
|
|
128
|
+
const totalCutByCount = filtered.length - preCapped.length;
|
|
129
|
+
const totalCutByBytes = byBytes.truncation.truncatedRows;
|
|
130
|
+
const truncatedCount = totalCutByCount + totalCutByBytes;
|
|
131
|
+
const cutAt = truncatedCount > 0 ? filtered[kept.length] : undefined;
|
|
132
|
+
const data = {
|
|
133
|
+
profile: profile.alias,
|
|
134
|
+
detail,
|
|
135
|
+
schemaCount: new Set(filtered.map((o) => o.schema)).size,
|
|
136
|
+
objectCount: kept.length,
|
|
137
|
+
totalMatched: filtered.length,
|
|
138
|
+
objects: kept,
|
|
139
|
+
truncated: truncatedCount > 0,
|
|
140
|
+
...(truncatedCount > 0 ? { truncationMessage: truncationHint(kept.length, filtered.length, cutAt) } : {}),
|
|
141
|
+
};
|
|
142
|
+
return formatOutcome({ kind: 'report', data });
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
return toToolResult(err);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=get_schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get_schema.js","sourceRoot":"","sources":["../../src/tools/get_schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { adminTool } from './admin/index.js';
|
|
2
|
+
import { backupTool } from './backup.js';
|
|
3
|
+
import { connectionStatusTool } from './connection_status.js';
|
|
4
|
+
import { copyTool } from './copy.js';
|
|
5
|
+
import { deleteBatchTool } from './delete_batch.js';
|
|
6
|
+
import { describeObjectTool } from './describe_object.js';
|
|
7
|
+
import { executeSqlTool } from './execute_sql.js';
|
|
8
|
+
import { explainTool } from './explain.js';
|
|
9
|
+
import { exportTool } from './export.js';
|
|
10
|
+
import { getSchemaTool } from './get_schema.js';
|
|
11
|
+
import { insertBatchTool } from './insert_batch.js';
|
|
12
|
+
import { listObjectsTool } from './list_objects.js';
|
|
13
|
+
import { prepareMigrationTool } from './prepare_migration.js';
|
|
14
|
+
import { restoreTool } from './restore.js';
|
|
15
|
+
import { schemaDiffTool } from './schema_diff.js';
|
|
16
|
+
import { schemaSnapshotTool } from './schema_snapshot.js';
|
|
17
|
+
import { selectTool } from './select.js';
|
|
18
|
+
import { updateBatchTool } from './update_batch.js';
|
|
19
|
+
/** The tool registry. Each phase appends its `defineTool(...)` entries here. */
|
|
20
|
+
export const tools = [
|
|
21
|
+
connectionStatusTool,
|
|
22
|
+
listObjectsTool,
|
|
23
|
+
describeObjectTool,
|
|
24
|
+
getSchemaTool,
|
|
25
|
+
selectTool,
|
|
26
|
+
explainTool,
|
|
27
|
+
executeSqlTool,
|
|
28
|
+
exportTool,
|
|
29
|
+
copyTool,
|
|
30
|
+
insertBatchTool,
|
|
31
|
+
updateBatchTool,
|
|
32
|
+
deleteBatchTool,
|
|
33
|
+
schemaSnapshotTool,
|
|
34
|
+
schemaDiffTool,
|
|
35
|
+
prepareMigrationTool,
|
|
36
|
+
adminTool,
|
|
37
|
+
backupTool,
|
|
38
|
+
restoreTool,
|
|
39
|
+
];
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,gFAAgF;AAChF,MAAM,CAAC,MAAM,KAAK,GAAc;IAC9B,oBAAoB;IACpB,eAAe;IACf,kBAAkB;IAClB,aAAa;IACb,UAAU;IACV,WAAW;IACX,cAAc;IACd,UAAU;IACV,QAAQ;IACR,eAAe;IACf,eAAe;IACf,eAAe;IACf,kBAAkB;IAClB,cAAc;IACd,oBAAoB;IACpB,SAAS;IACT,UAAU;IACV,WAAW;CACZ,CAAC"}
|