@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.
Files changed (180) hide show
  1. package/README.md +68 -62
  2. package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
  3. package/dist/adapters/postgresql/PostgresAdapter.js +53 -3
  4. package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
  5. package/dist/adapters/postgresql/prompts/ltree.js +2 -2
  6. package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
  7. package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
  8. package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
  9. package/dist/adapters/postgresql/schemas/admin.js +10 -5
  10. package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
  11. package/dist/adapters/postgresql/schemas/backup.d.ts +8 -4
  12. package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
  13. package/dist/adapters/postgresql/schemas/backup.js +11 -4
  14. package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
  15. package/dist/adapters/postgresql/schemas/core.d.ts +53 -19
  16. package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
  17. package/dist/adapters/postgresql/schemas/core.js +61 -17
  18. package/dist/adapters/postgresql/schemas/core.js.map +1 -1
  19. package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
  20. package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
  21. package/dist/adapters/postgresql/schemas/cron.js +64 -44
  22. package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
  23. package/dist/adapters/postgresql/schemas/extensions.d.ts +168 -73
  24. package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
  25. package/dist/adapters/postgresql/schemas/extensions.js +177 -60
  26. package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
  27. package/dist/adapters/postgresql/schemas/index.d.ts +5 -5
  28. package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
  29. package/dist/adapters/postgresql/schemas/index.js +9 -7
  30. package/dist/adapters/postgresql/schemas/index.js.map +1 -1
  31. package/dist/adapters/postgresql/schemas/jsonb.d.ts +94 -42
  32. package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
  33. package/dist/adapters/postgresql/schemas/jsonb.js +101 -30
  34. package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
  35. package/dist/adapters/postgresql/schemas/monitoring.d.ts +28 -11
  36. package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
  37. package/dist/adapters/postgresql/schemas/monitoring.js +49 -24
  38. package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
  39. package/dist/adapters/postgresql/schemas/partitioning.d.ts +5 -4
  40. package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
  41. package/dist/adapters/postgresql/schemas/partitioning.js +5 -4
  42. package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
  43. package/dist/adapters/postgresql/schemas/performance.d.ts +60 -30
  44. package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
  45. package/dist/adapters/postgresql/schemas/performance.js +85 -22
  46. package/dist/adapters/postgresql/schemas/performance.js.map +1 -1
  47. package/dist/adapters/postgresql/schemas/postgis.d.ts +20 -0
  48. package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -1
  49. package/dist/adapters/postgresql/schemas/postgis.js +20 -0
  50. package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
  51. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +35 -23
  52. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
  53. package/dist/adapters/postgresql/schemas/schema-mgmt.js +69 -26
  54. package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
  55. package/dist/adapters/postgresql/schemas/stats.d.ts +33 -20
  56. package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -1
  57. package/dist/adapters/postgresql/schemas/stats.js +36 -20
  58. package/dist/adapters/postgresql/schemas/stats.js.map +1 -1
  59. package/dist/adapters/postgresql/schemas/text-search.d.ts +8 -5
  60. package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
  61. package/dist/adapters/postgresql/schemas/text-search.js +11 -4
  62. package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
  63. package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -1
  64. package/dist/adapters/postgresql/tools/admin.js +211 -140
  65. package/dist/adapters/postgresql/tools/admin.js.map +1 -1
  66. package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
  67. package/dist/adapters/postgresql/tools/backup/dump.js +360 -337
  68. package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
  69. package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
  70. package/dist/adapters/postgresql/tools/citext.js +221 -163
  71. package/dist/adapters/postgresql/tools/citext.js.map +1 -1
  72. package/dist/adapters/postgresql/tools/core/convenience.d.ts +9 -1
  73. package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
  74. package/dist/adapters/postgresql/tools/core/convenience.js +78 -11
  75. package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
  76. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
  77. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
  78. package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
  79. package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
  80. package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
  81. package/dist/adapters/postgresql/tools/core/health.js +18 -4
  82. package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
  83. package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
  84. package/dist/adapters/postgresql/tools/core/indexes.js +45 -4
  85. package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
  86. package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
  87. package/dist/adapters/postgresql/tools/core/objects.js +104 -85
  88. package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
  89. package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
  90. package/dist/adapters/postgresql/tools/core/query.js +100 -42
  91. package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
  92. package/dist/adapters/postgresql/tools/core/schemas.d.ts +51 -25
  93. package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
  94. package/dist/adapters/postgresql/tools/core/schemas.js +51 -25
  95. package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
  96. package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
  97. package/dist/adapters/postgresql/tools/core/tables.js +68 -28
  98. package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
  99. package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -1
  100. package/dist/adapters/postgresql/tools/cron.js +274 -179
  101. package/dist/adapters/postgresql/tools/cron.js.map +1 -1
  102. package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
  103. package/dist/adapters/postgresql/tools/jsonb/advanced.js +372 -284
  104. package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -1
  105. package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -1
  106. package/dist/adapters/postgresql/tools/jsonb/basic.js +617 -398
  107. package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -1
  108. package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
  109. package/dist/adapters/postgresql/tools/kcache.js +278 -246
  110. package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
  111. package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
  112. package/dist/adapters/postgresql/tools/ltree.js +121 -35
  113. package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
  114. package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -1
  115. package/dist/adapters/postgresql/tools/monitoring.js +54 -34
  116. package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
  117. package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
  118. package/dist/adapters/postgresql/tools/partitioning.js +79 -15
  119. package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
  120. package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
  121. package/dist/adapters/postgresql/tools/partman/management.js +11 -4
  122. package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
  123. package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
  124. package/dist/adapters/postgresql/tools/partman/operations.js +132 -19
  125. package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
  126. package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
  127. package/dist/adapters/postgresql/tools/performance/analysis.js +264 -160
  128. package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
  129. package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -1
  130. package/dist/adapters/postgresql/tools/performance/explain.js +61 -21
  131. package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -1
  132. package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
  133. package/dist/adapters/postgresql/tools/performance/monitoring.js +44 -7
  134. package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
  135. package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
  136. package/dist/adapters/postgresql/tools/performance/optimization.js +92 -81
  137. package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
  138. package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
  139. package/dist/adapters/postgresql/tools/performance/stats.js +124 -36
  140. package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
  141. package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
  142. package/dist/adapters/postgresql/tools/pgcrypto.js +244 -89
  143. package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
  144. package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -1
  145. package/dist/adapters/postgresql/tools/postgis/advanced.js +285 -222
  146. package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -1
  147. package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
  148. package/dist/adapters/postgresql/tools/postgis/basic.js +359 -249
  149. package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
  150. package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -1
  151. package/dist/adapters/postgresql/tools/postgis/standalone.js +135 -51
  152. package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -1
  153. package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -1
  154. package/dist/adapters/postgresql/tools/schema.js +504 -231
  155. package/dist/adapters/postgresql/tools/schema.js.map +1 -1
  156. package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
  157. package/dist/adapters/postgresql/tools/stats/advanced.js +515 -476
  158. package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
  159. package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -1
  160. package/dist/adapters/postgresql/tools/stats/basic.js +302 -293
  161. package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -1
  162. package/dist/adapters/postgresql/tools/text.d.ts.map +1 -1
  163. package/dist/adapters/postgresql/tools/text.js +392 -218
  164. package/dist/adapters/postgresql/tools/text.js.map +1 -1
  165. package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
  166. package/dist/adapters/postgresql/tools/transactions.js +157 -50
  167. package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
  168. package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
  169. package/dist/adapters/postgresql/tools/vector/advanced.js +18 -0
  170. package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
  171. package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
  172. package/dist/adapters/postgresql/tools/vector/basic.js +100 -53
  173. package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
  174. package/dist/codemode/api.js +1 -1
  175. package/dist/codemode/api.js.map +1 -1
  176. package/dist/constants/ServerInstructions.d.ts +1 -1
  177. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  178. package/dist/constants/ServerInstructions.js +45 -7
  179. package/dist/constants/ServerInstructions.js.map +1 -1
  180. 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
- const { name, authorization, ifNotExists } = CreateSchemaSchema.parse(params);
70
- // Check if schema already exists when ifNotExists is true
71
- let alreadyExisted;
72
- if (ifNotExists === true) {
73
- const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = '${name}'`);
74
- alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
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
- const ifNotExistsClause = ifNotExists ? "IF NOT EXISTS " : "";
77
- const schemaName = sanitizeIdentifier(name);
78
- const authClause = authorization
79
- ? ` AUTHORIZATION ${sanitizeIdentifier(authorization)}`
80
- : "";
81
- const sql = `CREATE SCHEMA ${ifNotExistsClause}${schemaName}${authClause}`;
82
- await adapter.executeQuery(sql);
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
- const { name, cascade, ifExists } = DropSchemaSchema.parse(params);
102
- // Check if schema exists before dropping (for accurate response)
103
- const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = '${name}'`);
104
- const existed = (existsResult.rows?.length ?? 0) > 0;
105
- const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
106
- const cascadeClause = cascade === true ? " CASCADE" : "";
107
- const schemaName = sanitizeIdentifier(name);
108
- const sql = `DROP SCHEMA ${ifExistsClause}${schemaName}${cascadeClause}`;
109
- await adapter.executeQuery(sql);
110
- return {
111
- success: true,
112
- schema: name,
113
- existed,
114
- note: existed
115
- ? undefined
116
- : `Schema '${name}' did not exist (ifExists: true)`,
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
- ? `AND n.nspname = '${parsed.schema}'`
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 = await adapter.executeQuery(sql);
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
- const { name, schema, start, increment, minValue, maxValue, cache, cycle, ownedBy, ifNotExists, } = CreateSequenceSchema.parse(params);
169
- const schemaName = schema ?? "public";
170
- // Check if sequence already exists when ifNotExists is true
171
- let alreadyExisted;
172
- if (ifNotExists === true) {
173
- 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 = '${schemaName}' AND c.relname = '${name}'`);
174
- alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
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
- const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
177
- const ifNotExistsClause = ifNotExists === true ? "IF NOT EXISTS " : "";
178
- const parts = [
179
- `CREATE SEQUENCE ${ifNotExistsClause}${schemaPrefix}${sanitizeIdentifier(name)}`,
180
- ];
181
- if (start !== undefined)
182
- parts.push(`START WITH ${String(start)}`);
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
- const { name, schema, ifExists, cascade } = DropSequenceSchema.parse(params);
221
- const schemaName = schema ?? "public";
222
- // Check if sequence exists before dropping (for accurate response)
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 = '${schemaName}' AND c.relname = '${name}'`);
224
- const existed = (existsResult.rows?.length ?? 0) > 0;
225
- const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
226
- const cascadeClause = cascade === true ? " CASCADE" : "";
227
- const sql = `DROP SEQUENCE ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
228
- await adapter.executeQuery(sql);
229
- return { success: true, sequence: `${schemaName}.${name}`, existed };
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
- ? `AND n.nspname = '${parsed.schema}'`
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 = await adapter.executeQuery(sql);
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
- const { name, schema, query, materialized, orReplace, checkOption } = CreateViewSchema.parse(params);
327
- const schemaName = schema ?? "public";
328
- // Check if view already exists when orReplace is true (for informational response)
329
- let alreadyExisted;
330
- if (orReplace === true) {
331
- const relkind = materialized === true ? "'m'" : "'v'";
332
- const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = ${relkind} AND n.nspname = '${schemaName}' AND c.relname = '${name}'`);
333
- alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
334
- }
335
- const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
336
- const replaceClause = orReplace && !materialized ? "OR REPLACE " : "";
337
- const matClause = materialized ? "MATERIALIZED " : "";
338
- const viewName = sanitizeIdentifier(name);
339
- // WITH CHECK OPTION clause (not available for materialized views)
340
- let checkClause = "";
341
- if (checkOption && checkOption !== "none" && !materialized) {
342
- checkClause = ` WITH ${checkOption.toUpperCase()} CHECK OPTION`;
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
- const sql = `CREATE ${replaceClause}${matClause}VIEW ${schemaPrefix}${viewName} AS ${query}${checkClause}`;
345
- await adapter.executeQuery(sql);
346
- const result = {
347
- success: true,
348
- view: `${schemaName}.${name}`,
349
- materialized: !!materialized,
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
- const { name, schema, materialized, ifExists, cascade } = DropViewSchema.parse(params);
370
- const schemaName = schema ?? "public";
371
- // Check if view exists before dropping (for accurate response)
372
- const relkind = materialized === true ? "'m'" : "'v'";
373
- const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = ${relkind} AND n.nspname = '${schemaName}' AND c.relname = '${name}'`);
374
- const existed = (existsResult.rows?.length ?? 0) > 0;
375
- const matClause = materialized === true ? "MATERIALIZED " : "";
376
- const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
377
- const cascadeClause = cascade === true ? " CASCADE" : "";
378
- const sql = `DROP ${matClause}VIEW ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
379
- await adapter.executeQuery(sql);
380
- return {
381
- success: true,
382
- view: `${schemaName}.${name}`,
383
- materialized: materialized ?? false,
384
- existed,
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
- // Use full schema with preprocessing for validation
401
- const parsed = ListFunctionsSchema.parse(params);
402
- const conditions = [
403
- "n.nspname NOT IN ('pg_catalog', 'information_schema')",
404
- ];
405
- if (parsed.schema !== undefined) {
406
- conditions.push(`n.nspname = '${parsed.schema}'`);
407
- }
408
- if (parsed.exclude !== undefined && parsed.exclude.length > 0) {
409
- // Expand well-known aliases (e.g. "pgvector" -> ["pgvector", "vector"])
410
- const normalizedExclude = parsed.exclude.flatMap((s) => {
411
- const alias = EXTENSION_ALIASES[s];
412
- return alias ? [s, alias] : [s];
413
- });
414
- const excludeList = normalizedExclude.map((s) => `'${s}'`).join(", ");
415
- // Exclude by schema name
416
- conditions.push(`n.nspname NOT IN (${excludeList})`);
417
- // Also exclude extension-owned functions (e.g., ltree functions in public schema)
418
- conditions.push(`NOT EXISTS (
419
- SELECT 1 FROM pg_depend d
420
- JOIN pg_extension e ON d.refobjid = e.oid
421
- WHERE d.objid = p.oid
422
- AND d.deptype = 'e'
423
- AND e.extname IN (${excludeList})
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
- if (parsed.language !== undefined) {
427
- conditions.push(`l.lanname = '${parsed.language}'`);
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
- const parsed = (params ?? {});
467
- let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema')";
468
- if (parsed.schema)
469
- whereClause += ` AND n.nspname = '${parsed.schema}'`;
470
- if (parsed.table)
471
- whereClause += ` AND c.relname = '${parsed.table}'`;
472
- const sql = `SELECT n.nspname as schema, c.relname as table_name, t.tgname as name,
473
- CASE t.tgtype::int & 2 WHEN 2 THEN 'BEFORE' ELSE 'AFTER' END as timing,
474
- array_remove(ARRAY[
475
- CASE WHEN t.tgtype::int & 4 = 4 THEN 'INSERT' END,
476
- CASE WHEN t.tgtype::int & 8 = 8 THEN 'DELETE' END,
477
- CASE WHEN t.tgtype::int & 16 = 16 THEN 'UPDATE' END,
478
- CASE WHEN t.tgtype::int & 32 = 32 THEN 'TRUNCATE' END
479
- ], NULL) as events,
480
- p.proname as function_name,
481
- t.tgenabled != 'D' as enabled
482
- FROM pg_trigger t
483
- JOIN pg_class c ON c.oid = t.tgrelid
484
- JOIN pg_namespace n ON n.oid = c.relnamespace
485
- JOIN pg_proc p ON p.oid = t.tgfoid
486
- WHERE NOT t.tgisinternal
487
- AND ${whereClause}
488
- ORDER BY n.nspname, c.relname, t.tgname`;
489
- const result = await adapter.executeQuery(sql);
490
- return { triggers: result.rows, count: result.rows?.length ?? 0 };
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
- const parsed = (params ?? {});
511
- let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema') AND con.contype != 'n'";
512
- if (parsed.schema)
513
- whereClause += ` AND n.nspname = '${parsed.schema}'`;
514
- if (parsed.table)
515
- whereClause += ` AND c.relname = '${parsed.table}'`;
516
- if (parsed.type) {
517
- const typeMap = {
518
- primary_key: "p",
519
- foreign_key: "f",
520
- unique: "u",
521
- check: "c",
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
  }