@neverinfamous/postgres-mcp 1.3.0 → 2.1.0

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