@neverinfamous/postgres-mcp 1.2.0 → 1.3.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 +68 -62
- package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
- package/dist/adapters/postgresql/PostgresAdapter.js +53 -3
- package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
- package/dist/adapters/postgresql/prompts/ltree.js +2 -2
- package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
- package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/admin.js +10 -5
- package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.d.ts +8 -4
- package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/backup.js +11 -4
- package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
- package/dist/adapters/postgresql/schemas/core.d.ts +53 -19
- package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/core.js +61 -17
- package/dist/adapters/postgresql/schemas/core.js.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
- package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/cron.js +64 -44
- package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.d.ts +168 -73
- package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/extensions.js +177 -60
- package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
- package/dist/adapters/postgresql/schemas/index.d.ts +5 -5
- package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/index.js +9 -7
- package/dist/adapters/postgresql/schemas/index.js.map +1 -1
- package/dist/adapters/postgresql/schemas/jsonb.d.ts +94 -42
- package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/jsonb.js +101 -30
- package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.d.ts +28 -11
- package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/monitoring.js +49 -24
- package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.d.ts +5 -4
- package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/partitioning.js +5 -4
- package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.d.ts +60 -30
- package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/performance.js +85 -22
- package/dist/adapters/postgresql/schemas/performance.js.map +1 -1
- package/dist/adapters/postgresql/schemas/postgis.d.ts +20 -0
- package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/postgis.js +20 -0
- package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +35 -23
- package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/schema-mgmt.js +69 -26
- package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
- package/dist/adapters/postgresql/schemas/stats.d.ts +33 -20
- package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/stats.js +36 -20
- package/dist/adapters/postgresql/schemas/stats.js.map +1 -1
- package/dist/adapters/postgresql/schemas/text-search.d.ts +8 -5
- package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
- package/dist/adapters/postgresql/schemas/text-search.js +11 -4
- package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
- package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/admin.js +211 -140
- package/dist/adapters/postgresql/tools/admin.js.map +1 -1
- package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/backup/dump.js +360 -337
- package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
- package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/citext.js +221 -163
- package/dist/adapters/postgresql/tools/citext.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/convenience.d.ts +9 -1
- package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/convenience.js +78 -11
- package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
- package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
- package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/health.js +18 -4
- package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/indexes.js +45 -4
- package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/objects.js +104 -85
- package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/query.js +100 -42
- package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.d.ts +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/schemas.js +51 -25
- package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/core/tables.js +68 -28
- package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
- package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/cron.js +274 -179
- package/dist/adapters/postgresql/tools/cron.js.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/advanced.js +372 -284
- package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/jsonb/basic.js +617 -398
- package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/kcache.js +278 -246
- package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/ltree.js +121 -35
- package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
- package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/monitoring.js +54 -34
- package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partitioning.js +79 -15
- package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partman/management.js +11 -4
- package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
- package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/partman/operations.js +132 -19
- package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/analysis.js +264 -160
- package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/explain.js +61 -21
- package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/monitoring.js +44 -7
- package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/optimization.js +92 -81
- package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/performance/stats.js +124 -36
- package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
- package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/pgcrypto.js +244 -89
- package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/advanced.js +285 -222
- package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/basic.js +359 -249
- package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/postgis/standalone.js +135 -51
- package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -1
- package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/schema.js +504 -231
- package/dist/adapters/postgresql/tools/schema.js.map +1 -1
- package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/stats/advanced.js +515 -476
- package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/stats/basic.js +302 -293
- package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -1
- package/dist/adapters/postgresql/tools/text.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/text.js +392 -218
- package/dist/adapters/postgresql/tools/text.js.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/transactions.js +157 -50
- package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/advanced.js +18 -0
- package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
- package/dist/adapters/postgresql/tools/vector/basic.js +100 -53
- package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
- package/dist/codemode/api.js +1 -1
- package/dist/codemode/api.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +45 -7
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/package.json +9 -5
|
@@ -8,6 +8,7 @@ import { z } from "zod";
|
|
|
8
8
|
import { readOnly, write, destructive } from "../../../utils/annotations.js";
|
|
9
9
|
import { getToolIcons } from "../../../utils/icons.js";
|
|
10
10
|
import { sanitizeIdentifier } from "../../../utils/identifiers.js";
|
|
11
|
+
import { formatPostgresError } from "./core/error-helpers.js";
|
|
11
12
|
import { CreateSchemaSchema, DropSchemaSchema, CreateSequenceSchemaBase, CreateSequenceSchema, DropSequenceSchemaBase, DropSequenceSchema, CreateViewSchemaBase, CreateViewSchema, DropViewSchemaBase, DropViewSchema, ListFunctionsSchemaBase, ListFunctionsSchema,
|
|
12
13
|
// Output schemas
|
|
13
14
|
ListSchemasOutputSchema, CreateSchemaOutputSchema, DropSchemaOutputSchema, ListSequencesOutputSchema, CreateSequenceOutputSchema, DropSequenceOutputSchema, ListViewsOutputSchema, CreateViewOutputSchema, DropViewOutputSchema, ListFunctionsOutputSchema, ListTriggersOutputSchema, ListConstraintsOutputSchema, } from "../schemas/index.js";
|
|
@@ -18,6 +19,7 @@ ListSchemasOutputSchema, CreateSchemaOutputSchema, DropSchemaOutputSchema, ListS
|
|
|
18
19
|
*/
|
|
19
20
|
const EXTENSION_ALIASES = {
|
|
20
21
|
pgvector: "vector",
|
|
22
|
+
vector: "vector",
|
|
21
23
|
partman: "pg_partman",
|
|
22
24
|
fuzzymatch: "fuzzystrmatch",
|
|
23
25
|
fuzzy: "fuzzystrmatch",
|
|
@@ -66,25 +68,47 @@ function createCreateSchemaTool(adapter) {
|
|
|
66
68
|
annotations: write("Create Schema"),
|
|
67
69
|
icons: getToolIcons("schema", write("Create Schema")),
|
|
68
70
|
handler: async (params, _context) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
try {
|
|
72
|
+
const { name, authorization, ifNotExists } = CreateSchemaSchema.parse(params);
|
|
73
|
+
// Check if schema already exists when ifNotExists is true
|
|
74
|
+
let alreadyExisted;
|
|
75
|
+
if (ifNotExists === true) {
|
|
76
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [name]);
|
|
77
|
+
alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
|
|
78
|
+
}
|
|
79
|
+
const ifNotExistsClause = ifNotExists ? "IF NOT EXISTS " : "";
|
|
80
|
+
const schemaName = sanitizeIdentifier(name);
|
|
81
|
+
const authClause = authorization
|
|
82
|
+
? ` AUTHORIZATION ${sanitizeIdentifier(authorization)}`
|
|
83
|
+
: "";
|
|
84
|
+
const sql = `CREATE SCHEMA ${ifNotExistsClause}${schemaName}${authClause}`;
|
|
85
|
+
try {
|
|
86
|
+
await adapter.executeQuery(sql);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: formatPostgresError(error, {
|
|
92
|
+
tool: "pg_create_schema",
|
|
93
|
+
schema: name,
|
|
94
|
+
objectType: "schema",
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const result = { success: true, schema: name };
|
|
99
|
+
if (alreadyExisted !== undefined) {
|
|
100
|
+
result["alreadyExisted"] = alreadyExisted;
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
75
103
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const result = { success: true, schema: name };
|
|
84
|
-
if (alreadyExisted !== undefined) {
|
|
85
|
-
result["alreadyExisted"] = alreadyExisted;
|
|
104
|
+
catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: error instanceof z.ZodError
|
|
108
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
109
|
+
: formatPostgresError(error, { tool: "pg_create_schema" }),
|
|
110
|
+
};
|
|
86
111
|
}
|
|
87
|
-
return result;
|
|
88
112
|
},
|
|
89
113
|
};
|
|
90
114
|
}
|
|
@@ -98,23 +122,44 @@ function createDropSchemaTool(adapter) {
|
|
|
98
122
|
annotations: destructive("Drop Schema"),
|
|
99
123
|
icons: getToolIcons("schema", destructive("Drop Schema")),
|
|
100
124
|
handler: async (params, _context) => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
try {
|
|
126
|
+
const { name, cascade, ifExists } = DropSchemaSchema.parse(params);
|
|
127
|
+
// Check if schema exists before dropping (for accurate response)
|
|
128
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [name]);
|
|
129
|
+
const existed = (existsResult.rows?.length ?? 0) > 0;
|
|
130
|
+
const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
|
|
131
|
+
const cascadeClause = cascade === true ? " CASCADE" : "";
|
|
132
|
+
const schemaName = sanitizeIdentifier(name);
|
|
133
|
+
const sql = `DROP SCHEMA ${ifExistsClause}${schemaName}${cascadeClause}`;
|
|
134
|
+
try {
|
|
135
|
+
await adapter.executeQuery(sql);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: formatPostgresError(error, {
|
|
141
|
+
tool: "pg_drop_schema",
|
|
142
|
+
schema: name,
|
|
143
|
+
}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
schema: name,
|
|
149
|
+
existed,
|
|
150
|
+
note: existed
|
|
151
|
+
? undefined
|
|
152
|
+
: `Schema '${name}' did not exist (ifExists: true)`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
error: error instanceof z.ZodError
|
|
159
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
160
|
+
: formatPostgresError(error, { tool: "pg_drop_schema" }),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
118
163
|
},
|
|
119
164
|
};
|
|
120
165
|
}
|
|
@@ -133,8 +178,10 @@ function createListSequencesTool(adapter) {
|
|
|
133
178
|
icons: getToolIcons("schema", readOnly("List Sequences")),
|
|
134
179
|
handler: async (params, _context) => {
|
|
135
180
|
const parsed = (params ?? {});
|
|
181
|
+
const queryParams = [];
|
|
136
182
|
const schemaClause = parsed.schema
|
|
137
|
-
?
|
|
183
|
+
? (queryParams.push(parsed.schema),
|
|
184
|
+
`AND n.nspname = $${String(queryParams.length)}`)
|
|
138
185
|
: "";
|
|
139
186
|
// Use subquery for owned_by to avoid duplicate rows from JOINs
|
|
140
187
|
const sql = `SELECT n.nspname as schema, c.relname as name,
|
|
@@ -150,7 +197,9 @@ function createListSequencesTool(adapter) {
|
|
|
150
197
|
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
151
198
|
${schemaClause}
|
|
152
199
|
ORDER BY n.nspname, c.relname`;
|
|
153
|
-
const result =
|
|
200
|
+
const result = queryParams.length > 0
|
|
201
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
202
|
+
: await adapter.executeQuery(sql);
|
|
154
203
|
return { sequences: result.rows, count: result.rows?.length ?? 0 };
|
|
155
204
|
},
|
|
156
205
|
};
|
|
@@ -165,44 +214,78 @@ function createCreateSequenceTool(adapter) {
|
|
|
165
214
|
annotations: write("Create Sequence"),
|
|
166
215
|
icons: getToolIcons("schema", write("Create Sequence")),
|
|
167
216
|
handler: async (params, _context) => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
217
|
+
try {
|
|
218
|
+
const { name, schema, start, increment, minValue, maxValue, cache, cycle, ownedBy, ifNotExists, } = CreateSequenceSchema.parse(params);
|
|
219
|
+
const schemaName = schema ?? "public";
|
|
220
|
+
// Check if sequence already exists when ifNotExists is true
|
|
221
|
+
let alreadyExisted;
|
|
222
|
+
if (ifNotExists === true) {
|
|
223
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'S' AND n.nspname = $1 AND c.relname = $2`, [schemaName, name]);
|
|
224
|
+
alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
|
|
225
|
+
}
|
|
226
|
+
const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
|
|
227
|
+
const ifNotExistsClause = ifNotExists === true ? "IF NOT EXISTS " : "";
|
|
228
|
+
const parts = [
|
|
229
|
+
`CREATE SEQUENCE ${ifNotExistsClause}${schemaPrefix}${sanitizeIdentifier(name)}`,
|
|
230
|
+
];
|
|
231
|
+
if (start !== undefined)
|
|
232
|
+
parts.push(`START WITH ${String(start)}`);
|
|
233
|
+
if (increment !== undefined)
|
|
234
|
+
parts.push(`INCREMENT BY ${String(increment)}`);
|
|
235
|
+
if (minValue !== undefined)
|
|
236
|
+
parts.push(`MINVALUE ${String(minValue)}`);
|
|
237
|
+
if (maxValue !== undefined)
|
|
238
|
+
parts.push(`MAXVALUE ${String(maxValue)}`);
|
|
239
|
+
if (cache !== undefined)
|
|
240
|
+
parts.push(`CACHE ${String(cache)}`);
|
|
241
|
+
if (cycle)
|
|
242
|
+
parts.push("CYCLE");
|
|
243
|
+
if (ownedBy !== undefined) {
|
|
244
|
+
// Validate and sanitize ownedBy: table.column or schema.table.column
|
|
245
|
+
const ownedByParts = ownedBy.split(".");
|
|
246
|
+
if (ownedByParts.length < 2 || ownedByParts.length > 3) {
|
|
247
|
+
return {
|
|
248
|
+
success: false,
|
|
249
|
+
error: `Invalid ownedBy format: '${ownedBy}'. Expected 'table.column' or 'schema.table.column'.`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const sanitizedOwnedBy = ownedByParts
|
|
253
|
+
.map((p) => sanitizeIdentifier(p))
|
|
254
|
+
.join(".");
|
|
255
|
+
parts.push(`OWNED BY ${sanitizedOwnedBy}`);
|
|
256
|
+
}
|
|
257
|
+
const sql = parts.join(" ");
|
|
258
|
+
try {
|
|
259
|
+
await adapter.executeQuery(sql);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
return {
|
|
263
|
+
success: false,
|
|
264
|
+
error: formatPostgresError(error, {
|
|
265
|
+
tool: "pg_create_sequence",
|
|
266
|
+
objectType: "sequence",
|
|
267
|
+
...(schema !== undefined && { schema }),
|
|
268
|
+
}),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const result = {
|
|
272
|
+
success: true,
|
|
273
|
+
sequence: `${schemaName}.${name}`,
|
|
274
|
+
ifNotExists: ifNotExists ?? false,
|
|
275
|
+
};
|
|
276
|
+
if (alreadyExisted !== undefined) {
|
|
277
|
+
result["alreadyExisted"] = alreadyExisted;
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
175
280
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (increment !== undefined)
|
|
184
|
-
parts.push(`INCREMENT BY ${String(increment)}`);
|
|
185
|
-
if (minValue !== undefined)
|
|
186
|
-
parts.push(`MINVALUE ${String(minValue)}`);
|
|
187
|
-
if (maxValue !== undefined)
|
|
188
|
-
parts.push(`MAXVALUE ${String(maxValue)}`);
|
|
189
|
-
if (cache !== undefined)
|
|
190
|
-
parts.push(`CACHE ${String(cache)}`);
|
|
191
|
-
if (cycle)
|
|
192
|
-
parts.push("CYCLE");
|
|
193
|
-
if (ownedBy !== undefined)
|
|
194
|
-
parts.push(`OWNED BY ${ownedBy}`);
|
|
195
|
-
const sql = parts.join(" ");
|
|
196
|
-
await adapter.executeQuery(sql);
|
|
197
|
-
const result = {
|
|
198
|
-
success: true,
|
|
199
|
-
sequence: `${schemaName}.${name}`,
|
|
200
|
-
ifNotExists: ifNotExists ?? false,
|
|
201
|
-
};
|
|
202
|
-
if (alreadyExisted !== undefined) {
|
|
203
|
-
result["alreadyExisted"] = alreadyExisted;
|
|
281
|
+
catch (error) {
|
|
282
|
+
return {
|
|
283
|
+
success: false,
|
|
284
|
+
error: error instanceof z.ZodError
|
|
285
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
286
|
+
: formatPostgresError(error, { tool: "pg_create_sequence" }),
|
|
287
|
+
};
|
|
204
288
|
}
|
|
205
|
-
return result;
|
|
206
289
|
},
|
|
207
290
|
};
|
|
208
291
|
}
|
|
@@ -217,16 +300,37 @@ function createDropSequenceTool(adapter) {
|
|
|
217
300
|
annotations: destructive("Drop Sequence"),
|
|
218
301
|
icons: getToolIcons("schema", destructive("Drop Sequence")),
|
|
219
302
|
handler: async (params, _context) => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
303
|
+
try {
|
|
304
|
+
const { name, schema, ifExists, cascade } = DropSequenceSchema.parse(params);
|
|
305
|
+
const schemaName = schema ?? "public";
|
|
306
|
+
// Check if sequence exists before dropping (for accurate response)
|
|
307
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'S' AND n.nspname = $1 AND c.relname = $2`, [schemaName, name]);
|
|
308
|
+
const existed = (existsResult.rows?.length ?? 0) > 0;
|
|
309
|
+
const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
|
|
310
|
+
const cascadeClause = cascade === true ? " CASCADE" : "";
|
|
311
|
+
const sql = `DROP SEQUENCE ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
|
|
312
|
+
try {
|
|
313
|
+
await adapter.executeQuery(sql);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
return {
|
|
317
|
+
success: false,
|
|
318
|
+
error: formatPostgresError(error, {
|
|
319
|
+
tool: "pg_drop_sequence",
|
|
320
|
+
...(schema !== undefined && { schema }),
|
|
321
|
+
}),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return { success: true, sequence: `${schemaName}.${name}`, existed };
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
return {
|
|
328
|
+
success: false,
|
|
329
|
+
error: error instanceof z.ZodError
|
|
330
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
331
|
+
: formatPostgresError(error, { tool: "pg_drop_sequence" }),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
230
334
|
},
|
|
231
335
|
};
|
|
232
336
|
}
|
|
@@ -252,8 +356,10 @@ function createListViewsTool(adapter) {
|
|
|
252
356
|
icons: getToolIcons("schema", readOnly("List Views")),
|
|
253
357
|
handler: async (params, _context) => {
|
|
254
358
|
const parsed = (params ?? {});
|
|
359
|
+
const queryParams = [];
|
|
255
360
|
const schemaClause = parsed.schema
|
|
256
|
-
?
|
|
361
|
+
? (queryParams.push(parsed.schema),
|
|
362
|
+
`AND n.nspname = $${String(queryParams.length)}`)
|
|
257
363
|
: "";
|
|
258
364
|
const kindClause = parsed.includeMaterialized !== false ? "IN ('v', 'm')" : "= 'v'";
|
|
259
365
|
// Default truncation: 500 chars, 0 = no truncation
|
|
@@ -271,7 +377,9 @@ function createListViewsTool(adapter) {
|
|
|
271
377
|
${schemaClause}
|
|
272
378
|
ORDER BY n.nspname, c.relname
|
|
273
379
|
${limitClause}`;
|
|
274
|
-
const result =
|
|
380
|
+
const result = queryParams.length > 0
|
|
381
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
382
|
+
: await adapter.executeQuery(sql);
|
|
275
383
|
let views = result.rows ?? [];
|
|
276
384
|
// Check if there are more results than the limit
|
|
277
385
|
const hasMore = limitVal > 0 && views.length > limitVal;
|
|
@@ -323,35 +431,57 @@ function createCreateViewTool(adapter) {
|
|
|
323
431
|
annotations: write("Create View"),
|
|
324
432
|
icons: getToolIcons("schema", write("Create View")),
|
|
325
433
|
handler: async (params, _context) => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
434
|
+
try {
|
|
435
|
+
const { name, schema, query, materialized, orReplace, checkOption } = CreateViewSchema.parse(params);
|
|
436
|
+
const schemaName = schema ?? "public";
|
|
437
|
+
// Check if view already exists when orReplace is true (for informational response)
|
|
438
|
+
let alreadyExisted;
|
|
439
|
+
if (orReplace === true) {
|
|
440
|
+
const relkind = materialized === true ? "m" : "v";
|
|
441
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = $1 AND n.nspname = $2 AND c.relname = $3`, [relkind, schemaName, name]);
|
|
442
|
+
alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
|
|
443
|
+
}
|
|
444
|
+
const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
|
|
445
|
+
const replaceClause = orReplace && !materialized ? "OR REPLACE " : "";
|
|
446
|
+
const matClause = materialized ? "MATERIALIZED " : "";
|
|
447
|
+
const viewName = sanitizeIdentifier(name);
|
|
448
|
+
// WITH CHECK OPTION clause (not available for materialized views)
|
|
449
|
+
let checkClause = "";
|
|
450
|
+
if (checkOption && checkOption !== "none" && !materialized) {
|
|
451
|
+
checkClause = ` WITH ${checkOption.toUpperCase()} CHECK OPTION`;
|
|
452
|
+
}
|
|
453
|
+
const sql = `CREATE ${replaceClause}${matClause}VIEW ${schemaPrefix}${viewName} AS ${query}${checkClause}`;
|
|
454
|
+
try {
|
|
455
|
+
await adapter.executeQuery(sql);
|
|
456
|
+
}
|
|
457
|
+
catch (error) {
|
|
458
|
+
return {
|
|
459
|
+
success: false,
|
|
460
|
+
error: formatPostgresError(error, {
|
|
461
|
+
tool: "pg_create_view",
|
|
462
|
+
objectType: "view",
|
|
463
|
+
...(schema !== undefined && { schema }),
|
|
464
|
+
}),
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
const result = {
|
|
468
|
+
success: true,
|
|
469
|
+
view: `${schemaName}.${name}`,
|
|
470
|
+
materialized: !!materialized,
|
|
471
|
+
};
|
|
472
|
+
if (alreadyExisted !== undefined) {
|
|
473
|
+
result["alreadyExisted"] = alreadyExisted;
|
|
474
|
+
}
|
|
475
|
+
return result;
|
|
343
476
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (alreadyExisted !== undefined) {
|
|
352
|
-
result["alreadyExisted"] = alreadyExisted;
|
|
477
|
+
catch (error) {
|
|
478
|
+
return {
|
|
479
|
+
success: false,
|
|
480
|
+
error: error instanceof z.ZodError
|
|
481
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
482
|
+
: formatPostgresError(error, { tool: "pg_create_view" }),
|
|
483
|
+
};
|
|
353
484
|
}
|
|
354
|
-
return result;
|
|
355
485
|
},
|
|
356
486
|
};
|
|
357
487
|
}
|
|
@@ -366,23 +496,44 @@ function createDropViewTool(adapter) {
|
|
|
366
496
|
annotations: destructive("Drop View"),
|
|
367
497
|
icons: getToolIcons("schema", destructive("Drop View")),
|
|
368
498
|
handler: async (params, _context) => {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
499
|
+
try {
|
|
500
|
+
const { name, schema, materialized, ifExists, cascade } = DropViewSchema.parse(params);
|
|
501
|
+
const schemaName = schema ?? "public";
|
|
502
|
+
// Check if view exists before dropping (for accurate response)
|
|
503
|
+
const relkind = materialized === true ? "m" : "v";
|
|
504
|
+
const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = $1 AND n.nspname = $2 AND c.relname = $3`, [relkind, schemaName, name]);
|
|
505
|
+
const existed = (existsResult.rows?.length ?? 0) > 0;
|
|
506
|
+
const matClause = materialized === true ? "MATERIALIZED " : "";
|
|
507
|
+
const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
|
|
508
|
+
const cascadeClause = cascade === true ? " CASCADE" : "";
|
|
509
|
+
const sql = `DROP ${matClause}VIEW ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
|
|
510
|
+
try {
|
|
511
|
+
await adapter.executeQuery(sql);
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
return {
|
|
515
|
+
success: false,
|
|
516
|
+
error: formatPostgresError(error, {
|
|
517
|
+
tool: "pg_drop_view",
|
|
518
|
+
...(schema !== undefined && { schema }),
|
|
519
|
+
}),
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
success: true,
|
|
524
|
+
view: `${schemaName}.${name}`,
|
|
525
|
+
materialized: materialized ?? false,
|
|
526
|
+
existed,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
return {
|
|
531
|
+
success: false,
|
|
532
|
+
error: error instanceof z.ZodError
|
|
533
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
534
|
+
: formatPostgresError(error, { tool: "pg_drop_view" }),
|
|
535
|
+
};
|
|
536
|
+
}
|
|
386
537
|
},
|
|
387
538
|
};
|
|
388
539
|
}
|
|
@@ -397,56 +548,85 @@ function createListFunctionsTool(adapter) {
|
|
|
397
548
|
annotations: readOnly("List Functions"),
|
|
398
549
|
icons: getToolIcons("schema", readOnly("List Functions")),
|
|
399
550
|
handler: async (params, _context) => {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
551
|
+
try {
|
|
552
|
+
// Use full schema with preprocessing for validation
|
|
553
|
+
const parsed = ListFunctionsSchema.parse(params);
|
|
554
|
+
const queryParams = [];
|
|
555
|
+
// Validate schema existence when filtering by schema
|
|
556
|
+
if (parsed.schema !== undefined) {
|
|
557
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
558
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
559
|
+
return {
|
|
560
|
+
success: false,
|
|
561
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const conditions = [
|
|
566
|
+
"n.nspname NOT IN ('pg_catalog', 'information_schema')",
|
|
567
|
+
];
|
|
568
|
+
if (parsed.schema !== undefined) {
|
|
569
|
+
queryParams.push(parsed.schema);
|
|
570
|
+
conditions.push(`n.nspname = $${String(queryParams.length)}`);
|
|
571
|
+
}
|
|
572
|
+
if (parsed.exclude !== undefined && parsed.exclude.length > 0) {
|
|
573
|
+
// Expand well-known aliases (e.g. "pgvector" -> ["pgvector", "vector"])
|
|
574
|
+
const normalizedExclude = parsed.exclude.flatMap((s) => {
|
|
575
|
+
const alias = EXTENSION_ALIASES[s];
|
|
576
|
+
return alias ? [s, alias] : [s];
|
|
577
|
+
});
|
|
578
|
+
const excludePlaceholders = normalizedExclude.map((s) => {
|
|
579
|
+
queryParams.push(s);
|
|
580
|
+
return `$${String(queryParams.length)}`;
|
|
581
|
+
});
|
|
582
|
+
const excludeList = excludePlaceholders.join(", ");
|
|
583
|
+
// Exclude by schema name
|
|
584
|
+
conditions.push(`n.nspname NOT IN (${excludeList})`);
|
|
585
|
+
// Also exclude extension-owned functions (e.g., ltree functions in public schema)
|
|
586
|
+
conditions.push(`NOT EXISTS (
|
|
587
|
+
SELECT 1 FROM pg_depend d
|
|
588
|
+
JOIN pg_extension e ON d.refobjid = e.oid
|
|
589
|
+
WHERE d.objid = p.oid
|
|
590
|
+
AND d.deptype = 'e'
|
|
591
|
+
AND e.extname IN (${excludeList})
|
|
592
|
+
)`);
|
|
593
|
+
}
|
|
594
|
+
if (parsed.language !== undefined) {
|
|
595
|
+
queryParams.push(parsed.language);
|
|
596
|
+
conditions.push(`l.lanname = $${String(queryParams.length)}`);
|
|
597
|
+
}
|
|
598
|
+
const limitVal = parsed.limit ?? 500;
|
|
599
|
+
const sql = `SELECT n.nspname as schema, p.proname as name,
|
|
600
|
+
pg_get_function_arguments(p.oid) as arguments,
|
|
601
|
+
pg_get_function_result(p.oid) as returns,
|
|
602
|
+
l.lanname as language,
|
|
603
|
+
p.provolatile as volatility
|
|
604
|
+
FROM pg_proc p
|
|
605
|
+
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
606
|
+
JOIN pg_language l ON l.oid = p.prolang
|
|
607
|
+
WHERE ${conditions.join(" AND ")}
|
|
608
|
+
ORDER BY n.nspname, p.proname
|
|
609
|
+
LIMIT ${String(limitVal)}`;
|
|
610
|
+
const result = queryParams.length > 0
|
|
611
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
612
|
+
: await adapter.executeQuery(sql);
|
|
613
|
+
return {
|
|
614
|
+
functions: result.rows,
|
|
615
|
+
count: result.rows?.length ?? 0,
|
|
616
|
+
limit: limitVal,
|
|
617
|
+
note: (result.rows?.length ?? 0) >= limitVal
|
|
618
|
+
? `Results limited to ${String(limitVal)}. Use 'limit' param for more, or 'exclude' to filter out extension schemas.`
|
|
619
|
+
: undefined,
|
|
620
|
+
};
|
|
425
621
|
}
|
|
426
|
-
|
|
427
|
-
|
|
622
|
+
catch (error) {
|
|
623
|
+
return {
|
|
624
|
+
success: false,
|
|
625
|
+
error: error instanceof z.ZodError
|
|
626
|
+
? error.issues.map((i) => i.message).join("; ")
|
|
627
|
+
: formatPostgresError(error, { tool: "pg_list_functions" }),
|
|
628
|
+
};
|
|
428
629
|
}
|
|
429
|
-
const limitVal = parsed.limit ?? 500;
|
|
430
|
-
const sql = `SELECT n.nspname as schema, p.proname as name,
|
|
431
|
-
pg_get_function_arguments(p.oid) as arguments,
|
|
432
|
-
pg_get_function_result(p.oid) as returns,
|
|
433
|
-
l.lanname as language,
|
|
434
|
-
p.provolatile as volatility
|
|
435
|
-
FROM pg_proc p
|
|
436
|
-
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
437
|
-
JOIN pg_language l ON l.oid = p.prolang
|
|
438
|
-
WHERE ${conditions.join(" AND ")}
|
|
439
|
-
ORDER BY n.nspname, p.proname
|
|
440
|
-
LIMIT ${String(limitVal)}`;
|
|
441
|
-
const result = await adapter.executeQuery(sql);
|
|
442
|
-
return {
|
|
443
|
-
functions: result.rows,
|
|
444
|
-
count: result.rows?.length ?? 0,
|
|
445
|
-
limit: limitVal,
|
|
446
|
-
note: (result.rows?.length ?? 0) >= limitVal
|
|
447
|
-
? `Results limited to ${String(limitVal)}. Use 'limit' param for more, or 'exclude' to filter out extension schemas.`
|
|
448
|
-
: undefined,
|
|
449
|
-
};
|
|
450
630
|
},
|
|
451
631
|
};
|
|
452
632
|
}
|
|
@@ -463,31 +643,77 @@ function createListTriggersTool(adapter) {
|
|
|
463
643
|
annotations: readOnly("List Triggers"),
|
|
464
644
|
icons: getToolIcons("schema", readOnly("List Triggers")),
|
|
465
645
|
handler: async (params, _context) => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
646
|
+
try {
|
|
647
|
+
const parsed = (params ?? {});
|
|
648
|
+
// Parse schema.table format
|
|
649
|
+
if (typeof parsed.table === "string" &&
|
|
650
|
+
parsed.table.includes(".") &&
|
|
651
|
+
!parsed.schema) {
|
|
652
|
+
const parts = parsed.table.split(".");
|
|
653
|
+
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
654
|
+
parsed.schema = parts[0];
|
|
655
|
+
parsed.table = parts[1];
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const schemaName = parsed.schema ?? "public";
|
|
659
|
+
// Validate schema existence when filtering by schema
|
|
660
|
+
if (parsed.schema) {
|
|
661
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
662
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
663
|
+
return {
|
|
664
|
+
success: false,
|
|
665
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
// Validate table existence when filtering by table
|
|
670
|
+
if (parsed.table) {
|
|
671
|
+
const tableCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2`, [schemaName, parsed.table]);
|
|
672
|
+
if ((tableCheck.rows?.length ?? 0) === 0) {
|
|
673
|
+
return {
|
|
674
|
+
success: false,
|
|
675
|
+
error: `Table '${schemaName}.${parsed.table}' not found. Use pg_list_tables to see available tables.`,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const queryParams = [];
|
|
680
|
+
let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema')";
|
|
681
|
+
if (parsed.schema) {
|
|
682
|
+
queryParams.push(parsed.schema);
|
|
683
|
+
whereClause += ` AND n.nspname = $${String(queryParams.length)}`;
|
|
684
|
+
}
|
|
685
|
+
if (parsed.table) {
|
|
686
|
+
queryParams.push(parsed.table);
|
|
687
|
+
whereClause += ` AND c.relname = $${String(queryParams.length)}`;
|
|
688
|
+
}
|
|
689
|
+
const sql = `SELECT n.nspname as schema, c.relname as table_name, t.tgname as name,
|
|
690
|
+
CASE t.tgtype::int & 2 WHEN 2 THEN 'BEFORE' ELSE 'AFTER' END as timing,
|
|
691
|
+
array_remove(ARRAY[
|
|
692
|
+
CASE WHEN t.tgtype::int & 4 = 4 THEN 'INSERT' END,
|
|
693
|
+
CASE WHEN t.tgtype::int & 8 = 8 THEN 'DELETE' END,
|
|
694
|
+
CASE WHEN t.tgtype::int & 16 = 16 THEN 'UPDATE' END,
|
|
695
|
+
CASE WHEN t.tgtype::int & 32 = 32 THEN 'TRUNCATE' END
|
|
696
|
+
], NULL) as events,
|
|
697
|
+
p.proname as function_name,
|
|
698
|
+
t.tgenabled != 'D' as enabled
|
|
699
|
+
FROM pg_trigger t
|
|
700
|
+
JOIN pg_class c ON c.oid = t.tgrelid
|
|
701
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
702
|
+
JOIN pg_proc p ON p.oid = t.tgfoid
|
|
703
|
+
WHERE NOT t.tgisinternal
|
|
704
|
+
AND ${whereClause}
|
|
705
|
+
ORDER BY n.nspname, c.relname, t.tgname`;
|
|
706
|
+
const result = queryParams.length > 0
|
|
707
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
708
|
+
: await adapter.executeQuery(sql);
|
|
709
|
+
return { triggers: result.rows, count: result.rows?.length ?? 0 };
|
|
710
|
+
}
|
|
711
|
+
catch (error) {
|
|
712
|
+
return {
|
|
713
|
+
success: false,
|
|
714
|
+
error: formatPostgresError(error, { tool: "pg_list_triggers" }),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
491
717
|
},
|
|
492
718
|
};
|
|
493
719
|
}
|
|
@@ -507,38 +733,85 @@ function createListConstraintsTool(adapter) {
|
|
|
507
733
|
annotations: readOnly("List Constraints"),
|
|
508
734
|
icons: getToolIcons("schema", readOnly("List Constraints")),
|
|
509
735
|
handler: async (params, _context) => {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
736
|
+
try {
|
|
737
|
+
const parsed = (params ?? {});
|
|
738
|
+
// Parse schema.table format
|
|
739
|
+
if (typeof parsed.table === "string" &&
|
|
740
|
+
parsed.table.includes(".") &&
|
|
741
|
+
!parsed.schema) {
|
|
742
|
+
const parts = parsed.table.split(".");
|
|
743
|
+
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
744
|
+
parsed.schema = parts[0];
|
|
745
|
+
parsed.table = parts[1];
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
const schemaName = parsed.schema ?? "public";
|
|
749
|
+
// Validate schema existence when filtering by schema
|
|
750
|
+
if (parsed.schema) {
|
|
751
|
+
const schemaCheck = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = $1`, [parsed.schema]);
|
|
752
|
+
if ((schemaCheck.rows?.length ?? 0) === 0) {
|
|
753
|
+
return {
|
|
754
|
+
success: false,
|
|
755
|
+
error: `Schema '${parsed.schema}' does not exist. Use pg_list_schemas to see available schemas.`,
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
// Validate table existence when filtering by table
|
|
760
|
+
if (parsed.table) {
|
|
761
|
+
const tableCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2`, [schemaName, parsed.table]);
|
|
762
|
+
if ((tableCheck.rows?.length ?? 0) === 0) {
|
|
763
|
+
return {
|
|
764
|
+
success: false,
|
|
765
|
+
error: `Table '${schemaName}.${parsed.table}' not found. Use pg_list_tables to see available tables.`,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const queryParams = [];
|
|
770
|
+
let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema') AND con.contype != 'n'";
|
|
771
|
+
if (parsed.schema) {
|
|
772
|
+
queryParams.push(parsed.schema);
|
|
773
|
+
whereClause += ` AND n.nspname = $${String(queryParams.length)}`;
|
|
774
|
+
}
|
|
775
|
+
if (parsed.table) {
|
|
776
|
+
queryParams.push(parsed.table);
|
|
777
|
+
whereClause += ` AND c.relname = $${String(queryParams.length)}`;
|
|
778
|
+
}
|
|
779
|
+
if (parsed.type) {
|
|
780
|
+
const typeMap = {
|
|
781
|
+
primary_key: "p",
|
|
782
|
+
foreign_key: "f",
|
|
783
|
+
unique: "u",
|
|
784
|
+
check: "c",
|
|
785
|
+
};
|
|
786
|
+
queryParams.push(typeMap[parsed.type] ?? "");
|
|
787
|
+
whereClause += ` AND con.contype = $${String(queryParams.length)}`;
|
|
788
|
+
}
|
|
789
|
+
const sql = `SELECT n.nspname as schema, c.relname as table_name, con.conname as name,
|
|
790
|
+
CASE con.contype
|
|
791
|
+
WHEN 'p' THEN 'primary_key'
|
|
792
|
+
WHEN 'f' THEN 'foreign_key'
|
|
793
|
+
WHEN 'u' THEN 'unique'
|
|
794
|
+
WHEN 'c' THEN 'check'
|
|
795
|
+
WHEN 'n' THEN 'not_null'
|
|
796
|
+
ELSE con.contype
|
|
797
|
+
END as type,
|
|
798
|
+
pg_get_constraintdef(con.oid) as definition
|
|
799
|
+
FROM pg_constraint con
|
|
800
|
+
JOIN pg_class c ON c.oid = con.conrelid
|
|
801
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
802
|
+
WHERE ${whereClause}
|
|
803
|
+
ORDER BY n.nspname, c.relname, con.conname`;
|
|
804
|
+
const result = queryParams.length > 0
|
|
805
|
+
? await adapter.executeQuery(sql, queryParams)
|
|
806
|
+
: await adapter.executeQuery(sql);
|
|
807
|
+
return { constraints: result.rows, count: result.rows?.length ?? 0 };
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
return {
|
|
811
|
+
success: false,
|
|
812
|
+
error: formatPostgresError(error, { tool: "pg_list_constraints" }),
|
|
522
813
|
};
|
|
523
|
-
whereClause += ` AND con.contype = '${typeMap[parsed.type] ?? ""}'`;
|
|
524
814
|
}
|
|
525
|
-
const sql = `SELECT n.nspname as schema, c.relname as table_name, con.conname as name,
|
|
526
|
-
CASE con.contype
|
|
527
|
-
WHEN 'p' THEN 'primary_key'
|
|
528
|
-
WHEN 'f' THEN 'foreign_key'
|
|
529
|
-
WHEN 'u' THEN 'unique'
|
|
530
|
-
WHEN 'c' THEN 'check'
|
|
531
|
-
WHEN 'n' THEN 'not_null'
|
|
532
|
-
ELSE con.contype
|
|
533
|
-
END as type,
|
|
534
|
-
pg_get_constraintdef(con.oid) as definition
|
|
535
|
-
FROM pg_constraint con
|
|
536
|
-
JOIN pg_class c ON c.oid = con.conrelid
|
|
537
|
-
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
538
|
-
WHERE ${whereClause}
|
|
539
|
-
ORDER BY n.nspname, c.relname, con.conname`;
|
|
540
|
-
const result = await adapter.executeQuery(sql);
|
|
541
|
-
return { constraints: result.rows, count: result.rows?.length ?? 0 };
|
|
542
815
|
},
|
|
543
816
|
};
|
|
544
817
|
}
|