@neverinfamous/postgres-mcp 2.0.0 → 2.2.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 (518) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +119 -46
  3. package/dist/__tests__/benchmarks/codemode.bench.js +3 -3
  4. package/dist/__tests__/benchmarks/codemode.bench.js.map +1 -1
  5. package/dist/__tests__/benchmarks/connection-pool.bench.js +3 -3
  6. package/dist/__tests__/benchmarks/connection-pool.bench.js.map +1 -1
  7. package/dist/__tests__/benchmarks/introspection-migration.bench.d.ts +11 -0
  8. package/dist/__tests__/benchmarks/introspection-migration.bench.d.ts.map +1 -0
  9. package/dist/__tests__/benchmarks/introspection-migration.bench.js +143 -0
  10. package/dist/__tests__/benchmarks/introspection-migration.bench.js.map +1 -0
  11. package/dist/__tests__/benchmarks/resource-prompts.bench.js +0 -64
  12. package/dist/__tests__/benchmarks/resource-prompts.bench.js.map +1 -1
  13. package/dist/__tests__/benchmarks/schema-parsing.bench.js +5 -5
  14. package/dist/__tests__/benchmarks/schema-parsing.bench.js.map +1 -1
  15. package/dist/__tests__/benchmarks/tool-filtering.bench.js +17 -8
  16. package/dist/__tests__/benchmarks/tool-filtering.bench.js.map +1 -1
  17. package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
  18. package/dist/__tests__/mocks/adapter.js +2 -1
  19. package/dist/__tests__/mocks/adapter.js.map +1 -1
  20. package/dist/adapters/DatabaseAdapter.d.ts +6 -5
  21. package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
  22. package/dist/adapters/DatabaseAdapter.js +11 -20
  23. package/dist/adapters/DatabaseAdapter.js.map +1 -1
  24. package/dist/adapters/postgresql/PostgresAdapter.d.ts +5 -26
  25. package/dist/adapters/postgresql/PostgresAdapter.d.ts.map +1 -1
  26. package/dist/adapters/postgresql/PostgresAdapter.js +31 -526
  27. package/dist/adapters/postgresql/PostgresAdapter.js.map +1 -1
  28. package/dist/adapters/postgresql/prompts/index.js +1 -1
  29. package/dist/adapters/postgresql/prompts/index.js.map +1 -1
  30. package/dist/adapters/postgresql/resources/index.d.ts +1 -1
  31. package/dist/adapters/postgresql/resources/index.js +3 -3
  32. package/dist/adapters/postgresql/resources/index.js.map +1 -1
  33. package/dist/adapters/postgresql/schema-operations.d.ts +71 -0
  34. package/dist/adapters/postgresql/schema-operations.d.ts.map +1 -0
  35. package/dist/adapters/postgresql/schema-operations.js +561 -0
  36. package/dist/adapters/postgresql/schema-operations.js.map +1 -0
  37. package/dist/adapters/postgresql/schemas/admin.d.ts +4 -4
  38. package/dist/adapters/postgresql/schemas/admin.js +4 -4
  39. package/dist/adapters/postgresql/schemas/admin.js.map +1 -1
  40. package/dist/adapters/postgresql/schemas/backup.d.ts +2 -2
  41. package/dist/adapters/postgresql/schemas/backup.d.ts.map +1 -1
  42. package/dist/adapters/postgresql/schemas/backup.js +1 -3
  43. package/dist/adapters/postgresql/schemas/backup.js.map +1 -1
  44. package/dist/adapters/postgresql/schemas/core/index.d.ts +6 -0
  45. package/dist/adapters/postgresql/schemas/core/index.d.ts.map +1 -0
  46. package/dist/adapters/postgresql/schemas/core/index.js +6 -0
  47. package/dist/adapters/postgresql/schemas/core/index.js.map +1 -0
  48. package/dist/adapters/postgresql/schemas/{core.d.ts → core/queries.d.ts} +16 -171
  49. package/dist/adapters/postgresql/schemas/core/queries.d.ts.map +1 -0
  50. package/dist/adapters/postgresql/schemas/{core.js → core/queries.js} +5 -213
  51. package/dist/adapters/postgresql/schemas/core/queries.js.map +1 -0
  52. package/dist/adapters/postgresql/schemas/core/transactions.d.ts +149 -0
  53. package/dist/adapters/postgresql/schemas/core/transactions.d.ts.map +1 -0
  54. package/dist/adapters/postgresql/schemas/core/transactions.js +239 -0
  55. package/dist/adapters/postgresql/schemas/core/transactions.js.map +1 -0
  56. package/dist/adapters/postgresql/schemas/cron.d.ts +12 -12
  57. package/dist/adapters/postgresql/schemas/cron.d.ts.map +1 -1
  58. package/dist/adapters/postgresql/schemas/cron.js +38 -10
  59. package/dist/adapters/postgresql/schemas/cron.js.map +1 -1
  60. package/dist/adapters/postgresql/schemas/extensions/citext.d.ts +222 -0
  61. package/dist/adapters/postgresql/schemas/extensions/citext.d.ts.map +1 -0
  62. package/dist/adapters/postgresql/schemas/extensions/citext.js +306 -0
  63. package/dist/adapters/postgresql/schemas/extensions/citext.js.map +1 -0
  64. package/dist/adapters/postgresql/schemas/extensions/index.d.ts +15 -0
  65. package/dist/adapters/postgresql/schemas/extensions/index.d.ts.map +1 -0
  66. package/dist/adapters/postgresql/schemas/extensions/index.js +20 -0
  67. package/dist/adapters/postgresql/schemas/extensions/index.js.map +1 -0
  68. package/dist/adapters/postgresql/schemas/extensions/kcache.d.ts +164 -0
  69. package/dist/adapters/postgresql/schemas/extensions/kcache.d.ts.map +1 -0
  70. package/dist/adapters/postgresql/schemas/extensions/kcache.js +225 -0
  71. package/dist/adapters/postgresql/schemas/extensions/kcache.js.map +1 -0
  72. package/dist/adapters/postgresql/schemas/extensions/ltree.d.ts +253 -0
  73. package/dist/adapters/postgresql/schemas/extensions/ltree.d.ts.map +1 -0
  74. package/dist/adapters/postgresql/schemas/extensions/ltree.js +430 -0
  75. package/dist/adapters/postgresql/schemas/extensions/ltree.js.map +1 -0
  76. package/dist/adapters/postgresql/schemas/extensions/pgcrypto.d.ts +251 -0
  77. package/dist/adapters/postgresql/schemas/extensions/pgcrypto.d.ts.map +1 -0
  78. package/dist/adapters/postgresql/schemas/extensions/pgcrypto.js +294 -0
  79. package/dist/adapters/postgresql/schemas/extensions/pgcrypto.js.map +1 -0
  80. package/dist/adapters/postgresql/schemas/extensions/shared.d.ts +10 -0
  81. package/dist/adapters/postgresql/schemas/extensions/shared.d.ts.map +1 -0
  82. package/dist/adapters/postgresql/schemas/extensions/shared.js +15 -0
  83. package/dist/adapters/postgresql/schemas/extensions/shared.js.map +1 -0
  84. package/dist/adapters/postgresql/schemas/index.d.ts +6 -6
  85. package/dist/adapters/postgresql/schemas/index.d.ts.map +1 -1
  86. package/dist/adapters/postgresql/schemas/index.js +8 -8
  87. package/dist/adapters/postgresql/schemas/index.js.map +1 -1
  88. package/dist/adapters/postgresql/schemas/introspection.d.ts +19 -42
  89. package/dist/adapters/postgresql/schemas/introspection.d.ts.map +1 -1
  90. package/dist/adapters/postgresql/schemas/introspection.js +72 -27
  91. package/dist/adapters/postgresql/schemas/introspection.js.map +1 -1
  92. package/dist/adapters/postgresql/schemas/jsonb/advanced.d.ts +270 -0
  93. package/dist/adapters/postgresql/schemas/jsonb/advanced.d.ts.map +1 -0
  94. package/dist/adapters/postgresql/schemas/jsonb/advanced.js +371 -0
  95. package/dist/adapters/postgresql/schemas/jsonb/advanced.js.map +1 -0
  96. package/dist/adapters/postgresql/schemas/jsonb/basic.d.ts +283 -0
  97. package/dist/adapters/postgresql/schemas/jsonb/basic.d.ts.map +1 -0
  98. package/dist/adapters/postgresql/schemas/jsonb/basic.js +456 -0
  99. package/dist/adapters/postgresql/schemas/jsonb/basic.js.map +1 -0
  100. package/dist/adapters/postgresql/schemas/jsonb/index.d.ts +6 -0
  101. package/dist/adapters/postgresql/schemas/jsonb/index.d.ts.map +1 -0
  102. package/dist/adapters/postgresql/schemas/jsonb/index.js +6 -0
  103. package/dist/adapters/postgresql/schemas/jsonb/index.js.map +1 -0
  104. package/dist/adapters/postgresql/schemas/monitoring.d.ts +4 -4
  105. package/dist/adapters/postgresql/schemas/monitoring.js +2 -2
  106. package/dist/adapters/postgresql/schemas/monitoring.js.map +1 -1
  107. package/dist/adapters/postgresql/schemas/partitioning.d.ts +14 -14
  108. package/dist/adapters/postgresql/schemas/partitioning.d.ts.map +1 -1
  109. package/dist/adapters/postgresql/schemas/partitioning.js +64 -46
  110. package/dist/adapters/postgresql/schemas/partitioning.js.map +1 -1
  111. package/dist/adapters/postgresql/schemas/partman.d.ts +16 -14
  112. package/dist/adapters/postgresql/schemas/partman.d.ts.map +1 -1
  113. package/dist/adapters/postgresql/schemas/partman.js +9 -9
  114. package/dist/adapters/postgresql/schemas/partman.js.map +1 -1
  115. package/dist/adapters/postgresql/schemas/postgis/advanced.d.ts +429 -0
  116. package/dist/adapters/postgresql/schemas/postgis/advanced.d.ts.map +1 -0
  117. package/dist/adapters/postgresql/schemas/postgis/advanced.js +495 -0
  118. package/dist/adapters/postgresql/schemas/postgis/advanced.js.map +1 -0
  119. package/dist/adapters/postgresql/schemas/{postgis.d.ts → postgis/basic.d.ts} +1 -423
  120. package/dist/adapters/postgresql/schemas/postgis/basic.d.ts.map +1 -0
  121. package/dist/adapters/postgresql/schemas/{postgis.js → postgis/basic.js} +1 -486
  122. package/dist/adapters/postgresql/schemas/postgis/basic.js.map +1 -0
  123. package/dist/adapters/postgresql/schemas/postgis/index.d.ts +6 -0
  124. package/dist/adapters/postgresql/schemas/postgis/index.d.ts.map +1 -0
  125. package/dist/adapters/postgresql/schemas/postgis/index.js +6 -0
  126. package/dist/adapters/postgresql/schemas/postgis/index.js.map +1 -0
  127. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts +35 -25
  128. package/dist/adapters/postgresql/schemas/schema-mgmt.d.ts.map +1 -1
  129. package/dist/adapters/postgresql/schemas/schema-mgmt.js +57 -19
  130. package/dist/adapters/postgresql/schemas/schema-mgmt.js.map +1 -1
  131. package/dist/adapters/postgresql/schemas/stats/index.d.ts +6 -0
  132. package/dist/adapters/postgresql/schemas/stats/index.d.ts.map +1 -0
  133. package/dist/adapters/postgresql/schemas/stats/index.js +6 -0
  134. package/dist/adapters/postgresql/schemas/stats/index.js.map +1 -0
  135. package/dist/adapters/postgresql/schemas/stats/input.d.ts +260 -0
  136. package/dist/adapters/postgresql/schemas/stats/input.d.ts.map +1 -0
  137. package/dist/adapters/postgresql/schemas/{stats.js → stats/input.js} +2 -331
  138. package/dist/adapters/postgresql/schemas/stats/input.js.map +1 -0
  139. package/dist/adapters/postgresql/schemas/{stats.d.ts → stats/output.d.ts} +3 -246
  140. package/dist/adapters/postgresql/schemas/stats/output.d.ts.map +1 -0
  141. package/dist/adapters/postgresql/schemas/stats/output.js +334 -0
  142. package/dist/adapters/postgresql/schemas/stats/output.js.map +1 -0
  143. package/dist/adapters/postgresql/schemas/text-search.d.ts +18 -18
  144. package/dist/adapters/postgresql/schemas/text-search.d.ts.map +1 -1
  145. package/dist/adapters/postgresql/schemas/text-search.js +12 -27
  146. package/dist/adapters/postgresql/schemas/text-search.js.map +1 -1
  147. package/dist/adapters/postgresql/schemas/vector.d.ts +10 -10
  148. package/dist/adapters/postgresql/schemas/vector.d.ts.map +1 -1
  149. package/dist/adapters/postgresql/schemas/vector.js +9 -15
  150. package/dist/adapters/postgresql/schemas/vector.js.map +1 -1
  151. package/dist/adapters/postgresql/tools/backup/dump.d.ts.map +1 -1
  152. package/dist/adapters/postgresql/tools/backup/dump.js +95 -76
  153. package/dist/adapters/postgresql/tools/backup/dump.js.map +1 -1
  154. package/dist/adapters/postgresql/tools/backup/planning.d.ts.map +1 -1
  155. package/dist/adapters/postgresql/tools/backup/planning.js +345 -287
  156. package/dist/adapters/postgresql/tools/backup/planning.js.map +1 -1
  157. package/dist/adapters/postgresql/tools/citext/analysis.d.ts +24 -0
  158. package/dist/adapters/postgresql/tools/citext/analysis.d.ts.map +1 -0
  159. package/dist/adapters/postgresql/tools/{citext.js → citext/analysis.js} +50 -232
  160. package/dist/adapters/postgresql/tools/citext/analysis.js.map +1 -0
  161. package/dist/adapters/postgresql/tools/citext/index.d.ts +15 -0
  162. package/dist/adapters/postgresql/tools/citext/index.d.ts.map +1 -0
  163. package/dist/adapters/postgresql/tools/citext/index.js +23 -0
  164. package/dist/adapters/postgresql/tools/citext/index.js.map +1 -0
  165. package/dist/adapters/postgresql/tools/citext/setup.d.ts +16 -0
  166. package/dist/adapters/postgresql/tools/citext/setup.d.ts.map +1 -0
  167. package/dist/adapters/postgresql/tools/citext/setup.js +193 -0
  168. package/dist/adapters/postgresql/tools/citext/setup.js.map +1 -0
  169. package/dist/adapters/postgresql/tools/codemode/index.js +1 -1
  170. package/dist/adapters/postgresql/tools/codemode/index.js.map +1 -1
  171. package/dist/adapters/postgresql/tools/core/convenience.d.ts +12 -22
  172. package/dist/adapters/postgresql/tools/core/convenience.d.ts.map +1 -1
  173. package/dist/adapters/postgresql/tools/core/convenience.js +100 -210
  174. package/dist/adapters/postgresql/tools/core/convenience.js.map +1 -1
  175. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts +1 -0
  176. package/dist/adapters/postgresql/tools/core/error-helpers.d.ts.map +1 -1
  177. package/dist/adapters/postgresql/tools/core/error-helpers.js +8 -1
  178. package/dist/adapters/postgresql/tools/core/error-helpers.js.map +1 -1
  179. package/dist/adapters/postgresql/tools/core/health.d.ts.map +1 -1
  180. package/dist/adapters/postgresql/tools/core/health.js +124 -114
  181. package/dist/adapters/postgresql/tools/core/health.js.map +1 -1
  182. package/dist/adapters/postgresql/tools/core/index.d.ts +2 -1
  183. package/dist/adapters/postgresql/tools/core/index.d.ts.map +1 -1
  184. package/dist/adapters/postgresql/tools/core/index.js +3 -2
  185. package/dist/adapters/postgresql/tools/core/index.js.map +1 -1
  186. package/dist/adapters/postgresql/tools/core/indexes.d.ts.map +1 -1
  187. package/dist/adapters/postgresql/tools/core/indexes.js +151 -127
  188. package/dist/adapters/postgresql/tools/core/indexes.js.map +1 -1
  189. package/dist/adapters/postgresql/tools/core/objects.d.ts.map +1 -1
  190. package/dist/adapters/postgresql/tools/core/objects.js +186 -161
  191. package/dist/adapters/postgresql/tools/core/objects.js.map +1 -1
  192. package/dist/adapters/postgresql/tools/core/query.d.ts.map +1 -1
  193. package/dist/adapters/postgresql/tools/core/query.js +37 -25
  194. package/dist/adapters/postgresql/tools/core/query.js.map +1 -1
  195. package/dist/adapters/postgresql/tools/core/schemas.d.ts +6 -3
  196. package/dist/adapters/postgresql/tools/core/schemas.d.ts.map +1 -1
  197. package/dist/adapters/postgresql/tools/core/schemas.js +11 -2
  198. package/dist/adapters/postgresql/tools/core/schemas.js.map +1 -1
  199. package/dist/adapters/postgresql/tools/core/tables.d.ts.map +1 -1
  200. package/dist/adapters/postgresql/tools/core/tables.js +156 -129
  201. package/dist/adapters/postgresql/tools/core/tables.js.map +1 -1
  202. package/dist/adapters/postgresql/tools/core/utility.d.ts +26 -0
  203. package/dist/adapters/postgresql/tools/core/utility.d.ts.map +1 -0
  204. package/dist/adapters/postgresql/tools/core/utility.js +174 -0
  205. package/dist/adapters/postgresql/tools/core/utility.js.map +1 -0
  206. package/dist/adapters/postgresql/tools/cron.js +90 -43
  207. package/dist/adapters/postgresql/tools/cron.js.map +1 -1
  208. package/dist/adapters/postgresql/tools/introspection/analysis.d.ts +12 -0
  209. package/dist/adapters/postgresql/tools/introspection/analysis.d.ts.map +1 -0
  210. package/dist/adapters/postgresql/tools/introspection/analysis.js +605 -0
  211. package/dist/adapters/postgresql/tools/introspection/analysis.js.map +1 -0
  212. package/dist/adapters/postgresql/tools/introspection/graph.d.ts +55 -0
  213. package/dist/adapters/postgresql/tools/introspection/graph.d.ts.map +1 -0
  214. package/dist/adapters/postgresql/tools/introspection/graph.js +621 -0
  215. package/dist/adapters/postgresql/tools/introspection/graph.js.map +1 -0
  216. package/dist/adapters/postgresql/tools/introspection/index.d.ts +21 -0
  217. package/dist/adapters/postgresql/tools/introspection/index.d.ts.map +1 -0
  218. package/dist/adapters/postgresql/tools/introspection/index.js +31 -0
  219. package/dist/adapters/postgresql/tools/introspection/index.js.map +1 -0
  220. package/dist/adapters/postgresql/tools/introspection/migration.d.ts +15 -0
  221. package/dist/adapters/postgresql/tools/introspection/migration.d.ts.map +1 -0
  222. package/dist/adapters/postgresql/tools/introspection/migration.js +575 -0
  223. package/dist/adapters/postgresql/tools/introspection/migration.js.map +1 -0
  224. package/dist/adapters/postgresql/tools/jsonb/analytics.d.ts +20 -0
  225. package/dist/adapters/postgresql/tools/jsonb/analytics.d.ts.map +1 -0
  226. package/dist/adapters/postgresql/tools/jsonb/analytics.js +367 -0
  227. package/dist/adapters/postgresql/tools/jsonb/analytics.js.map +1 -0
  228. package/dist/adapters/postgresql/tools/jsonb/index.d.ts +4 -2
  229. package/dist/adapters/postgresql/tools/jsonb/index.d.ts.map +1 -1
  230. package/dist/adapters/postgresql/tools/jsonb/index.js +8 -4
  231. package/dist/adapters/postgresql/tools/jsonb/index.js.map +1 -1
  232. package/dist/adapters/postgresql/tools/jsonb/read.d.ts +38 -0
  233. package/dist/adapters/postgresql/tools/jsonb/read.d.ts.map +1 -0
  234. package/dist/adapters/postgresql/tools/jsonb/{basic.js → read.js} +41 -482
  235. package/dist/adapters/postgresql/tools/jsonb/read.js.map +1 -0
  236. package/dist/adapters/postgresql/tools/jsonb/{advanced.d.ts → transform.d.ts} +1 -13
  237. package/dist/adapters/postgresql/tools/jsonb/transform.d.ts.map +1 -0
  238. package/dist/adapters/postgresql/tools/jsonb/{advanced.js → transform.js} +26 -357
  239. package/dist/adapters/postgresql/tools/jsonb/transform.js.map +1 -0
  240. package/dist/adapters/postgresql/tools/jsonb/write.d.ts +14 -0
  241. package/dist/adapters/postgresql/tools/jsonb/write.d.ts.map +1 -0
  242. package/dist/adapters/postgresql/tools/jsonb/write.js +468 -0
  243. package/dist/adapters/postgresql/tools/jsonb/write.js.map +1 -0
  244. package/dist/adapters/postgresql/tools/kcache.d.ts.map +1 -1
  245. package/dist/adapters/postgresql/tools/kcache.js +116 -51
  246. package/dist/adapters/postgresql/tools/kcache.js.map +1 -1
  247. package/dist/adapters/postgresql/tools/ltree.js +346 -260
  248. package/dist/adapters/postgresql/tools/ltree.js.map +1 -1
  249. package/dist/adapters/postgresql/tools/migration/index.d.ts +15 -0
  250. package/dist/adapters/postgresql/tools/migration/index.d.ts.map +1 -0
  251. package/dist/adapters/postgresql/tools/migration/index.js +23 -0
  252. package/dist/adapters/postgresql/tools/migration/index.js.map +1 -0
  253. package/dist/adapters/postgresql/tools/monitoring/analysis.d.ts +15 -0
  254. package/dist/adapters/postgresql/tools/monitoring/analysis.d.ts.map +1 -0
  255. package/dist/adapters/postgresql/tools/{monitoring.js → monitoring/analysis.js} +24 -359
  256. package/dist/adapters/postgresql/tools/monitoring/analysis.js.map +1 -0
  257. package/dist/adapters/postgresql/tools/monitoring/basic.d.ts +17 -0
  258. package/dist/adapters/postgresql/tools/monitoring/basic.d.ts.map +1 -0
  259. package/dist/adapters/postgresql/tools/monitoring/basic.js +432 -0
  260. package/dist/adapters/postgresql/tools/monitoring/basic.js.map +1 -0
  261. package/dist/adapters/postgresql/tools/monitoring/index.d.ts +16 -0
  262. package/dist/adapters/postgresql/tools/monitoring/index.d.ts.map +1 -0
  263. package/dist/adapters/postgresql/tools/monitoring/index.js +31 -0
  264. package/dist/adapters/postgresql/tools/monitoring/index.js.map +1 -0
  265. package/dist/adapters/postgresql/tools/partitioning/index.d.ts +15 -0
  266. package/dist/adapters/postgresql/tools/partitioning/index.d.ts.map +1 -0
  267. package/dist/adapters/postgresql/tools/partitioning/index.js +23 -0
  268. package/dist/adapters/postgresql/tools/partitioning/index.js.map +1 -0
  269. package/dist/adapters/postgresql/tools/partitioning/info.d.ts +11 -0
  270. package/dist/adapters/postgresql/tools/partitioning/info.d.ts.map +1 -0
  271. package/dist/adapters/postgresql/tools/partitioning/info.js +302 -0
  272. package/dist/adapters/postgresql/tools/partitioning/info.js.map +1 -0
  273. package/dist/adapters/postgresql/tools/partitioning/management.d.ts +28 -0
  274. package/dist/adapters/postgresql/tools/partitioning/management.d.ts.map +1 -0
  275. package/dist/adapters/postgresql/tools/{partitioning.js → partitioning/management.js} +48 -307
  276. package/dist/adapters/postgresql/tools/partitioning/management.js.map +1 -0
  277. package/dist/adapters/postgresql/tools/partman/helpers.d.ts +29 -0
  278. package/dist/adapters/postgresql/tools/partman/helpers.d.ts.map +1 -0
  279. package/dist/adapters/postgresql/tools/partman/helpers.js +59 -0
  280. package/dist/adapters/postgresql/tools/partman/helpers.js.map +1 -0
  281. package/dist/adapters/postgresql/tools/partman/index.d.ts +2 -1
  282. package/dist/adapters/postgresql/tools/partman/index.d.ts.map +1 -1
  283. package/dist/adapters/postgresql/tools/partman/index.js +4 -2
  284. package/dist/adapters/postgresql/tools/partman/index.js.map +1 -1
  285. package/dist/adapters/postgresql/tools/partman/maintenance.d.ts +20 -0
  286. package/dist/adapters/postgresql/tools/partman/maintenance.d.ts.map +1 -0
  287. package/dist/adapters/postgresql/tools/partman/maintenance.js +496 -0
  288. package/dist/adapters/postgresql/tools/partman/maintenance.js.map +1 -0
  289. package/dist/adapters/postgresql/tools/partman/management.d.ts.map +1 -1
  290. package/dist/adapters/postgresql/tools/partman/management.js +438 -383
  291. package/dist/adapters/postgresql/tools/partman/management.js.map +1 -1
  292. package/dist/adapters/postgresql/tools/partman/operations.d.ts +1 -13
  293. package/dist/adapters/postgresql/tools/partman/operations.d.ts.map +1 -1
  294. package/dist/adapters/postgresql/tools/partman/operations.js +171 -652
  295. package/dist/adapters/postgresql/tools/partman/operations.js.map +1 -1
  296. package/dist/adapters/postgresql/tools/performance/analysis.d.ts.map +1 -1
  297. package/dist/adapters/postgresql/tools/performance/analysis.js +69 -42
  298. package/dist/adapters/postgresql/tools/performance/analysis.js.map +1 -1
  299. package/dist/adapters/postgresql/tools/performance/anomaly-detection.d.ts +18 -0
  300. package/dist/adapters/postgresql/tools/performance/anomaly-detection.d.ts.map +1 -0
  301. package/dist/adapters/postgresql/tools/performance/anomaly-detection.js +533 -0
  302. package/dist/adapters/postgresql/tools/performance/anomaly-detection.js.map +1 -0
  303. package/dist/adapters/postgresql/tools/performance/diagnostics.d.ts +11 -0
  304. package/dist/adapters/postgresql/tools/performance/diagnostics.d.ts.map +1 -0
  305. package/dist/adapters/postgresql/tools/performance/diagnostics.js +332 -0
  306. package/dist/adapters/postgresql/tools/performance/diagnostics.js.map +1 -0
  307. package/dist/adapters/postgresql/tools/performance/index.d.ts +1 -1
  308. package/dist/adapters/postgresql/tools/performance/index.d.ts.map +1 -1
  309. package/dist/adapters/postgresql/tools/performance/index.js +7 -1
  310. package/dist/adapters/postgresql/tools/performance/index.js.map +1 -1
  311. package/dist/adapters/postgresql/tools/performance/monitoring.d.ts.map +1 -1
  312. package/dist/adapters/postgresql/tools/performance/monitoring.js +80 -55
  313. package/dist/adapters/postgresql/tools/performance/monitoring.js.map +1 -1
  314. package/dist/adapters/postgresql/tools/performance/optimization.d.ts.map +1 -1
  315. package/dist/adapters/postgresql/tools/performance/optimization.js +18 -11
  316. package/dist/adapters/postgresql/tools/performance/optimization.js.map +1 -1
  317. package/dist/adapters/postgresql/tools/performance/stats.d.ts.map +1 -1
  318. package/dist/adapters/postgresql/tools/performance/stats.js +439 -318
  319. package/dist/adapters/postgresql/tools/performance/stats.js.map +1 -1
  320. package/dist/adapters/postgresql/tools/pgcrypto.d.ts.map +1 -1
  321. package/dist/adapters/postgresql/tools/pgcrypto.js +45 -77
  322. package/dist/adapters/postgresql/tools/pgcrypto.js.map +1 -1
  323. package/dist/adapters/postgresql/tools/postgis/basic.d.ts.map +1 -1
  324. package/dist/adapters/postgresql/tools/postgis/basic.js +121 -93
  325. package/dist/adapters/postgresql/tools/postgis/basic.js.map +1 -1
  326. package/dist/adapters/postgresql/tools/schema/index.d.ts +16 -0
  327. package/dist/adapters/postgresql/tools/schema/index.d.ts.map +1 -0
  328. package/dist/adapters/postgresql/tools/schema/index.js +32 -0
  329. package/dist/adapters/postgresql/tools/schema/index.js.map +1 -0
  330. package/dist/adapters/postgresql/tools/schema/objects.d.ts +15 -0
  331. package/dist/adapters/postgresql/tools/schema/objects.d.ts.map +1 -0
  332. package/dist/adapters/postgresql/tools/schema/objects.js +378 -0
  333. package/dist/adapters/postgresql/tools/schema/objects.js.map +1 -0
  334. package/dist/adapters/postgresql/tools/schema/views.d.ts +15 -0
  335. package/dist/adapters/postgresql/tools/schema/views.d.ts.map +1 -0
  336. package/dist/adapters/postgresql/tools/{schema.js → schema/views.js} +64 -386
  337. package/dist/adapters/postgresql/tools/schema/views.js.map +1 -0
  338. package/dist/adapters/postgresql/tools/stats/advanced.d.ts.map +1 -1
  339. package/dist/adapters/postgresql/tools/stats/advanced.js +1 -218
  340. package/dist/adapters/postgresql/tools/stats/advanced.js.map +1 -1
  341. package/dist/adapters/postgresql/tools/stats/math-utils.d.ts +33 -0
  342. package/dist/adapters/postgresql/tools/stats/math-utils.d.ts.map +1 -0
  343. package/dist/adapters/postgresql/tools/stats/math-utils.js +225 -0
  344. package/dist/adapters/postgresql/tools/stats/math-utils.js.map +1 -0
  345. package/dist/adapters/postgresql/tools/text/index.d.ts +16 -0
  346. package/dist/adapters/postgresql/tools/text/index.d.ts.map +1 -0
  347. package/dist/adapters/postgresql/tools/text/index.js +33 -0
  348. package/dist/adapters/postgresql/tools/text/index.js.map +1 -0
  349. package/dist/adapters/postgresql/tools/text/matching.d.ts +17 -0
  350. package/dist/adapters/postgresql/tools/text/matching.d.ts.map +1 -0
  351. package/dist/adapters/postgresql/tools/text/matching.js +565 -0
  352. package/dist/adapters/postgresql/tools/text/matching.js.map +1 -0
  353. package/dist/adapters/postgresql/tools/text/search.d.ts +17 -0
  354. package/dist/adapters/postgresql/tools/text/search.d.ts.map +1 -0
  355. package/dist/adapters/postgresql/tools/text/search.js +653 -0
  356. package/dist/adapters/postgresql/tools/text/search.js.map +1 -0
  357. package/dist/adapters/postgresql/tools/transactions.d.ts.map +1 -1
  358. package/dist/adapters/postgresql/tools/transactions.js +11 -27
  359. package/dist/adapters/postgresql/tools/transactions.js.map +1 -1
  360. package/dist/adapters/postgresql/tools/vector/{basic.d.ts → data.d.ts} +10 -8
  361. package/dist/adapters/postgresql/tools/vector/data.d.ts.map +1 -0
  362. package/dist/adapters/postgresql/tools/vector/data.js +540 -0
  363. package/dist/adapters/postgresql/tools/vector/data.js.map +1 -0
  364. package/dist/adapters/postgresql/tools/vector/index.d.ts.map +1 -1
  365. package/dist/adapters/postgresql/tools/vector/index.js +6 -2
  366. package/dist/adapters/postgresql/tools/vector/index.js.map +1 -1
  367. package/dist/adapters/postgresql/tools/vector/management.d.ts +11 -0
  368. package/dist/adapters/postgresql/tools/vector/management.d.ts.map +1 -0
  369. package/dist/adapters/postgresql/tools/vector/management.js +425 -0
  370. package/dist/adapters/postgresql/tools/vector/management.js.map +1 -0
  371. package/dist/adapters/postgresql/tools/vector/query.d.ts +14 -0
  372. package/dist/adapters/postgresql/tools/vector/query.d.ts.map +1 -0
  373. package/dist/adapters/postgresql/tools/vector/query.js +767 -0
  374. package/dist/adapters/postgresql/tools/vector/query.js.map +1 -0
  375. package/dist/adapters/postgresql/tools/vector/{advanced.d.ts → search-advanced.d.ts} +4 -5
  376. package/dist/adapters/postgresql/tools/vector/search-advanced.d.ts.map +1 -0
  377. package/dist/adapters/postgresql/tools/vector/search-advanced.js +626 -0
  378. package/dist/adapters/postgresql/tools/vector/search-advanced.js.map +1 -0
  379. package/dist/auth/scopes.d.ts.map +1 -1
  380. package/dist/auth/scopes.js +3 -1
  381. package/dist/auth/scopes.js.map +1 -1
  382. package/dist/cli/args.d.ts +3 -2
  383. package/dist/cli/args.d.ts.map +1 -1
  384. package/dist/cli/args.js +4 -3
  385. package/dist/cli/args.js.map +1 -1
  386. package/dist/cli.js +16 -4
  387. package/dist/cli.js.map +1 -1
  388. package/dist/codemode/api/aliases.d.ts +14 -0
  389. package/dist/codemode/api/aliases.d.ts.map +1 -0
  390. package/dist/codemode/api/aliases.js +503 -0
  391. package/dist/codemode/api/aliases.js.map +1 -0
  392. package/dist/codemode/api/group-api.d.ts +23 -0
  393. package/dist/codemode/api/group-api.d.ts.map +1 -0
  394. package/dist/codemode/api/group-api.js +179 -0
  395. package/dist/codemode/api/group-api.js.map +1 -0
  396. package/dist/codemode/{api.d.ts → api/index.d.ts} +5 -4
  397. package/dist/codemode/api/index.d.ts.map +1 -0
  398. package/dist/codemode/api/index.js +195 -0
  399. package/dist/codemode/api/index.js.map +1 -0
  400. package/dist/codemode/api/maps.d.ts +47 -0
  401. package/dist/codemode/api/maps.d.ts.map +1 -0
  402. package/dist/codemode/api/maps.js +529 -0
  403. package/dist/codemode/api/maps.js.map +1 -0
  404. package/dist/codemode/api/normalize.d.ts +13 -0
  405. package/dist/codemode/api/normalize.d.ts.map +1 -0
  406. package/dist/codemode/api/normalize.js +120 -0
  407. package/dist/codemode/api/normalize.js.map +1 -0
  408. package/dist/codemode/index.d.ts +1 -1
  409. package/dist/codemode/index.d.ts.map +1 -1
  410. package/dist/codemode/index.js +1 -1
  411. package/dist/codemode/index.js.map +1 -1
  412. package/dist/codemode/sandbox.d.ts.map +1 -1
  413. package/dist/codemode/sandbox.js +8 -25
  414. package/dist/codemode/sandbox.js.map +1 -1
  415. package/dist/filtering/ToolConstants.d.ts +11 -11
  416. package/dist/filtering/ToolConstants.d.ts.map +1 -1
  417. package/dist/filtering/ToolConstants.js +28 -15
  418. package/dist/filtering/ToolConstants.js.map +1 -1
  419. package/dist/filtering/ToolFilter.d.ts +0 -32
  420. package/dist/filtering/ToolFilter.d.ts.map +1 -1
  421. package/dist/filtering/ToolFilter.js +0 -43
  422. package/dist/filtering/ToolFilter.js.map +1 -1
  423. package/dist/server/McpServer.d.ts +1 -1
  424. package/dist/server/McpServer.d.ts.map +1 -1
  425. package/dist/server/McpServer.js +1 -2
  426. package/dist/server/McpServer.js.map +1 -1
  427. package/dist/transports/http.d.ts +55 -10
  428. package/dist/transports/http.d.ts.map +1 -1
  429. package/dist/transports/http.js +301 -50
  430. package/dist/transports/http.js.map +1 -1
  431. package/dist/types/filtering.d.ts +1 -1
  432. package/dist/types/filtering.d.ts.map +1 -1
  433. package/dist/types/index.d.ts +2 -2
  434. package/dist/types/index.d.ts.map +1 -1
  435. package/dist/types/index.js.map +1 -1
  436. package/dist/types/mcp.d.ts +0 -21
  437. package/dist/types/mcp.d.ts.map +1 -1
  438. package/dist/types/schema.d.ts +0 -79
  439. package/dist/types/schema.d.ts.map +1 -1
  440. package/dist/utils/fts-config.d.ts +0 -6
  441. package/dist/utils/fts-config.d.ts.map +1 -1
  442. package/dist/utils/fts-config.js +1 -1
  443. package/dist/utils/fts-config.js.map +1 -1
  444. package/dist/utils/icons.d.ts.map +1 -1
  445. package/dist/utils/icons.js +5 -0
  446. package/dist/utils/icons.js.map +1 -1
  447. package/dist/utils/identifiers.d.ts.map +1 -1
  448. package/dist/utils/identifiers.js +6 -6
  449. package/dist/utils/identifiers.js.map +1 -1
  450. package/dist/utils/logger.d.ts +6 -6
  451. package/dist/utils/logger.d.ts.map +1 -1
  452. package/dist/utils/logger.js +18 -15
  453. package/dist/utils/logger.js.map +1 -1
  454. package/dist/utils/progress-utils.d.ts +3 -14
  455. package/dist/utils/progress-utils.d.ts.map +1 -1
  456. package/dist/utils/progress-utils.js +2 -21
  457. package/dist/utils/progress-utils.js.map +1 -1
  458. package/dist/utils/version.d.ts +9 -0
  459. package/dist/utils/version.d.ts.map +1 -0
  460. package/dist/utils/version.js +12 -0
  461. package/dist/utils/version.js.map +1 -0
  462. package/dist/utils/where-clause.d.ts +4 -0
  463. package/dist/utils/where-clause.d.ts.map +1 -1
  464. package/dist/utils/where-clause.js +16 -0
  465. package/dist/utils/where-clause.js.map +1 -1
  466. package/package.json +6 -4
  467. package/dist/adapters/postgresql/schemas/core.d.ts.map +0 -1
  468. package/dist/adapters/postgresql/schemas/core.js.map +0 -1
  469. package/dist/adapters/postgresql/schemas/extensions.d.ts +0 -852
  470. package/dist/adapters/postgresql/schemas/extensions.d.ts.map +0 -1
  471. package/dist/adapters/postgresql/schemas/extensions.js +0 -1202
  472. package/dist/adapters/postgresql/schemas/extensions.js.map +0 -1
  473. package/dist/adapters/postgresql/schemas/jsonb.d.ts +0 -541
  474. package/dist/adapters/postgresql/schemas/jsonb.d.ts.map +0 -1
  475. package/dist/adapters/postgresql/schemas/jsonb.js +0 -814
  476. package/dist/adapters/postgresql/schemas/jsonb.js.map +0 -1
  477. package/dist/adapters/postgresql/schemas/postgis.d.ts.map +0 -1
  478. package/dist/adapters/postgresql/schemas/postgis.js.map +0 -1
  479. package/dist/adapters/postgresql/schemas/stats.d.ts.map +0 -1
  480. package/dist/adapters/postgresql/schemas/stats.js.map +0 -1
  481. package/dist/adapters/postgresql/tools/citext.d.ts +0 -18
  482. package/dist/adapters/postgresql/tools/citext.d.ts.map +0 -1
  483. package/dist/adapters/postgresql/tools/citext.js.map +0 -1
  484. package/dist/adapters/postgresql/tools/introspection.d.ts +0 -15
  485. package/dist/adapters/postgresql/tools/introspection.d.ts.map +0 -1
  486. package/dist/adapters/postgresql/tools/introspection.js +0 -1682
  487. package/dist/adapters/postgresql/tools/introspection.js.map +0 -1
  488. package/dist/adapters/postgresql/tools/jsonb/advanced.d.ts.map +0 -1
  489. package/dist/adapters/postgresql/tools/jsonb/advanced.js.map +0 -1
  490. package/dist/adapters/postgresql/tools/jsonb/basic.d.ts +0 -20
  491. package/dist/adapters/postgresql/tools/jsonb/basic.d.ts.map +0 -1
  492. package/dist/adapters/postgresql/tools/jsonb/basic.js.map +0 -1
  493. package/dist/adapters/postgresql/tools/monitoring.d.ts +0 -13
  494. package/dist/adapters/postgresql/tools/monitoring.d.ts.map +0 -1
  495. package/dist/adapters/postgresql/tools/monitoring.js.map +0 -1
  496. package/dist/adapters/postgresql/tools/partitioning.d.ts +0 -13
  497. package/dist/adapters/postgresql/tools/partitioning.d.ts.map +0 -1
  498. package/dist/adapters/postgresql/tools/partitioning.js.map +0 -1
  499. package/dist/adapters/postgresql/tools/schema.d.ts +0 -13
  500. package/dist/adapters/postgresql/tools/schema.d.ts.map +0 -1
  501. package/dist/adapters/postgresql/tools/schema.js.map +0 -1
  502. package/dist/adapters/postgresql/tools/text.d.ts +0 -13
  503. package/dist/adapters/postgresql/tools/text.d.ts.map +0 -1
  504. package/dist/adapters/postgresql/tools/text.js +0 -1082
  505. package/dist/adapters/postgresql/tools/text.js.map +0 -1
  506. package/dist/adapters/postgresql/tools/vector/advanced.d.ts.map +0 -1
  507. package/dist/adapters/postgresql/tools/vector/advanced.js +0 -958
  508. package/dist/adapters/postgresql/tools/vector/advanced.js.map +0 -1
  509. package/dist/adapters/postgresql/tools/vector/basic.d.ts.map +0 -1
  510. package/dist/adapters/postgresql/tools/vector/basic.js +0 -1165
  511. package/dist/adapters/postgresql/tools/vector/basic.js.map +0 -1
  512. package/dist/codemode/api.d.ts.map +0 -1
  513. package/dist/codemode/api.js +0 -1544
  514. package/dist/codemode/api.js.map +0 -1
  515. package/dist/utils/promptGenerator.d.ts +0 -20
  516. package/dist/utils/promptGenerator.d.ts.map +0 -1
  517. package/dist/utils/promptGenerator.js +0 -81
  518. package/dist/utils/promptGenerator.js.map +0 -1
@@ -4,6 +4,7 @@
4
4
  import { z } from "zod";
5
5
  import { readOnly } from "../../../../utils/annotations.js";
6
6
  import { getToolIcons } from "../../../../utils/icons.js";
7
+ import { formatPostgresError } from "../core/error-helpers.js";
7
8
  import { IndexStatsOutputSchema, TableStatsOutputSchema, StatStatementsOutputSchema, StatActivityOutputSchema, UnusedIndexesOutputSchema, DuplicateIndexesOutputSchema, VacuumStatsOutputSchema, QueryPlanStatsOutputSchema, } from "../../schemas/index.js";
8
9
  // Helper to handle undefined params (allows tools to be called without {})
9
10
  const defaultToEmpty = (val) => val ?? {};
@@ -41,7 +42,7 @@ export function createIndexStatsTool(adapter) {
41
42
  table: z.string().optional().describe("Table name to filter indexes"),
42
43
  schema: z.string().optional().describe("Schema name to filter indexes"),
43
44
  limit: z
44
- .number()
45
+ .any()
45
46
  .optional()
46
47
  .describe("Max rows to return (default: 50, use 0 for all)"),
47
48
  });
@@ -55,61 +56,76 @@ export function createIndexStatsTool(adapter) {
55
56
  annotations: readOnly("Index Stats"),
56
57
  icons: getToolIcons("performance", readOnly("Index Stats")),
57
58
  handler: async (params, _context) => {
58
- const parsed = IndexStatsSchemaLocal.parse(params);
59
- let { table, schema } = parsed;
60
- // Parse schema from table if it contains a dot (e.g., 'myschema.orders')
61
- if (table?.includes(".")) {
62
- const parts = table.split(".");
63
- schema = schema ?? parts[0];
64
- table = parts[1] ?? table;
65
- }
66
- const limit = parsed.limit === 0 ? null : (parsed.limit ?? 50);
67
- // P154: Validate table/schema existence before querying
68
- const validationError = await validatePerformanceTableExists(adapter, table, schema);
69
- if (validationError !== null) {
70
- return { success: false, error: validationError };
71
- }
72
- let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema')";
73
- const queryParams = [];
74
- if (schema) {
75
- queryParams.push(schema);
76
- whereClause += ` AND schemaname = $${String(queryParams.length)}`;
77
- }
78
- if (table) {
79
- queryParams.push(table);
80
- whereClause += ` AND relname = $${String(queryParams.length)}`;
81
- }
82
- const sql = `SELECT schemaname, relname as table_name, indexrelname as index_name,
59
+ try {
60
+ const parsed = IndexStatsSchemaLocal.parse(params);
61
+ let { table, schema } = parsed;
62
+ // Parse schema from table if it contains a dot (e.g., 'myschema.orders')
63
+ if (table?.includes(".")) {
64
+ const parts = table.split(".");
65
+ schema = schema ?? parts[0];
66
+ table = parts[1] ?? table;
67
+ }
68
+ const rawLimit = Number(parsed.limit);
69
+ const limit = parsed.limit === undefined
70
+ ? 50
71
+ : isNaN(rawLimit)
72
+ ? 50
73
+ : rawLimit === 0
74
+ ? null
75
+ : rawLimit;
76
+ // P154: Validate table/schema existence before querying
77
+ const validationError = await validatePerformanceTableExists(adapter, table, schema);
78
+ if (validationError !== null) {
79
+ return { success: false, error: validationError };
80
+ }
81
+ let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema')";
82
+ const queryParams = [];
83
+ if (schema) {
84
+ queryParams.push(schema);
85
+ whereClause += ` AND schemaname = $${String(queryParams.length)}`;
86
+ }
87
+ if (table) {
88
+ queryParams.push(table);
89
+ whereClause += ` AND relname = $${String(queryParams.length)}`;
90
+ }
91
+ const sql = `SELECT schemaname, relname as table_name, indexrelname as index_name,
83
92
  idx_scan as scans, idx_tup_read as tuples_read, idx_tup_fetch as tuples_fetched,
84
93
  pg_size_pretty(pg_relation_size(indexrelid)) as size
85
94
  FROM pg_stat_user_indexes
86
95
  WHERE ${whereClause}
87
96
  ORDER BY idx_scan DESC
88
97
  ${limit !== null ? `LIMIT ${String(limit)}` : ""}`;
89
- const result = await adapter.executeQuery(sql, queryParams);
90
- // Coerce numeric fields to JavaScript numbers
91
- const indexes = (result.rows ?? []).map((row) => ({
92
- ...row,
93
- scans: toNum(row["scans"]),
94
- tuples_read: toNum(row["tuples_read"]),
95
- tuples_fetched: toNum(row["tuples_fetched"]),
96
- }));
97
- const response = {
98
- indexes,
99
- count: indexes.length,
100
- };
101
- // Add totalCount if results were limited
102
- if (limit !== null && indexes.length === limit) {
103
- const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_indexes WHERE ${whereClause}`;
104
- const countResult = await adapter.executeQuery(countSql, queryParams);
105
- response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
106
- response["truncated"] = true;
98
+ const result = await adapter.executeQuery(sql, queryParams);
99
+ // Coerce numeric fields to JavaScript numbers
100
+ const indexes = (result.rows ?? []).map((row) => ({
101
+ ...row,
102
+ scans: toNum(row["scans"]),
103
+ tuples_read: toNum(row["tuples_read"]),
104
+ tuples_fetched: toNum(row["tuples_fetched"]),
105
+ }));
106
+ const response = {
107
+ indexes,
108
+ count: indexes.length,
109
+ };
110
+ // Add totalCount if results were limited
111
+ if (limit !== null && indexes.length === limit) {
112
+ const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_indexes WHERE ${whereClause}`;
113
+ const countResult = await adapter.executeQuery(countSql, queryParams);
114
+ response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
115
+ response["truncated"] = true;
116
+ }
117
+ else {
118
+ response["truncated"] = false;
119
+ response["totalCount"] = indexes.length;
120
+ }
121
+ return response;
107
122
  }
108
- else {
109
- response["truncated"] = false;
110
- response["totalCount"] = indexes.length;
123
+ catch (error) {
124
+ return {
125
+ success: false,
126
+ error: formatPostgresError(error, { tool: "pg_index_stats" }),
127
+ };
111
128
  }
112
- return response;
113
129
  },
114
130
  };
115
131
  }
@@ -118,7 +134,7 @@ export function createTableStatsTool(adapter) {
118
134
  table: z.string().optional().describe("Table name (all tables if omitted)"),
119
135
  schema: z.string().optional().describe("Schema name"),
120
136
  limit: z
121
- .number()
137
+ .any()
122
138
  .optional()
123
139
  .describe("Max rows to return (default: 50, use 0 for all)"),
124
140
  });
@@ -132,31 +148,39 @@ export function createTableStatsTool(adapter) {
132
148
  annotations: readOnly("Table Stats"),
133
149
  icons: getToolIcons("performance", readOnly("Table Stats")),
134
150
  handler: async (params, _context) => {
135
- const parsed = TableStatsSchemaLocal.parse(params);
136
- let { table, schema } = parsed;
137
- // Parse schema from table if it contains a dot (e.g., 'myschema.orders')
138
- if (table?.includes(".")) {
139
- const parts = table.split(".");
140
- schema = schema ?? parts[0];
141
- table = parts[1] ?? table;
142
- }
143
- const limit = parsed.limit === 0 ? null : (parsed.limit ?? 50);
144
- // P154: Validate table/schema existence before querying
145
- const validationError = await validatePerformanceTableExists(adapter, table, schema);
146
- if (validationError !== null) {
147
- return { success: false, error: validationError };
148
- }
149
- let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema')";
150
- const queryParams = [];
151
- if (schema) {
152
- queryParams.push(schema);
153
- whereClause += ` AND schemaname = $${String(queryParams.length)}`;
154
- }
155
- if (table) {
156
- queryParams.push(table);
157
- whereClause += ` AND relname = $${String(queryParams.length)}`;
158
- }
159
- const sql = `SELECT schemaname, relname as table_name,
151
+ try {
152
+ const parsed = TableStatsSchemaLocal.parse(params);
153
+ let { table, schema } = parsed;
154
+ // Parse schema from table if it contains a dot (e.g., 'myschema.orders')
155
+ if (table?.includes(".")) {
156
+ const parts = table.split(".");
157
+ schema = schema ?? parts[0];
158
+ table = parts[1] ?? table;
159
+ }
160
+ const rawLimit = Number(parsed.limit);
161
+ const limit = parsed.limit === undefined
162
+ ? 50
163
+ : isNaN(rawLimit)
164
+ ? 50
165
+ : rawLimit === 0
166
+ ? null
167
+ : rawLimit;
168
+ // P154: Validate table/schema existence before querying
169
+ const validationError = await validatePerformanceTableExists(adapter, table, schema);
170
+ if (validationError !== null) {
171
+ return { success: false, error: validationError };
172
+ }
173
+ let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema')";
174
+ const queryParams = [];
175
+ if (schema) {
176
+ queryParams.push(schema);
177
+ whereClause += ` AND schemaname = $${String(queryParams.length)}`;
178
+ }
179
+ if (table) {
180
+ queryParams.push(table);
181
+ whereClause += ` AND relname = $${String(queryParams.length)}`;
182
+ }
183
+ const sql = `SELECT schemaname, relname as table_name,
160
184
  seq_scan, seq_tup_read, idx_scan, idx_tup_fetch,
161
185
  n_tup_ins as inserts, n_tup_upd as updates, n_tup_del as deletes,
162
186
  n_live_tup as live_tuples, n_dead_tup as dead_tuples,
@@ -165,43 +189,50 @@ export function createTableStatsTool(adapter) {
165
189
  WHERE ${whereClause}
166
190
  ORDER BY seq_scan DESC
167
191
  ${limit !== null ? `LIMIT ${String(limit)}` : ""}`;
168
- const result = await adapter.executeQuery(sql, queryParams);
169
- // Coerce numeric fields to JavaScript numbers
170
- const tables = (result.rows ?? []).map((row) => ({
171
- ...row,
172
- seq_scan: toNum(row["seq_scan"]),
173
- seq_tup_read: toNum(row["seq_tup_read"]),
174
- idx_scan: toNum(row["idx_scan"]),
175
- idx_tup_fetch: toNum(row["idx_tup_fetch"]),
176
- inserts: toNum(row["inserts"]),
177
- updates: toNum(row["updates"]),
178
- deletes: toNum(row["deletes"]),
179
- live_tuples: toNum(row["live_tuples"]),
180
- dead_tuples: toNum(row["dead_tuples"]),
181
- }));
182
- // Get total count if limited
183
- const response = {
184
- tables,
185
- count: tables.length,
186
- };
187
- if (limit !== null && tables.length === limit) {
188
- const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_tables WHERE ${whereClause}`;
189
- const countResult = await adapter.executeQuery(countSql, queryParams);
190
- response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
191
- response["truncated"] = true;
192
+ const result = await adapter.executeQuery(sql, queryParams);
193
+ // Coerce numeric fields to JavaScript numbers
194
+ const tables = (result.rows ?? []).map((row) => ({
195
+ ...row,
196
+ seq_scan: toNum(row["seq_scan"]),
197
+ seq_tup_read: toNum(row["seq_tup_read"]),
198
+ idx_scan: toNum(row["idx_scan"]),
199
+ idx_tup_fetch: toNum(row["idx_tup_fetch"]),
200
+ inserts: toNum(row["inserts"]),
201
+ updates: toNum(row["updates"]),
202
+ deletes: toNum(row["deletes"]),
203
+ live_tuples: toNum(row["live_tuples"]),
204
+ dead_tuples: toNum(row["dead_tuples"]),
205
+ }));
206
+ // Get total count if limited
207
+ const response = {
208
+ tables,
209
+ count: tables.length,
210
+ };
211
+ if (limit !== null && tables.length === limit) {
212
+ const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_tables WHERE ${whereClause}`;
213
+ const countResult = await adapter.executeQuery(countSql, queryParams);
214
+ response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
215
+ response["truncated"] = true;
216
+ }
217
+ else {
218
+ response["truncated"] = false;
219
+ response["totalCount"] = tables.length;
220
+ }
221
+ return response;
192
222
  }
193
- else {
194
- response["truncated"] = false;
195
- response["totalCount"] = tables.length;
223
+ catch (error) {
224
+ return {
225
+ success: false,
226
+ error: formatPostgresError(error, { tool: "pg_table_stats" }),
227
+ };
196
228
  }
197
- return response;
198
229
  },
199
230
  };
200
231
  }
201
232
  export function createStatStatementsTool(adapter) {
202
233
  const StatStatementsSchemaBase = z.object({
203
234
  limit: z
204
- .number()
235
+ .any()
205
236
  .optional()
206
237
  .describe("Max statements to return (default: 20, use 0 for all)"),
207
238
  orderBy: z
@@ -219,36 +250,51 @@ export function createStatStatementsTool(adapter) {
219
250
  annotations: readOnly("Query Statistics"),
220
251
  icons: getToolIcons("performance", readOnly("Query Statistics")),
221
252
  handler: async (params, _context) => {
222
- const parsed = StatStatementsSchema.parse(params);
223
- const limit = parsed.limit === 0 ? null : (parsed.limit ?? 20);
224
- const orderBy = parsed.orderBy ?? "total_time";
225
- const sql = `SELECT query, calls, total_exec_time as total_time,
253
+ try {
254
+ const parsed = StatStatementsSchema.parse(params);
255
+ const rawLimit = Number(parsed.limit);
256
+ const limit = parsed.limit === undefined
257
+ ? 20
258
+ : isNaN(rawLimit)
259
+ ? 20
260
+ : rawLimit === 0
261
+ ? null
262
+ : rawLimit;
263
+ const orderBy = parsed.orderBy ?? "total_time";
264
+ const sql = `SELECT query, calls, total_exec_time as total_time,
226
265
  mean_exec_time as mean_time, rows,
227
266
  shared_blks_hit, shared_blks_read
228
267
  FROM pg_stat_statements
229
268
  ORDER BY ${orderBy === "total_time" ? "total_exec_time" : orderBy} DESC
230
269
  ${limit !== null ? `LIMIT ${String(limit)}` : ""}`;
231
- const result = await adapter.executeQuery(sql);
232
- // Coerce numeric fields to JavaScript numbers
233
- const statements = (result.rows ?? []).map((row) => ({
234
- ...row,
235
- calls: toNum(row["calls"]),
236
- rows: toNum(row["rows"]),
237
- shared_blks_hit: toNum(row["shared_blks_hit"]),
238
- shared_blks_read: toNum(row["shared_blks_read"]),
239
- }));
240
- const response = {
241
- statements,
242
- count: statements.length,
243
- };
244
- // Add totalCount if results were limited
245
- if (limit !== null && statements.length === limit) {
246
- const countSql = `SELECT COUNT(*) as total FROM pg_stat_statements`;
247
- const countResult = await adapter.executeQuery(countSql);
248
- response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
249
- response["truncated"] = true;
270
+ const result = await adapter.executeQuery(sql);
271
+ // Coerce numeric fields to JavaScript numbers
272
+ const statements = (result.rows ?? []).map((row) => ({
273
+ ...row,
274
+ calls: toNum(row["calls"]),
275
+ rows: toNum(row["rows"]),
276
+ shared_blks_hit: toNum(row["shared_blks_hit"]),
277
+ shared_blks_read: toNum(row["shared_blks_read"]),
278
+ }));
279
+ const response = {
280
+ statements,
281
+ count: statements.length,
282
+ };
283
+ // Add totalCount if results were limited
284
+ if (limit !== null && statements.length === limit) {
285
+ const countSql = `SELECT COUNT(*) as total FROM pg_stat_statements`;
286
+ const countResult = await adapter.executeQuery(countSql);
287
+ response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
288
+ response["truncated"] = true;
289
+ }
290
+ return response;
291
+ }
292
+ catch (error) {
293
+ return {
294
+ success: false,
295
+ error: formatPostgresError(error, { tool: "pg_stat_statements" }),
296
+ };
250
297
  }
251
- return response;
252
298
  },
253
299
  };
254
300
  }
@@ -266,9 +312,10 @@ export function createStatActivityTool(adapter) {
266
312
  annotations: readOnly("Activity Stats"),
267
313
  icons: getToolIcons("performance", readOnly("Activity Stats")),
268
314
  handler: async (params, _context) => {
269
- const parsed = StatActivitySchema.parse(params);
270
- const idleClause = parsed.includeIdle === true ? "" : "AND state != 'idle'";
271
- const sql = `SELECT pid, usename, datname, client_addr, state,
315
+ try {
316
+ const parsed = StatActivitySchema.parse(params);
317
+ const idleClause = parsed.includeIdle === true ? "" : "AND state != 'idle'";
318
+ const sql = `SELECT pid, usename, datname, client_addr, state,
272
319
  query_start, state_change,
273
320
  now() - query_start as duration,
274
321
  query
@@ -277,16 +324,23 @@ export function createStatActivityTool(adapter) {
277
324
  AND backend_type = 'client backend'
278
325
  ${idleClause}
279
326
  ORDER BY query_start`;
280
- const result = await adapter.executeQuery(sql);
281
- // Count background workers for metadata
282
- const bgResult = await adapter.executeQuery(`SELECT COUNT(*)::int as count FROM pg_stat_activity
327
+ const result = await adapter.executeQuery(sql);
328
+ // Count background workers for metadata
329
+ const bgResult = await adapter.executeQuery(`SELECT COUNT(*)::int as count FROM pg_stat_activity
283
330
  WHERE pid != pg_backend_pid() AND backend_type != 'client backend'`);
284
- const bgCount = bgResult.rows?.[0]?.["count"] ?? 0;
285
- return {
286
- connections: result.rows,
287
- count: result.rows?.length ?? 0,
288
- backgroundWorkers: bgCount,
289
- };
331
+ const bgCount = bgResult.rows?.[0]?.["count"] ?? 0;
332
+ return {
333
+ connections: result.rows,
334
+ count: result.rows?.length ?? 0,
335
+ backgroundWorkers: bgCount,
336
+ };
337
+ }
338
+ catch (error) {
339
+ return {
340
+ success: false,
341
+ error: formatPostgresError(error, { tool: "pg_stat_activity" }),
342
+ };
343
+ }
290
344
  },
291
345
  };
292
346
  }
@@ -301,7 +355,7 @@ export function createUnusedIndexesTool(adapter) {
301
355
  .optional()
302
356
  .describe('Minimum index size to include (e.g., "1 MB")'),
303
357
  limit: z
304
- .number()
358
+ .any()
305
359
  .optional()
306
360
  .describe("Max indexes to return (default: 20, use 0 for all)"),
307
361
  summary: z
@@ -319,24 +373,32 @@ export function createUnusedIndexesTool(adapter) {
319
373
  annotations: readOnly("Unused Indexes"),
320
374
  icons: getToolIcons("performance", readOnly("Unused Indexes")),
321
375
  handler: async (params, _context) => {
322
- const parsed = UnusedIndexesSchema.parse(params);
323
- const limit = parsed.limit === 0 ? null : (parsed.limit ?? 20);
324
- // P154: Validate schema existence before querying
325
- if (parsed.schema !== undefined) {
326
- const validationError = await validatePerformanceTableExists(adapter, undefined, parsed.schema);
327
- if (validationError !== null) {
328
- return { success: false, error: validationError };
376
+ try {
377
+ const parsed = UnusedIndexesSchema.parse(params);
378
+ const rawLimit = Number(parsed.limit);
379
+ const limit = parsed.limit === undefined
380
+ ? 20
381
+ : isNaN(rawLimit)
382
+ ? 20
383
+ : rawLimit === 0
384
+ ? null
385
+ : rawLimit;
386
+ // P154: Validate schema existence before querying
387
+ if (parsed.schema !== undefined) {
388
+ const validationError = await validatePerformanceTableExists(adapter, undefined, parsed.schema);
389
+ if (validationError !== null) {
390
+ return { success: false, error: validationError };
391
+ }
329
392
  }
330
- }
331
- let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema') AND idx_scan = 0";
332
- const queryParams = [];
333
- if (parsed.schema !== undefined) {
334
- queryParams.push(parsed.schema);
335
- whereClause += ` AND schemaname = $${String(queryParams.length)}`;
336
- }
337
- // Summary mode - return aggregated stats
338
- if (parsed.summary === true) {
339
- const summarySql = `SELECT schemaname,
393
+ let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema') AND idx_scan = 0";
394
+ const queryParams = [];
395
+ if (parsed.schema !== undefined) {
396
+ queryParams.push(parsed.schema);
397
+ whereClause += ` AND schemaname = $${String(queryParams.length)}`;
398
+ }
399
+ // Summary mode - return aggregated stats
400
+ if (parsed.summary === true) {
401
+ const summarySql = `SELECT schemaname,
340
402
  COUNT(*) as unused_count,
341
403
  pg_size_pretty(SUM(pg_relation_size(indexrelid))) as total_size,
342
404
  SUM(pg_relation_size(indexrelid)) as total_size_bytes
@@ -345,24 +407,24 @@ export function createUnusedIndexesTool(adapter) {
345
407
  ${parsed.minSize !== undefined ? `AND pg_relation_size(indexrelid) >= pg_size_bytes('${parsed.minSize}')` : ""}
346
408
  GROUP BY schemaname
347
409
  ORDER BY SUM(pg_relation_size(indexrelid)) DESC`;
348
- const summaryResult = await adapter.executeQuery(summarySql, queryParams);
349
- const bySchema = (summaryResult.rows ?? []).map((row) => ({
350
- schema: row["schemaname"],
351
- unusedCount: toNum(row["unused_count"]),
352
- totalSize: row["total_size"],
353
- totalSizeBytes: toNum(row["total_size_bytes"]),
354
- }));
355
- const totalCount = bySchema.reduce((sum, s) => sum + (s.unusedCount ?? 0), 0);
356
- const totalBytes = bySchema.reduce((sum, s) => sum + (s.totalSizeBytes ?? 0), 0);
357
- return {
358
- summary: true,
359
- bySchema,
360
- totalCount,
361
- totalSizeBytes: totalBytes,
362
- hint: "Use summary=false or omit to see individual indexes.",
363
- };
364
- }
365
- const sql = `SELECT schemaname, relname as table_name, indexrelname as index_name,
410
+ const summaryResult = await adapter.executeQuery(summarySql, queryParams);
411
+ const bySchema = (summaryResult.rows ?? []).map((row) => ({
412
+ schema: row["schemaname"],
413
+ unusedCount: toNum(row["unused_count"]),
414
+ totalSize: row["total_size"],
415
+ totalSizeBytes: toNum(row["total_size_bytes"]),
416
+ }));
417
+ const totalCount = bySchema.reduce((sum, s) => sum + (s.unusedCount ?? 0), 0);
418
+ const totalBytes = bySchema.reduce((sum, s) => sum + (s.totalSizeBytes ?? 0), 0);
419
+ return {
420
+ summary: true,
421
+ bySchema,
422
+ totalCount,
423
+ totalSizeBytes: totalBytes,
424
+ hint: "Use summary=false or omit to see individual indexes.",
425
+ };
426
+ }
427
+ const sql = `SELECT schemaname, relname as table_name, indexrelname as index_name,
366
428
  idx_scan as scans, idx_tup_read as tuples_read,
367
429
  pg_size_pretty(pg_relation_size(indexrelid)) as size,
368
430
  pg_relation_size(indexrelid) as size_bytes
@@ -371,28 +433,35 @@ export function createUnusedIndexesTool(adapter) {
371
433
  ${parsed.minSize !== undefined ? `AND pg_relation_size(indexrelid) >= pg_size_bytes('${parsed.minSize}')` : ""}
372
434
  ORDER BY pg_relation_size(indexrelid) DESC
373
435
  ${limit !== null ? `LIMIT ${String(limit)}` : ""}`;
374
- const result = await adapter.executeQuery(sql, queryParams);
375
- // Coerce numeric fields to JavaScript numbers
376
- const unusedIndexes = (result.rows ?? []).map((row) => ({
377
- ...row,
378
- scans: toNum(row["scans"]),
379
- tuples_read: toNum(row["tuples_read"]),
380
- size_bytes: toNum(row["size_bytes"]),
381
- }));
382
- const response = {
383
- unusedIndexes,
384
- count: unusedIndexes.length,
385
- hint: "These indexes have never been used. Consider removing them to save disk space and improve write performance.",
386
- };
387
- // Add totalCount if results were limited
388
- if (limit !== null && unusedIndexes.length === limit) {
389
- const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_indexes WHERE ${whereClause}
436
+ const result = await adapter.executeQuery(sql, queryParams);
437
+ // Coerce numeric fields to JavaScript numbers
438
+ const unusedIndexes = (result.rows ?? []).map((row) => ({
439
+ ...row,
440
+ scans: toNum(row["scans"]),
441
+ tuples_read: toNum(row["tuples_read"]),
442
+ size_bytes: toNum(row["size_bytes"]),
443
+ }));
444
+ const response = {
445
+ unusedIndexes,
446
+ count: unusedIndexes.length,
447
+ hint: "These indexes have never been used. Consider removing them to save disk space and improve write performance.",
448
+ };
449
+ // Add totalCount if results were limited
450
+ if (limit !== null && unusedIndexes.length === limit) {
451
+ const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_indexes WHERE ${whereClause}
390
452
  ${parsed.minSize !== undefined ? `AND pg_relation_size(indexrelid) >= pg_size_bytes('${parsed.minSize}')` : ""}`;
391
- const countResult = await adapter.executeQuery(countSql, queryParams);
392
- response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
393
- response["truncated"] = true;
453
+ const countResult = await adapter.executeQuery(countSql, queryParams);
454
+ response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
455
+ response["truncated"] = true;
456
+ }
457
+ return response;
458
+ }
459
+ catch (error) {
460
+ return {
461
+ success: false,
462
+ error: formatPostgresError(error, { tool: "pg_unused_indexes" }),
463
+ };
394
464
  }
395
- return response;
396
465
  },
397
466
  };
398
467
  }
@@ -403,7 +472,7 @@ export function createDuplicateIndexesTool(adapter) {
403
472
  .optional()
404
473
  .describe("Schema to filter (default: all user schemas)"),
405
474
  limit: z
406
- .number()
475
+ .any()
407
476
  .optional()
408
477
  .describe("Max rows to return (default: 50, use 0 for all)"),
409
478
  });
@@ -417,22 +486,30 @@ export function createDuplicateIndexesTool(adapter) {
417
486
  annotations: readOnly("Duplicate Indexes"),
418
487
  icons: getToolIcons("performance", readOnly("Duplicate Indexes")),
419
488
  handler: async (params, _context) => {
420
- const parsed = DuplicateIndexesSchema.parse(params);
421
- const limit = parsed.limit === 0 ? null : (parsed.limit ?? 50);
422
- // P154: Validate schema existence before querying
423
- if (parsed.schema !== undefined) {
424
- const validationError = await validatePerformanceTableExists(adapter, undefined, parsed.schema);
425
- if (validationError !== null) {
426
- return { success: false, error: validationError };
489
+ try {
490
+ const parsed = DuplicateIndexesSchema.parse(params);
491
+ const rawLimit = Number(parsed.limit);
492
+ const limit = parsed.limit === undefined
493
+ ? 50
494
+ : isNaN(rawLimit)
495
+ ? 50
496
+ : rawLimit === 0
497
+ ? null
498
+ : rawLimit;
499
+ // P154: Validate schema existence before querying
500
+ if (parsed.schema !== undefined) {
501
+ const validationError = await validatePerformanceTableExists(adapter, undefined, parsed.schema);
502
+ if (validationError !== null) {
503
+ return { success: false, error: validationError };
504
+ }
427
505
  }
428
- }
429
- const queryParams = [];
430
- const schemaFilter = parsed.schema !== undefined
431
- ? (queryParams.push(parsed.schema),
432
- `AND n.nspname = $${String(queryParams.length)}`)
433
- : "AND n.nspname NOT IN ('pg_catalog', 'information_schema')";
434
- // Find indexes with the same leading column(s) on the same table
435
- const sql = `WITH index_cols AS (
506
+ const queryParams = [];
507
+ const schemaFilter = parsed.schema !== undefined
508
+ ? (queryParams.push(parsed.schema),
509
+ `AND n.nspname = $${String(queryParams.length)}`)
510
+ : "AND n.nspname NOT IN ('pg_catalog', 'information_schema')";
511
+ // Find indexes with the same leading column(s) on the same table
512
+ const sql = `WITH index_cols AS (
436
513
  SELECT
437
514
  n.nspname as schemaname,
438
515
  t.relname as tablename,
@@ -467,16 +544,16 @@ export function createDuplicateIndexesTool(adapter) {
467
544
  OR b.columns[1:array_length(a.columns, 1)] = a.columns)
468
545
  ORDER BY a.schemaname, a.tablename, a.size_bytes DESC
469
546
  ${limit !== null ? `LIMIT ${String(limit)}` : ""}`;
470
- const result = await adapter.executeQuery(sql, queryParams);
471
- const duplicates = result.rows ?? [];
472
- const response = {
473
- duplicateIndexes: duplicates,
474
- count: duplicates.length,
475
- hint: "EXACT_DUPLICATE: Remove one. OVERLAPPING/SUBSET: Smaller index may be redundant.",
476
- };
477
- // Add totalCount if results were limited
478
- if (limit !== null && duplicates.length === limit) {
479
- const countSql = `WITH index_cols AS (
547
+ const result = await adapter.executeQuery(sql, queryParams);
548
+ const duplicates = result.rows ?? [];
549
+ const response = {
550
+ duplicateIndexes: duplicates,
551
+ count: duplicates.length,
552
+ hint: "EXACT_DUPLICATE: Remove one. OVERLAPPING/SUBSET: Smaller index may be redundant.",
553
+ };
554
+ // Add totalCount if results were limited
555
+ if (limit !== null && duplicates.length === limit) {
556
+ const countSql = `WITH index_cols AS (
480
557
  SELECT
481
558
  n.nspname as schemaname,
482
559
  t.relname as tablename,
@@ -500,11 +577,18 @@ export function createDuplicateIndexesTool(adapter) {
500
577
  AND (a.columns = b.columns
501
578
  OR a.columns[1:array_length(b.columns, 1)] = b.columns
502
579
  OR b.columns[1:array_length(a.columns, 1)] = a.columns)`;
503
- const countResult = await adapter.executeQuery(countSql, queryParams);
504
- response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
505
- response["truncated"] = true;
580
+ const countResult = await adapter.executeQuery(countSql, queryParams);
581
+ response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
582
+ response["truncated"] = true;
583
+ }
584
+ return response;
585
+ }
586
+ catch (error) {
587
+ return {
588
+ success: false,
589
+ error: formatPostgresError(error, { tool: "pg_duplicate_indexes" }),
590
+ };
506
591
  }
507
- return response;
508
592
  },
509
593
  };
510
594
  }
@@ -513,7 +597,7 @@ export function createVacuumStatsTool(adapter) {
513
597
  schema: z.string().optional().describe("Schema to filter"),
514
598
  table: z.string().optional().describe("Table name to filter"),
515
599
  limit: z
516
- .number()
600
+ .any()
517
601
  .optional()
518
602
  .describe("Max rows to return (default: 50, use 0 for all)"),
519
603
  });
@@ -527,32 +611,40 @@ export function createVacuumStatsTool(adapter) {
527
611
  annotations: readOnly("Vacuum Stats"),
528
612
  icons: getToolIcons("performance", readOnly("Vacuum Stats")),
529
613
  handler: async (params, _context) => {
530
- const parsed = VacuumStatsSchema.parse(params);
531
- let table = parsed.table;
532
- let schema = parsed.schema;
533
- // Parse schema from table if it contains a dot (e.g., 'myschema.orders')
534
- if (table?.includes(".")) {
535
- const parts = table.split(".");
536
- schema = schema ?? parts[0];
537
- table = parts[1] ?? table;
538
- }
539
- const limit = parsed.limit === 0 ? null : (parsed.limit ?? 50);
540
- let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema')";
541
- const queryParams = [];
542
- if (schema !== undefined) {
543
- queryParams.push(schema);
544
- whereClause += ` AND schemaname = $${String(queryParams.length)}`;
545
- }
546
- if (table !== undefined) {
547
- queryParams.push(table);
548
- whereClause += ` AND relname = $${String(queryParams.length)}`;
549
- }
550
- // P154: Validate table/schema existence before querying
551
- const validationError = await validatePerformanceTableExists(adapter, table, schema);
552
- if (validationError !== null) {
553
- return { success: false, error: validationError };
554
- }
555
- const sql = `SELECT
614
+ try {
615
+ const parsed = VacuumStatsSchema.parse(params);
616
+ let table = parsed.table;
617
+ let schema = parsed.schema;
618
+ // Parse schema from table if it contains a dot (e.g., 'myschema.orders')
619
+ if (table?.includes(".")) {
620
+ const parts = table.split(".");
621
+ schema = schema ?? parts[0];
622
+ table = parts[1] ?? table;
623
+ }
624
+ const rawLimit = Number(parsed.limit);
625
+ const limit = parsed.limit === undefined
626
+ ? 50
627
+ : isNaN(rawLimit)
628
+ ? 50
629
+ : rawLimit === 0
630
+ ? null
631
+ : rawLimit;
632
+ let whereClause = "schemaname NOT IN ('pg_catalog', 'information_schema')";
633
+ const queryParams = [];
634
+ if (schema !== undefined) {
635
+ queryParams.push(schema);
636
+ whereClause += ` AND schemaname = $${String(queryParams.length)}`;
637
+ }
638
+ if (table !== undefined) {
639
+ queryParams.push(table);
640
+ whereClause += ` AND relname = $${String(queryParams.length)}`;
641
+ }
642
+ // P154: Validate table/schema existence before querying
643
+ const validationError = await validatePerformanceTableExists(adapter, table, schema);
644
+ if (validationError !== null) {
645
+ return { success: false, error: validationError };
646
+ }
647
+ const sql = `SELECT
556
648
  s.schemaname, s.relname as table_name,
557
649
  s.n_live_tup as live_tuples, s.n_dead_tup as dead_tuples,
558
650
  CASE WHEN s.n_live_tup > 0 THEN round((100.0 * s.n_dead_tup / s.n_live_tup)::numeric, 2) ELSE 0 END as dead_pct,
@@ -572,45 +664,52 @@ export function createVacuumStatsTool(adapter) {
572
664
  WHERE ${whereClause.replace(/schemaname/g, "s.schemaname").replace(/relname/g, "s.relname")}
573
665
  ORDER BY s.n_dead_tup DESC
574
666
  ${limit !== null ? `LIMIT ${String(limit)}` : ""}`;
575
- const result = await adapter.executeQuery(sql, queryParams);
576
- // Coerce numeric fields to JavaScript numbers
577
- const tables = (result.rows ?? []).map((row) => ({
578
- ...row,
579
- live_tuples: toNum(row["live_tuples"]),
580
- dead_tuples: toNum(row["dead_tuples"]),
581
- dead_pct: toNum(row["dead_pct"]),
582
- vacuum_count: toNum(row["vacuum_count"]),
583
- autovacuum_count: toNum(row["autovacuum_count"]),
584
- analyze_count: toNum(row["analyze_count"]),
585
- autoanalyze_count: toNum(row["autoanalyze_count"]),
586
- }));
587
- const response = {
588
- tables,
589
- count: tables.length,
590
- };
591
- // Add totalCount if results were limited
592
- if (limit !== null && tables.length === limit) {
593
- const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_tables WHERE ${whereClause}`;
594
- const countResult = await adapter.executeQuery(countSql, queryParams);
595
- response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
596
- response["truncated"] = true;
667
+ const result = await adapter.executeQuery(sql, queryParams);
668
+ // Coerce numeric fields to JavaScript numbers
669
+ const tables = (result.rows ?? []).map((row) => ({
670
+ ...row,
671
+ live_tuples: toNum(row["live_tuples"]),
672
+ dead_tuples: toNum(row["dead_tuples"]),
673
+ dead_pct: toNum(row["dead_pct"]),
674
+ vacuum_count: toNum(row["vacuum_count"]),
675
+ autovacuum_count: toNum(row["autovacuum_count"]),
676
+ analyze_count: toNum(row["analyze_count"]),
677
+ autoanalyze_count: toNum(row["autoanalyze_count"]),
678
+ }));
679
+ const response = {
680
+ tables,
681
+ count: tables.length,
682
+ };
683
+ // Add totalCount if results were limited
684
+ if (limit !== null && tables.length === limit) {
685
+ const countSql = `SELECT COUNT(*) as total FROM pg_stat_user_tables WHERE ${whereClause}`;
686
+ const countResult = await adapter.executeQuery(countSql, queryParams);
687
+ response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
688
+ response["truncated"] = true;
689
+ }
690
+ else {
691
+ response["truncated"] = false;
692
+ response["totalCount"] = tables.length;
693
+ }
694
+ return response;
597
695
  }
598
- else {
599
- response["truncated"] = false;
600
- response["totalCount"] = tables.length;
696
+ catch (error) {
697
+ return {
698
+ success: false,
699
+ error: formatPostgresError(error, { tool: "pg_vacuum_stats" }),
700
+ };
601
701
  }
602
- return response;
603
702
  },
604
703
  };
605
704
  }
606
705
  export function createQueryPlanStatsTool(adapter) {
607
706
  const QueryPlanStatsSchemaBase = z.object({
608
707
  limit: z
609
- .number()
708
+ .any()
610
709
  .optional()
611
710
  .describe("Number of queries to return (default: 20, use 0 for all)"),
612
711
  truncateQuery: z
613
- .number()
712
+ .any()
614
713
  .optional()
615
714
  .describe("Max query length in chars (default: 100, use 0 for full text)"),
616
715
  });
@@ -624,11 +723,26 @@ export function createQueryPlanStatsTool(adapter) {
624
723
  annotations: readOnly("Query Plan Stats"),
625
724
  icons: getToolIcons("performance", readOnly("Query Plan Stats")),
626
725
  handler: async (params, _context) => {
627
- const parsed = QueryPlanStatsSchema.parse(params);
628
- const limit = parsed.limit === 0 ? null : (parsed.limit ?? 20);
629
- const truncateLen = parsed.truncateQuery === 0 ? null : (parsed.truncateQuery ?? 100);
630
- // Check if pg_stat_statements is available with planning time columns
631
- const sql = `SELECT
726
+ try {
727
+ const parsed = QueryPlanStatsSchema.parse(params);
728
+ const rawLimit = Number(parsed.limit);
729
+ const limit = parsed.limit === undefined
730
+ ? 20
731
+ : isNaN(rawLimit)
732
+ ? 20
733
+ : rawLimit === 0
734
+ ? null
735
+ : rawLimit;
736
+ const rawTruncate = Number(parsed.truncateQuery);
737
+ const truncateLen = parsed.truncateQuery === undefined
738
+ ? 100
739
+ : isNaN(rawTruncate)
740
+ ? 100
741
+ : rawTruncate === 0
742
+ ? null
743
+ : rawTruncate;
744
+ // Check if pg_stat_statements is available with planning time columns
745
+ const sql = `SELECT
632
746
  query,
633
747
  calls,
634
748
  total_plan_time,
@@ -651,42 +765,49 @@ export function createQueryPlanStatsTool(adapter) {
651
765
  FROM pg_stat_statements
652
766
  ORDER BY total_plan_time + total_exec_time DESC
653
767
  ${limit !== null ? `LIMIT ${String(limit)}` : ""}`;
654
- const result = await adapter.executeQuery(sql);
655
- // Coerce numeric fields to JavaScript numbers and optionally truncate query
656
- const queryPlanStats = (result.rows ?? []).map((row) => {
657
- const queryVal = row["query"];
658
- const query = typeof queryVal === "string" ? queryVal : "";
659
- const truncatedQuery = truncateLen !== null && query.length > truncateLen
660
- ? query.substring(0, truncateLen) + "..."
661
- : query;
768
+ const result = await adapter.executeQuery(sql);
769
+ // Coerce numeric fields to JavaScript numbers and optionally truncate query
770
+ const queryPlanStats = (result.rows ?? []).map((row) => {
771
+ const queryVal = row["query"];
772
+ const query = typeof queryVal === "string" ? queryVal : "";
773
+ const truncatedQuery = truncateLen !== null && query.length > truncateLen
774
+ ? query.substring(0, truncateLen) + "..."
775
+ : query;
776
+ return {
777
+ query: truncatedQuery,
778
+ queryTruncated: truncateLen !== null && query.length > truncateLen,
779
+ calls: toNum(row["calls"]),
780
+ total_plan_time: row["total_plan_time"],
781
+ mean_plan_time: row["mean_plan_time"],
782
+ total_exec_time: row["total_exec_time"],
783
+ mean_exec_time: row["mean_exec_time"],
784
+ rows: toNum(row["rows"]),
785
+ plan_pct: toNum(row["plan_pct"]),
786
+ cache_hit_pct: toNum(row["cache_hit_pct"]),
787
+ shared_blks_hit: toNum(row["shared_blks_hit"]),
788
+ shared_blks_read: toNum(row["shared_blks_read"]),
789
+ };
790
+ });
791
+ const response = {
792
+ queryPlanStats,
793
+ count: queryPlanStats.length,
794
+ hint: "High plan_pct indicates queries spending significant time in planning. Consider prepared statements.",
795
+ };
796
+ // Add totalCount if results were limited
797
+ if (limit !== null && queryPlanStats.length === limit) {
798
+ const countSql = `SELECT COUNT(*) as total FROM pg_stat_statements`;
799
+ const countResult = await adapter.executeQuery(countSql);
800
+ response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
801
+ response["truncated"] = true;
802
+ }
803
+ return response;
804
+ }
805
+ catch (error) {
662
806
  return {
663
- query: truncatedQuery,
664
- queryTruncated: truncateLen !== null && query.length > truncateLen,
665
- calls: toNum(row["calls"]),
666
- total_plan_time: row["total_plan_time"],
667
- mean_plan_time: row["mean_plan_time"],
668
- total_exec_time: row["total_exec_time"],
669
- mean_exec_time: row["mean_exec_time"],
670
- rows: toNum(row["rows"]),
671
- plan_pct: toNum(row["plan_pct"]),
672
- cache_hit_pct: toNum(row["cache_hit_pct"]),
673
- shared_blks_hit: toNum(row["shared_blks_hit"]),
674
- shared_blks_read: toNum(row["shared_blks_read"]),
807
+ success: false,
808
+ error: formatPostgresError(error, { tool: "pg_query_plan_stats" }),
675
809
  };
676
- });
677
- const response = {
678
- queryPlanStats,
679
- count: queryPlanStats.length,
680
- hint: "High plan_pct indicates queries spending significant time in planning. Consider prepared statements.",
681
- };
682
- // Add totalCount if results were limited
683
- if (limit !== null && queryPlanStats.length === limit) {
684
- const countSql = `SELECT COUNT(*) as total FROM pg_stat_statements`;
685
- const countResult = await adapter.executeQuery(countSql);
686
- response["totalCount"] = toNum(countResult.rows?.[0]?.["total"]);
687
- response["truncated"] = true;
688
810
  }
689
- return response;
690
811
  },
691
812
  };
692
813
  }