@neverinfamous/postgres-mcp 1.1.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 (243) hide show
  1. package/README.md +95 -81
  2. package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
  3. package/dist/__tests__/mocks/adapter.js +0 -1
  4. package/dist/__tests__/mocks/adapter.js.map +1 -1
  5. package/dist/__tests__/mocks/pool.d.ts.map +1 -1
  6. package/dist/__tests__/mocks/pool.js +0 -1
  7. package/dist/__tests__/mocks/pool.js.map +1 -1
  8. package/dist/adapters/DatabaseAdapter.js +1 -1
  9. package/dist/adapters/DatabaseAdapter.js.map +1 -1
  10. package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
  11. package/dist/adapters/postgresql/PostgresAdapter.js +78 -8
  12. package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
  13. package/dist/adapters/postgresql/prompts/backup.d.ts.map +1 -1
  14. package/dist/adapters/postgresql/prompts/backup.js +2 -3
  15. package/dist/adapters/postgresql/prompts/backup.js.map +1 -1
  16. package/dist/adapters/postgresql/prompts/citext.d.ts.map +1 -1
  17. package/dist/adapters/postgresql/prompts/citext.js +3 -4
  18. package/dist/adapters/postgresql/prompts/citext.js.map +1 -1
  19. package/dist/adapters/postgresql/prompts/extensionSetup.d.ts.map +1 -1
  20. package/dist/adapters/postgresql/prompts/extensionSetup.js +2 -3
  21. package/dist/adapters/postgresql/prompts/extensionSetup.js.map +1 -1
  22. package/dist/adapters/postgresql/prompts/health.d.ts.map +1 -1
  23. package/dist/adapters/postgresql/prompts/health.js +2 -3
  24. package/dist/adapters/postgresql/prompts/health.js.map +1 -1
  25. package/dist/adapters/postgresql/prompts/index.js +20 -27
  26. package/dist/adapters/postgresql/prompts/index.js.map +1 -1
  27. package/dist/adapters/postgresql/prompts/indexTuning.d.ts.map +1 -1
  28. package/dist/adapters/postgresql/prompts/indexTuning.js +2 -3
  29. package/dist/adapters/postgresql/prompts/indexTuning.js.map +1 -1
  30. package/dist/adapters/postgresql/prompts/kcache.d.ts.map +1 -1
  31. package/dist/adapters/postgresql/prompts/kcache.js +3 -4
  32. package/dist/adapters/postgresql/prompts/kcache.js.map +1 -1
  33. package/dist/adapters/postgresql/prompts/ltree.d.ts.map +1 -1
  34. package/dist/adapters/postgresql/prompts/ltree.js +5 -6
  35. package/dist/adapters/postgresql/prompts/ltree.js.map +1 -1
  36. package/dist/adapters/postgresql/prompts/partman.d.ts.map +1 -1
  37. package/dist/adapters/postgresql/prompts/partman.js +2 -3
  38. package/dist/adapters/postgresql/prompts/partman.js.map +1 -1
  39. package/dist/adapters/postgresql/prompts/pgcron.d.ts.map +1 -1
  40. package/dist/adapters/postgresql/prompts/pgcron.js +2 -3
  41. package/dist/adapters/postgresql/prompts/pgcron.js.map +1 -1
  42. package/dist/adapters/postgresql/prompts/pgcrypto.d.ts.map +1 -1
  43. package/dist/adapters/postgresql/prompts/pgcrypto.js +3 -4
  44. package/dist/adapters/postgresql/prompts/pgcrypto.js.map +1 -1
  45. package/dist/adapters/postgresql/prompts/pgvector.d.ts.map +1 -1
  46. package/dist/adapters/postgresql/prompts/pgvector.js +3 -4
  47. package/dist/adapters/postgresql/prompts/pgvector.js.map +1 -1
  48. package/dist/adapters/postgresql/prompts/postgis.d.ts.map +1 -1
  49. package/dist/adapters/postgresql/prompts/postgis.js +2 -3
  50. package/dist/adapters/postgresql/prompts/postgis.js.map +1 -1
  51. package/dist/adapters/postgresql/schemas/admin.d.ts +10 -5
  52. package/dist/adapters/postgresql/schemas/admin.d.ts.map +1 -1
  53. package/dist/adapters/postgresql/schemas/admin.js +10 -5
  54. package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
  55. package/dist/adapters/postgresql/schemas/backup.d.ts +8 -4
  56. package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
  57. package/dist/adapters/postgresql/schemas/backup.js +11 -4
  58. package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
  59. package/dist/adapters/postgresql/schemas/core.d.ts +54 -19
  60. package/dist/adapters/postgresql/schemas/core.d.ts.map +1 -1
  61. package/dist/adapters/postgresql/schemas/core.js +65 -17
  62. package/dist/adapters/postgresql/schemas/core.js.map +1 -1
  63. package/dist/adapters/postgresql/schemas/cron.d.ts +51 -32
  64. package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
  65. package/dist/adapters/postgresql/schemas/cron.js +64 -44
  66. package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
  67. package/dist/adapters/postgresql/schemas/extensions.d.ts +168 -73
  68. package/dist/adapters/postgresql/schemas/extensions.d.ts.map +1 -1
  69. package/dist/adapters/postgresql/schemas/extensions.js +179 -62
  70. package/dist/adapters/postgresql/schemas/extensions.js.map +1 -1
  71. package/dist/adapters/postgresql/schemas/index.d.ts +5 -5
  72. package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
  73. package/dist/adapters/postgresql/schemas/index.js +9 -7
  74. package/dist/adapters/postgresql/schemas/index.js.map +1 -1
  75. package/dist/adapters/postgresql/schemas/jsonb.d.ts +94 -42
  76. package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +1 -1
  77. package/dist/adapters/postgresql/schemas/jsonb.js +101 -30
  78. package/dist/adapters/postgresql/schemas/jsonb.js.map +1 -1
  79. package/dist/adapters/postgresql/schemas/monitoring.d.ts +28 -11
  80. package/dist/adapters/postgresql/schemas/monitoring.d.ts.map +1 -1
  81. package/dist/adapters/postgresql/schemas/monitoring.js +49 -24
  82. package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
  83. package/dist/adapters/postgresql/schemas/partitioning.d.ts +15 -11
  84. package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
  85. package/dist/adapters/postgresql/schemas/partitioning.js +17 -13
  86. package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
  87. package/dist/adapters/postgresql/schemas/performance.d.ts +62 -31
  88. package/dist/adapters/postgresql/schemas/performance.d.ts.map +1 -1
  89. package/dist/adapters/postgresql/schemas/performance.js +86 -24
  90. package/dist/adapters/postgresql/schemas/performance.js.map +1 -1
  91. package/dist/adapters/postgresql/schemas/postgis.d.ts +20 -0
  92. package/dist/adapters/postgresql/schemas/postgis.d.ts.map +1 -1
  93. package/dist/adapters/postgresql/schemas/postgis.js +20 -3
  94. package/dist/adapters/postgresql/schemas/postgis.js.map +1 -1
  95. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +35 -23
  96. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
  97. package/dist/adapters/postgresql/schemas/schema-mgmt.js +69 -26
  98. package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
  99. package/dist/adapters/postgresql/schemas/stats.d.ts +33 -20
  100. package/dist/adapters/postgresql/schemas/stats.d.ts.map +1 -1
  101. package/dist/adapters/postgresql/schemas/stats.js +36 -20
  102. package/dist/adapters/postgresql/schemas/stats.js.map +1 -1
  103. package/dist/adapters/postgresql/schemas/text-search.d.ts +8 -5
  104. package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
  105. package/dist/adapters/postgresql/schemas/text-search.js +15 -5
  106. package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
  107. package/dist/adapters/postgresql/tools/admin.d.ts.map +1 -1
  108. package/dist/adapters/postgresql/tools/admin.js +211 -140
  109. package/dist/adapters/postgresql/tools/admin.js.map +1 -1
  110. package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
  111. package/dist/adapters/postgresql/tools/backup/dump.js +410 -387
  112. package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
  113. package/dist/adapters/postgresql/tools/backup/planning.d.ts.map +1 -1
  114. package/dist/adapters/postgresql/tools/backup/planning.js +175 -172
  115. package/dist/adapters/postgresql/tools/backup/planning.js.map +1 -1
  116. package/dist/adapters/postgresql/tools/citext.d.ts.map +1 -1
  117. package/dist/adapters/postgresql/tools/citext.js +221 -163
  118. package/dist/adapters/postgresql/tools/citext.js.map +1 -1
  119. package/dist/adapters/postgresql/tools/core/convenience.d.ts +9 -1
  120. package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
  121. package/dist/adapters/postgresql/tools/core/convenience.js +96 -9
  122. package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
  123. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +48 -0
  124. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -0
  125. package/dist/adapters/postgresql/tools/core/error-helpers.js +256 -0
  126. package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -0
  127. package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
  128. package/dist/adapters/postgresql/tools/core/health.js +23 -6
  129. package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
  130. package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
  131. package/dist/adapters/postgresql/tools/core/indexes.js +45 -4
  132. package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
  133. package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
  134. package/dist/adapters/postgresql/tools/core/objects.js +104 -85
  135. package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
  136. package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
  137. package/dist/adapters/postgresql/tools/core/query.js +100 -42
  138. package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
  139. package/dist/adapters/postgresql/tools/core/schemas.d.ts +52 -25
  140. package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
  141. package/dist/adapters/postgresql/tools/core/schemas.js +55 -25
  142. package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
  143. package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
  144. package/dist/adapters/postgresql/tools/core/tables.js +74 -30
  145. package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
  146. package/dist/adapters/postgresql/tools/cron.d.ts.map +1 -1
  147. package/dist/adapters/postgresql/tools/cron.js +274 -179
  148. package/dist/adapters/postgresql/tools/cron.js.map +1 -1
  149. package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +1 -1
  150. package/dist/adapters/postgresql/tools/jsonb/advanced.js +372 -284
  151. package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +1 -1
  152. package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +1 -1
  153. package/dist/adapters/postgresql/tools/jsonb/basic.js +617 -398
  154. package/dist/adapters/postgresql/tools/jsonb/basic.js.map +1 -1
  155. package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
  156. package/dist/adapters/postgresql/tools/kcache.js +282 -220
  157. package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
  158. package/dist/adapters/postgresql/tools/ltree.d.ts.map +1 -1
  159. package/dist/adapters/postgresql/tools/ltree.js +126 -35
  160. package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
  161. package/dist/adapters/postgresql/tools/monitoring.d.ts.map +1 -1
  162. package/dist/adapters/postgresql/tools/monitoring.js +59 -40
  163. package/dist/adapters/postgresql/tools/monitoring.js.map +1 -1
  164. package/dist/adapters/postgresql/tools/partitioning.d.ts.map +1 -1
  165. package/dist/adapters/postgresql/tools/partitioning.js +150 -15
  166. package/dist/adapters/postgresql/tools/partitioning.js.map +1 -1
  167. package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
  168. package/dist/adapters/postgresql/tools/partman/management.js +12 -5
  169. package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
  170. package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
  171. package/dist/adapters/postgresql/tools/partman/operations.js +135 -22
  172. package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
  173. package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
  174. package/dist/adapters/postgresql/tools/performance/analysis.js +264 -160
  175. package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
  176. package/dist/adapters/postgresql/tools/performance/explain.d.ts.map +1 -1
  177. package/dist/adapters/postgresql/tools/performance/explain.js +61 -21
  178. package/dist/adapters/postgresql/tools/performance/explain.js.map +1 -1
  179. package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
  180. package/dist/adapters/postgresql/tools/performance/monitoring.js +44 -7
  181. package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
  182. package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
  183. package/dist/adapters/postgresql/tools/performance/optimization.js +92 -81
  184. package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
  185. package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
  186. package/dist/adapters/postgresql/tools/performance/stats.js +128 -37
  187. package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
  188. package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
  189. package/dist/adapters/postgresql/tools/pgcrypto.js +242 -87
  190. package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
  191. package/dist/adapters/postgresql/tools/postgis/advanced.d.ts.map +1 -1
  192. package/dist/adapters/postgresql/tools/postgis/advanced.js +293 -201
  193. package/dist/adapters/postgresql/tools/postgis/advanced.js.map +1 -1
  194. package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
  195. package/dist/adapters/postgresql/tools/postgis/basic.js +359 -249
  196. package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
  197. package/dist/adapters/postgresql/tools/postgis/standalone.d.ts.map +1 -1
  198. package/dist/adapters/postgresql/tools/postgis/standalone.js +135 -51
  199. package/dist/adapters/postgresql/tools/postgis/standalone.js.map +1 -1
  200. package/dist/adapters/postgresql/tools/schema.d.ts.map +1 -1
  201. package/dist/adapters/postgresql/tools/schema.js +515 -226
  202. package/dist/adapters/postgresql/tools/schema.js.map +1 -1
  203. package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
  204. package/dist/adapters/postgresql/tools/stats/advanced.js +515 -476
  205. package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
  206. package/dist/adapters/postgresql/tools/stats/basic.d.ts.map +1 -1
  207. package/dist/adapters/postgresql/tools/stats/basic.js +302 -293
  208. package/dist/adapters/postgresql/tools/stats/basic.js.map +1 -1
  209. package/dist/adapters/postgresql/tools/text.d.ts.map +1 -1
  210. package/dist/adapters/postgresql/tools/text.js +398 -220
  211. package/dist/adapters/postgresql/tools/text.js.map +1 -1
  212. package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
  213. package/dist/adapters/postgresql/tools/transactions.js +157 -50
  214. package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
  215. package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +1 -1
  216. package/dist/adapters/postgresql/tools/vector/advanced.js +70 -38
  217. package/dist/adapters/postgresql/tools/vector/advanced.js.map +1 -1
  218. package/dist/adapters/postgresql/tools/vector/basic.d.ts +8 -0
  219. package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +1 -1
  220. package/dist/adapters/postgresql/tools/vector/basic.js +194 -82
  221. package/dist/adapters/postgresql/tools/vector/basic.js.map +1 -1
  222. package/dist/cli/args.d.ts +2 -0
  223. package/dist/cli/args.d.ts.map +1 -1
  224. package/dist/cli/args.js +15 -0
  225. package/dist/cli/args.js.map +1 -1
  226. package/dist/cli.js +7 -6
  227. package/dist/cli.js.map +1 -1
  228. package/dist/codemode/api.d.ts.map +1 -1
  229. package/dist/codemode/api.js +4 -3
  230. package/dist/codemode/api.js.map +1 -1
  231. package/dist/constants/ServerInstructions.d.ts +1 -1
  232. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  233. package/dist/constants/ServerInstructions.js +76 -34
  234. package/dist/constants/ServerInstructions.js.map +1 -1
  235. package/dist/filtering/ToolConstants.d.ts +29 -13
  236. package/dist/filtering/ToolConstants.d.ts.map +1 -1
  237. package/dist/filtering/ToolConstants.js +44 -27
  238. package/dist/filtering/ToolConstants.js.map +1 -1
  239. package/dist/utils/logger.js +2 -2
  240. package/dist/utils/logger.js.map +1 -1
  241. package/dist/utils/progress-utils.js +1 -1
  242. package/dist/utils/progress-utils.js.map +1 -1
  243. package/package.json +13 -9
@@ -8,9 +8,22 @@ 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";
15
+ /**
16
+ * Well-known aliases for PostgreSQL extension names.
17
+ * Users naturally write "pgvector" but the extension registers as "vector".
18
+ * This map normalizes user input so exclude filters work correctly.
19
+ */
20
+ const EXTENSION_ALIASES = {
21
+ pgvector: "vector",
22
+ vector: "vector",
23
+ partman: "pg_partman",
24
+ fuzzymatch: "fuzzystrmatch",
25
+ fuzzy: "fuzzystrmatch",
26
+ };
14
27
  /**
15
28
  * Get all schema management tools
16
29
  */
@@ -55,25 +68,47 @@ function createCreateSchemaTool(adapter) {
55
68
  annotations: write("Create Schema"),
56
69
  icons: getToolIcons("schema", write("Create Schema")),
57
70
  handler: async (params, _context) => {
58
- const { name, authorization, ifNotExists } = CreateSchemaSchema.parse(params);
59
- // Check if schema already exists when ifNotExists is true
60
- let alreadyExisted;
61
- if (ifNotExists === true) {
62
- const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = '${name}'`);
63
- 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;
64
103
  }
65
- const ifNotExistsClause = ifNotExists ? "IF NOT EXISTS " : "";
66
- const schemaName = sanitizeIdentifier(name);
67
- const authClause = authorization
68
- ? ` AUTHORIZATION ${sanitizeIdentifier(authorization)}`
69
- : "";
70
- const sql = `CREATE SCHEMA ${ifNotExistsClause}${schemaName}${authClause}`;
71
- await adapter.executeQuery(sql);
72
- const result = { success: true, schema: name };
73
- if (alreadyExisted !== undefined) {
74
- 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
+ };
75
111
  }
76
- return result;
77
112
  },
78
113
  };
79
114
  }
@@ -87,23 +122,44 @@ function createDropSchemaTool(adapter) {
87
122
  annotations: destructive("Drop Schema"),
88
123
  icons: getToolIcons("schema", destructive("Drop Schema")),
89
124
  handler: async (params, _context) => {
90
- const { name, cascade, ifExists } = DropSchemaSchema.parse(params);
91
- // Check if schema exists before dropping (for accurate response)
92
- const existsResult = await adapter.executeQuery(`SELECT 1 FROM pg_namespace WHERE nspname = '${name}'`);
93
- const existed = (existsResult.rows?.length ?? 0) > 0;
94
- const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
95
- const cascadeClause = cascade === true ? " CASCADE" : "";
96
- const schemaName = sanitizeIdentifier(name);
97
- const sql = `DROP SCHEMA ${ifExistsClause}${schemaName}${cascadeClause}`;
98
- await adapter.executeQuery(sql);
99
- return {
100
- success: true,
101
- dropped: existed ? name : null,
102
- existed,
103
- note: existed
104
- ? undefined
105
- : `Schema '${name}' did not exist (ifExists: true)`,
106
- };
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
+ }
107
163
  },
108
164
  };
109
165
  }
@@ -122,8 +178,10 @@ function createListSequencesTool(adapter) {
122
178
  icons: getToolIcons("schema", readOnly("List Sequences")),
123
179
  handler: async (params, _context) => {
124
180
  const parsed = (params ?? {});
181
+ const queryParams = [];
125
182
  const schemaClause = parsed.schema
126
- ? `AND n.nspname = '${parsed.schema}'`
183
+ ? (queryParams.push(parsed.schema),
184
+ `AND n.nspname = $${String(queryParams.length)}`)
127
185
  : "";
128
186
  // Use subquery for owned_by to avoid duplicate rows from JOINs
129
187
  const sql = `SELECT n.nspname as schema, c.relname as name,
@@ -139,7 +197,9 @@ function createListSequencesTool(adapter) {
139
197
  AND n.nspname NOT IN ('pg_catalog', 'information_schema')
140
198
  ${schemaClause}
141
199
  ORDER BY n.nspname, c.relname`;
142
- const result = await adapter.executeQuery(sql);
200
+ const result = queryParams.length > 0
201
+ ? await adapter.executeQuery(sql, queryParams)
202
+ : await adapter.executeQuery(sql);
143
203
  return { sequences: result.rows, count: result.rows?.length ?? 0 };
144
204
  },
145
205
  };
@@ -154,44 +214,78 @@ function createCreateSequenceTool(adapter) {
154
214
  annotations: write("Create Sequence"),
155
215
  icons: getToolIcons("schema", write("Create Sequence")),
156
216
  handler: async (params, _context) => {
157
- const { name, schema, start, increment, minValue, maxValue, cache, cycle, ownedBy, ifNotExists, } = CreateSequenceSchema.parse(params);
158
- const schemaName = schema ?? "public";
159
- // Check if sequence already exists when ifNotExists is true
160
- let alreadyExisted;
161
- if (ifNotExists === true) {
162
- 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}'`);
163
- 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;
164
280
  }
165
- const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
166
- const ifNotExistsClause = ifNotExists === true ? "IF NOT EXISTS " : "";
167
- const parts = [
168
- `CREATE SEQUENCE ${ifNotExistsClause}${schemaPrefix}${sanitizeIdentifier(name)}`,
169
- ];
170
- if (start !== undefined)
171
- parts.push(`START WITH ${String(start)}`);
172
- if (increment !== undefined)
173
- parts.push(`INCREMENT BY ${String(increment)}`);
174
- if (minValue !== undefined)
175
- parts.push(`MINVALUE ${String(minValue)}`);
176
- if (maxValue !== undefined)
177
- parts.push(`MAXVALUE ${String(maxValue)}`);
178
- if (cache !== undefined)
179
- parts.push(`CACHE ${String(cache)}`);
180
- if (cycle)
181
- parts.push("CYCLE");
182
- if (ownedBy !== undefined)
183
- parts.push(`OWNED BY ${ownedBy}`);
184
- const sql = parts.join(" ");
185
- await adapter.executeQuery(sql);
186
- const result = {
187
- success: true,
188
- sequence: `${schemaName}.${name}`,
189
- ifNotExists: ifNotExists ?? false,
190
- };
191
- if (alreadyExisted !== undefined) {
192
- 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
+ };
193
288
  }
194
- return result;
195
289
  },
196
290
  };
197
291
  }
@@ -206,16 +300,37 @@ function createDropSequenceTool(adapter) {
206
300
  annotations: destructive("Drop Sequence"),
207
301
  icons: getToolIcons("schema", destructive("Drop Sequence")),
208
302
  handler: async (params, _context) => {
209
- const { name, schema, ifExists, cascade } = DropSequenceSchema.parse(params);
210
- const schemaName = schema ?? "public";
211
- // Check if sequence exists before dropping (for accurate response)
212
- 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}'`);
213
- const existed = (existsResult.rows?.length ?? 0) > 0;
214
- const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
215
- const cascadeClause = cascade === true ? " CASCADE" : "";
216
- const sql = `DROP SEQUENCE ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
217
- await adapter.executeQuery(sql);
218
- 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
+ }
219
334
  },
220
335
  };
221
336
  }
@@ -241,8 +356,10 @@ function createListViewsTool(adapter) {
241
356
  icons: getToolIcons("schema", readOnly("List Views")),
242
357
  handler: async (params, _context) => {
243
358
  const parsed = (params ?? {});
359
+ const queryParams = [];
244
360
  const schemaClause = parsed.schema
245
- ? `AND n.nspname = '${parsed.schema}'`
361
+ ? (queryParams.push(parsed.schema),
362
+ `AND n.nspname = $${String(queryParams.length)}`)
246
363
  : "";
247
364
  const kindClause = parsed.includeMaterialized !== false ? "IN ('v', 'm')" : "= 'v'";
248
365
  // Default truncation: 500 chars, 0 = no truncation
@@ -260,7 +377,9 @@ function createListViewsTool(adapter) {
260
377
  ${schemaClause}
261
378
  ORDER BY n.nspname, c.relname
262
379
  ${limitClause}`;
263
- const result = await adapter.executeQuery(sql);
380
+ const result = queryParams.length > 0
381
+ ? await adapter.executeQuery(sql, queryParams)
382
+ : await adapter.executeQuery(sql);
264
383
  let views = result.rows ?? [];
265
384
  // Check if there are more results than the limit
266
385
  const hasMore = limitVal > 0 && views.length > limitVal;
@@ -312,35 +431,57 @@ function createCreateViewTool(adapter) {
312
431
  annotations: write("Create View"),
313
432
  icons: getToolIcons("schema", write("Create View")),
314
433
  handler: async (params, _context) => {
315
- const { name, schema, query, materialized, orReplace, checkOption } = CreateViewSchema.parse(params);
316
- const schemaName = schema ?? "public";
317
- // Check if view already exists when orReplace is true (for informational response)
318
- let alreadyExisted;
319
- if (orReplace === true) {
320
- const relkind = materialized === true ? "'m'" : "'v'";
321
- 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}'`);
322
- alreadyExisted = (existsResult.rows?.length ?? 0) > 0;
323
- }
324
- const schemaPrefix = schema ? `${sanitizeIdentifier(schema)}.` : "";
325
- const replaceClause = orReplace && !materialized ? "OR REPLACE " : "";
326
- const matClause = materialized ? "MATERIALIZED " : "";
327
- const viewName = sanitizeIdentifier(name);
328
- // WITH CHECK OPTION clause (not available for materialized views)
329
- let checkClause = "";
330
- if (checkOption && checkOption !== "none" && !materialized) {
331
- 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;
332
476
  }
333
- const sql = `CREATE ${replaceClause}${matClause}VIEW ${schemaPrefix}${viewName} AS ${query}${checkClause}`;
334
- await adapter.executeQuery(sql);
335
- const result = {
336
- success: true,
337
- view: `${schemaName}.${name}`,
338
- materialized: !!materialized,
339
- };
340
- if (alreadyExisted !== undefined) {
341
- 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
+ };
342
484
  }
343
- return result;
344
485
  },
345
486
  };
346
487
  }
@@ -355,23 +496,44 @@ function createDropViewTool(adapter) {
355
496
  annotations: destructive("Drop View"),
356
497
  icons: getToolIcons("schema", destructive("Drop View")),
357
498
  handler: async (params, _context) => {
358
- const { name, schema, materialized, ifExists, cascade } = DropViewSchema.parse(params);
359
- const schemaName = schema ?? "public";
360
- // Check if view exists before dropping (for accurate response)
361
- const relkind = materialized === true ? "'m'" : "'v'";
362
- 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}'`);
363
- const existed = (existsResult.rows?.length ?? 0) > 0;
364
- const matClause = materialized === true ? "MATERIALIZED " : "";
365
- const ifExistsClause = ifExists === true ? "IF EXISTS " : "";
366
- const cascadeClause = cascade === true ? " CASCADE" : "";
367
- const sql = `DROP ${matClause}VIEW ${ifExistsClause}"${schemaName}"."${name}"${cascadeClause}`;
368
- await adapter.executeQuery(sql);
369
- return {
370
- success: true,
371
- view: `${schemaName}.${name}`,
372
- materialized: materialized ?? false,
373
- existed,
374
- };
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
+ }
375
537
  },
376
538
  };
377
539
  }
@@ -386,51 +548,85 @@ function createListFunctionsTool(adapter) {
386
548
  annotations: readOnly("List Functions"),
387
549
  icons: getToolIcons("schema", readOnly("List Functions")),
388
550
  handler: async (params, _context) => {
389
- // Use full schema with preprocessing for validation
390
- const parsed = ListFunctionsSchema.parse(params);
391
- const conditions = [
392
- "n.nspname NOT IN ('pg_catalog', 'information_schema')",
393
- ];
394
- if (parsed.schema !== undefined) {
395
- conditions.push(`n.nspname = '${parsed.schema}'`);
396
- }
397
- if (parsed.exclude !== undefined && parsed.exclude.length > 0) {
398
- const excludeList = parsed.exclude.map((s) => `'${s}'`).join(", ");
399
- // Exclude by schema name
400
- conditions.push(`n.nspname NOT IN (${excludeList})`);
401
- // Also exclude extension-owned functions (e.g., ltree functions in public schema)
402
- conditions.push(`NOT EXISTS (
403
- SELECT 1 FROM pg_depend d
404
- JOIN pg_extension e ON d.refobjid = e.oid
405
- WHERE d.objid = p.oid
406
- AND d.deptype = 'e'
407
- AND e.extname IN (${excludeList})
408
- )`);
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
+ };
409
621
  }
410
- if (parsed.language !== undefined) {
411
- 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
+ };
412
629
  }
413
- const limitVal = parsed.limit ?? 500;
414
- const sql = `SELECT n.nspname as schema, p.proname as name,
415
- pg_get_function_arguments(p.oid) as arguments,
416
- pg_get_function_result(p.oid) as returns,
417
- l.lanname as language,
418
- p.provolatile as volatility
419
- FROM pg_proc p
420
- JOIN pg_namespace n ON n.oid = p.pronamespace
421
- JOIN pg_language l ON l.oid = p.prolang
422
- WHERE ${conditions.join(" AND ")}
423
- ORDER BY n.nspname, p.proname
424
- LIMIT ${String(limitVal)}`;
425
- const result = await adapter.executeQuery(sql);
426
- return {
427
- functions: result.rows,
428
- count: result.rows?.length ?? 0,
429
- limit: limitVal,
430
- note: (result.rows?.length ?? 0) >= limitVal
431
- ? `Results limited to ${String(limitVal)}. Use 'limit' param for more, or 'exclude' to filter out extension schemas.`
432
- : undefined,
433
- };
434
630
  },
435
631
  };
436
632
  }
@@ -447,31 +643,77 @@ function createListTriggersTool(adapter) {
447
643
  annotations: readOnly("List Triggers"),
448
644
  icons: getToolIcons("schema", readOnly("List Triggers")),
449
645
  handler: async (params, _context) => {
450
- const parsed = (params ?? {});
451
- let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema')";
452
- if (parsed.schema)
453
- whereClause += ` AND n.nspname = '${parsed.schema}'`;
454
- if (parsed.table)
455
- whereClause += ` AND c.relname = '${parsed.table}'`;
456
- const sql = `SELECT n.nspname as schema, c.relname as table_name, t.tgname as name,
457
- CASE t.tgtype::int & 2 WHEN 2 THEN 'BEFORE' ELSE 'AFTER' END as timing,
458
- array_remove(ARRAY[
459
- CASE WHEN t.tgtype::int & 4 = 4 THEN 'INSERT' END,
460
- CASE WHEN t.tgtype::int & 8 = 8 THEN 'DELETE' END,
461
- CASE WHEN t.tgtype::int & 16 = 16 THEN 'UPDATE' END,
462
- CASE WHEN t.tgtype::int & 32 = 32 THEN 'TRUNCATE' END
463
- ], NULL) as events,
464
- p.proname as function_name,
465
- t.tgenabled != 'D' as enabled
466
- FROM pg_trigger t
467
- JOIN pg_class c ON c.oid = t.tgrelid
468
- JOIN pg_namespace n ON n.oid = c.relnamespace
469
- JOIN pg_proc p ON p.oid = t.tgfoid
470
- WHERE NOT t.tgisinternal
471
- AND ${whereClause}
472
- ORDER BY n.nspname, c.relname, t.tgname`;
473
- const result = await adapter.executeQuery(sql);
474
- 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
+ }
475
717
  },
476
718
  };
477
719
  }
@@ -491,38 +733,85 @@ function createListConstraintsTool(adapter) {
491
733
  annotations: readOnly("List Constraints"),
492
734
  icons: getToolIcons("schema", readOnly("List Constraints")),
493
735
  handler: async (params, _context) => {
494
- const parsed = (params ?? {});
495
- let whereClause = "n.nspname NOT IN ('pg_catalog', 'information_schema') AND con.contype != 'n'";
496
- if (parsed.schema)
497
- whereClause += ` AND n.nspname = '${parsed.schema}'`;
498
- if (parsed.table)
499
- whereClause += ` AND c.relname = '${parsed.table}'`;
500
- if (parsed.type) {
501
- const typeMap = {
502
- primary_key: "p",
503
- foreign_key: "f",
504
- unique: "u",
505
- 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" }),
506
813
  };
507
- whereClause += ` AND con.contype = '${typeMap[parsed.type] ?? ""}'`;
508
814
  }
509
- const sql = `SELECT n.nspname as schema, c.relname as table_name, con.conname as name,
510
- CASE con.contype
511
- WHEN 'p' THEN 'primary_key'
512
- WHEN 'f' THEN 'foreign_key'
513
- WHEN 'u' THEN 'unique'
514
- WHEN 'c' THEN 'check'
515
- WHEN 'n' THEN 'not_null'
516
- ELSE con.contype
517
- END as type,
518
- pg_get_constraintdef(con.oid) as definition
519
- FROM pg_constraint con
520
- JOIN pg_class c ON c.oid = con.conrelid
521
- JOIN pg_namespace n ON n.oid = c.relnamespace
522
- WHERE ${whereClause}
523
- ORDER BY n.nspname, c.relname, con.conname`;
524
- const result = await adapter.executeQuery(sql);
525
- return { constraints: result.rows, count: result.rows?.length ?? 0 };
526
815
  },
527
816
  };
528
817
  }