@neverinfamous/mysql-mcp 2.3.0 → 3.0.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 (347) hide show
  1. package/.dockerignore +1 -0
  2. package/.gitattributes +18 -0
  3. package/.github/workflows/codeql.yml +2 -10
  4. package/.github/workflows/docker-publish.yml +15 -13
  5. package/CHANGELOG.md +287 -1
  6. package/DOCKER_README.md +100 -265
  7. package/Dockerfile +5 -0
  8. package/README.md +124 -59
  9. package/VERSION +1 -1
  10. package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
  11. package/dist/__tests__/mocks/adapter.js +2 -0
  12. package/dist/__tests__/mocks/adapter.js.map +1 -1
  13. package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
  14. package/dist/adapters/DatabaseAdapter.js +50 -9
  15. package/dist/adapters/DatabaseAdapter.js.map +1 -1
  16. package/dist/adapters/mysql/MySQLAdapter.d.ts +6 -0
  17. package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
  18. package/dist/adapters/mysql/MySQLAdapter.js +8 -0
  19. package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
  20. package/dist/adapters/mysql/SchemaManager.js +16 -15
  21. package/dist/adapters/mysql/SchemaManager.js.map +1 -1
  22. package/dist/adapters/mysql/prompts/index.js +10 -20
  23. package/dist/adapters/mysql/prompts/index.js.map +1 -1
  24. package/dist/adapters/mysql/prompts/proxysqlSetup.js +1 -1
  25. package/dist/adapters/mysql/resources/docstore.d.ts.map +1 -1
  26. package/dist/adapters/mysql/resources/docstore.js +10 -7
  27. package/dist/adapters/mysql/resources/docstore.js.map +1 -1
  28. package/dist/adapters/mysql/resources/events.js +11 -8
  29. package/dist/adapters/mysql/resources/events.js.map +1 -1
  30. package/dist/adapters/mysql/resources/indexes.d.ts.map +1 -1
  31. package/dist/adapters/mysql/resources/indexes.js +12 -15
  32. package/dist/adapters/mysql/resources/indexes.js.map +1 -1
  33. package/dist/adapters/mysql/resources/innodb.d.ts.map +1 -1
  34. package/dist/adapters/mysql/resources/innodb.js +20 -17
  35. package/dist/adapters/mysql/resources/innodb.js.map +1 -1
  36. package/dist/adapters/mysql/resources/locks.d.ts.map +1 -1
  37. package/dist/adapters/mysql/resources/locks.js +9 -6
  38. package/dist/adapters/mysql/resources/locks.js.map +1 -1
  39. package/dist/adapters/mysql/resources/performance.d.ts.map +1 -1
  40. package/dist/adapters/mysql/resources/performance.js +15 -15
  41. package/dist/adapters/mysql/resources/performance.js.map +1 -1
  42. package/dist/adapters/mysql/resources/spatial.d.ts.map +1 -1
  43. package/dist/adapters/mysql/resources/spatial.js +9 -6
  44. package/dist/adapters/mysql/resources/spatial.js.map +1 -1
  45. package/dist/adapters/mysql/resources/sysschema.d.ts.map +1 -1
  46. package/dist/adapters/mysql/resources/sysschema.js +12 -9
  47. package/dist/adapters/mysql/resources/sysschema.js.map +1 -1
  48. package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
  49. package/dist/adapters/mysql/tools/admin/backup.js +170 -121
  50. package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
  51. package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
  52. package/dist/adapters/mysql/tools/admin/maintenance.js +106 -57
  53. package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
  54. package/dist/adapters/mysql/tools/admin/monitoring.d.ts.map +1 -1
  55. package/dist/adapters/mysql/tools/admin/monitoring.js +183 -101
  56. package/dist/adapters/mysql/tools/admin/monitoring.js.map +1 -1
  57. package/dist/adapters/mysql/tools/cluster/group-replication.d.ts.map +1 -1
  58. package/dist/adapters/mysql/tools/cluster/group-replication.js +164 -120
  59. package/dist/adapters/mysql/tools/cluster/group-replication.js.map +1 -1
  60. package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
  61. package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +212 -145
  62. package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
  63. package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -1
  64. package/dist/adapters/mysql/tools/codemode/index.js +6 -4
  65. package/dist/adapters/mysql/tools/codemode/index.js.map +1 -1
  66. package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
  67. package/dist/adapters/mysql/tools/core.js +152 -29
  68. package/dist/adapters/mysql/tools/core.js.map +1 -1
  69. package/dist/adapters/mysql/tools/docstore.d.ts.map +1 -1
  70. package/dist/adapters/mysql/tools/docstore.js +340 -163
  71. package/dist/adapters/mysql/tools/docstore.js.map +1 -1
  72. package/dist/adapters/mysql/tools/events.d.ts.map +1 -1
  73. package/dist/adapters/mysql/tools/events.js +284 -198
  74. package/dist/adapters/mysql/tools/events.js.map +1 -1
  75. package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
  76. package/dist/adapters/mysql/tools/json/core.js +11 -39
  77. package/dist/adapters/mysql/tools/json/core.js.map +1 -1
  78. package/dist/adapters/mysql/tools/json/enhanced.d.ts.map +1 -1
  79. package/dist/adapters/mysql/tools/json/enhanced.js +15 -33
  80. package/dist/adapters/mysql/tools/json/enhanced.js.map +1 -1
  81. package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
  82. package/dist/adapters/mysql/tools/json/helpers.js +13 -24
  83. package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
  84. package/dist/adapters/mysql/tools/partitioning.js +3 -0
  85. package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
  86. package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
  87. package/dist/adapters/mysql/tools/performance/analysis.js +89 -60
  88. package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
  89. package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
  90. package/dist/adapters/mysql/tools/performance/optimization.js +151 -127
  91. package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
  92. package/dist/adapters/mysql/tools/proxysql.d.ts +1 -1
  93. package/dist/adapters/mysql/tools/proxysql.d.ts.map +1 -1
  94. package/dist/adapters/mysql/tools/proxysql.js +289 -176
  95. package/dist/adapters/mysql/tools/proxysql.js.map +1 -1
  96. package/dist/adapters/mysql/tools/replication.js +75 -49
  97. package/dist/adapters/mysql/tools/replication.js.map +1 -1
  98. package/dist/adapters/mysql/tools/roles.d.ts.map +1 -1
  99. package/dist/adapters/mysql/tools/roles.js +224 -182
  100. package/dist/adapters/mysql/tools/roles.js.map +1 -1
  101. package/dist/adapters/mysql/tools/router.d.ts.map +1 -1
  102. package/dist/adapters/mysql/tools/router.js +168 -67
  103. package/dist/adapters/mysql/tools/router.js.map +1 -1
  104. package/dist/adapters/mysql/tools/schema/constraints.d.ts.map +1 -1
  105. package/dist/adapters/mysql/tools/schema/constraints.js +21 -3
  106. package/dist/adapters/mysql/tools/schema/constraints.js.map +1 -1
  107. package/dist/adapters/mysql/tools/schema/management.d.ts.map +1 -1
  108. package/dist/adapters/mysql/tools/schema/management.js +61 -14
  109. package/dist/adapters/mysql/tools/schema/management.js.map +1 -1
  110. package/dist/adapters/mysql/tools/schema/routines.d.ts.map +1 -1
  111. package/dist/adapters/mysql/tools/schema/routines.js +27 -4
  112. package/dist/adapters/mysql/tools/schema/routines.js.map +1 -1
  113. package/dist/adapters/mysql/tools/schema/scheduled_events.d.ts.map +1 -1
  114. package/dist/adapters/mysql/tools/schema/scheduled_events.js +24 -3
  115. package/dist/adapters/mysql/tools/schema/scheduled_events.js.map +1 -1
  116. package/dist/adapters/mysql/tools/schema/triggers.d.ts.map +1 -1
  117. package/dist/adapters/mysql/tools/schema/triggers.js +23 -2
  118. package/dist/adapters/mysql/tools/schema/triggers.js.map +1 -1
  119. package/dist/adapters/mysql/tools/schema/views.d.ts.map +1 -1
  120. package/dist/adapters/mysql/tools/schema/views.js +47 -7
  121. package/dist/adapters/mysql/tools/schema/views.js.map +1 -1
  122. package/dist/adapters/mysql/tools/security/audit.d.ts.map +1 -1
  123. package/dist/adapters/mysql/tools/security/audit.js +102 -34
  124. package/dist/adapters/mysql/tools/security/audit.js.map +1 -1
  125. package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
  126. package/dist/adapters/mysql/tools/security/data-protection.js +264 -205
  127. package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
  128. package/dist/adapters/mysql/tools/security/encryption.d.ts.map +1 -1
  129. package/dist/adapters/mysql/tools/security/encryption.js +137 -104
  130. package/dist/adapters/mysql/tools/security/encryption.js.map +1 -1
  131. package/dist/adapters/mysql/tools/shell/backup.d.ts.map +1 -1
  132. package/dist/adapters/mysql/tools/shell/backup.js +71 -59
  133. package/dist/adapters/mysql/tools/shell/backup.js.map +1 -1
  134. package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
  135. package/dist/adapters/mysql/tools/shell/restore.js +61 -47
  136. package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
  137. package/dist/adapters/mysql/tools/spatial/geometry.d.ts.map +1 -1
  138. package/dist/adapters/mysql/tools/spatial/geometry.js +19 -5
  139. package/dist/adapters/mysql/tools/spatial/geometry.js.map +1 -1
  140. package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
  141. package/dist/adapters/mysql/tools/spatial/operations.js +42 -17
  142. package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
  143. package/dist/adapters/mysql/tools/spatial/queries.d.ts.map +1 -1
  144. package/dist/adapters/mysql/tools/spatial/queries.js +109 -57
  145. package/dist/adapters/mysql/tools/spatial/queries.js.map +1 -1
  146. package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
  147. package/dist/adapters/mysql/tools/spatial/setup.js +103 -50
  148. package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
  149. package/dist/adapters/mysql/tools/stats/comparative.d.ts.map +1 -1
  150. package/dist/adapters/mysql/tools/stats/comparative.js +128 -79
  151. package/dist/adapters/mysql/tools/stats/comparative.js.map +1 -1
  152. package/dist/adapters/mysql/tools/stats/descriptive.d.ts.map +1 -1
  153. package/dist/adapters/mysql/tools/stats/descriptive.js +174 -102
  154. package/dist/adapters/mysql/tools/stats/descriptive.js.map +1 -1
  155. package/dist/adapters/mysql/tools/sysschema/activity.d.ts.map +1 -1
  156. package/dist/adapters/mysql/tools/sysschema/activity.js +50 -25
  157. package/dist/adapters/mysql/tools/sysschema/activity.js.map +1 -1
  158. package/dist/adapters/mysql/tools/sysschema/performance.d.ts.map +1 -1
  159. package/dist/adapters/mysql/tools/sysschema/performance.js +121 -66
  160. package/dist/adapters/mysql/tools/sysschema/performance.js.map +1 -1
  161. package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
  162. package/dist/adapters/mysql/tools/sysschema/resources.js +101 -64
  163. package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
  164. package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
  165. package/dist/adapters/mysql/tools/text/fulltext.js +18 -32
  166. package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
  167. package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
  168. package/dist/adapters/mysql/tools/transactions.js +48 -23
  169. package/dist/adapters/mysql/tools/transactions.js.map +1 -1
  170. package/dist/adapters/mysql/types/proxysql-types.d.ts +15 -0
  171. package/dist/adapters/mysql/types/proxysql-types.d.ts.map +1 -1
  172. package/dist/adapters/mysql/types/proxysql-types.js +33 -1
  173. package/dist/adapters/mysql/types/proxysql-types.js.map +1 -1
  174. package/dist/adapters/mysql/types/router-types.d.ts +1 -1
  175. package/dist/adapters/mysql/types/router-types.js +1 -1
  176. package/dist/adapters/mysql/types/router-types.js.map +1 -1
  177. package/dist/adapters/mysql/types/shell-types.js +2 -2
  178. package/dist/adapters/mysql/types/shell-types.js.map +1 -1
  179. package/dist/adapters/mysql/types.d.ts +485 -21
  180. package/dist/adapters/mysql/types.d.ts.map +1 -1
  181. package/dist/adapters/mysql/types.js +546 -19
  182. package/dist/adapters/mysql/types.js.map +1 -1
  183. package/dist/auth/scopes.js +1 -1
  184. package/dist/auth/scopes.js.map +1 -1
  185. package/dist/codemode/api.d.ts +3 -2
  186. package/dist/codemode/api.d.ts.map +1 -1
  187. package/dist/codemode/api.js +80 -5
  188. package/dist/codemode/api.js.map +1 -1
  189. package/dist/codemode/sandbox-factory.js +1 -1
  190. package/dist/codemode/sandbox-factory.js.map +1 -1
  191. package/dist/codemode/types.d.ts +26 -0
  192. package/dist/codemode/types.d.ts.map +1 -1
  193. package/dist/codemode/types.js +2 -0
  194. package/dist/codemode/types.js.map +1 -1
  195. package/dist/codemode/worker-sandbox.d.ts +4 -2
  196. package/dist/codemode/worker-sandbox.d.ts.map +1 -1
  197. package/dist/codemode/worker-sandbox.js +66 -7
  198. package/dist/codemode/worker-sandbox.js.map +1 -1
  199. package/dist/codemode/worker-script.d.ts +3 -0
  200. package/dist/codemode/worker-script.d.ts.map +1 -1
  201. package/dist/codemode/worker-script.js +128 -75
  202. package/dist/codemode/worker-script.js.map +1 -1
  203. package/dist/constants/ServerInstructions.d.ts +1 -1
  204. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  205. package/dist/constants/ServerInstructions.js +37 -31
  206. package/dist/constants/ServerInstructions.js.map +1 -1
  207. package/dist/filtering/ToolConstants.d.ts +1 -1
  208. package/dist/filtering/ToolConstants.d.ts.map +1 -1
  209. package/dist/filtering/ToolConstants.js +1 -2
  210. package/dist/filtering/ToolConstants.js.map +1 -1
  211. package/dist/pool/ConnectionPool.d.ts.map +1 -1
  212. package/dist/pool/ConnectionPool.js.map +1 -1
  213. package/dist/transports/http.d.ts.map +1 -1
  214. package/dist/transports/http.js +6 -0
  215. package/dist/transports/http.js.map +1 -1
  216. package/dist/utils/validators.d.ts +1 -1
  217. package/dist/utils/validators.d.ts.map +1 -1
  218. package/dist/utils/validators.js.map +1 -1
  219. package/package.json +4 -4
  220. package/releases/v2.3.0-release-notes.md +20 -20
  221. package/releases/v2.3.1-release-notes.md +34 -0
  222. package/releases/v3.0.0-release-notes.md +81 -0
  223. package/src/__tests__/mocks/adapter.ts +3 -0
  224. package/src/__tests__/perf.test.ts +6 -6
  225. package/src/adapters/DatabaseAdapter.ts +58 -9
  226. package/src/adapters/__tests__/DatabaseAdapter.test.ts +89 -8
  227. package/src/adapters/mysql/MySQLAdapter.ts +17 -2
  228. package/src/adapters/mysql/SchemaManager.ts +21 -21
  229. package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
  230. package/src/adapters/mysql/prompts/index.ts +12 -22
  231. package/src/adapters/mysql/prompts/proxysqlSetup.ts +1 -1
  232. package/src/adapters/mysql/resources/docstore.ts +13 -10
  233. package/src/adapters/mysql/resources/events.ts +12 -12
  234. package/src/adapters/mysql/resources/indexes.ts +17 -19
  235. package/src/adapters/mysql/resources/innodb.ts +23 -22
  236. package/src/adapters/mysql/resources/locks.ts +9 -7
  237. package/src/adapters/mysql/resources/performance.ts +23 -18
  238. package/src/adapters/mysql/resources/spatial.ts +9 -7
  239. package/src/adapters/mysql/resources/sysschema.ts +12 -11
  240. package/src/adapters/mysql/tools/__tests__/core.test.ts +126 -55
  241. package/src/adapters/mysql/tools/__tests__/docstore.test.ts +459 -88
  242. package/src/adapters/mysql/tools/__tests__/events.test.ts +281 -103
  243. package/src/adapters/mysql/tools/__tests__/proxysql.test.ts +128 -28
  244. package/src/adapters/mysql/tools/__tests__/replication.test.ts +48 -2
  245. package/src/adapters/mysql/tools/__tests__/roles.test.ts +15 -18
  246. package/src/adapters/mysql/tools/__tests__/router.test.ts +32 -5
  247. package/src/adapters/mysql/tools/__tests__/security.test.ts +126 -2
  248. package/src/adapters/mysql/tools/__tests__/security_injection.test.ts +84 -76
  249. package/src/adapters/mysql/tools/__tests__/security_integration.test.ts +47 -50
  250. package/src/adapters/mysql/tools/__tests__/spatial.test.ts +11 -10
  251. package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +54 -38
  252. package/src/adapters/mysql/tools/__tests__/stats.test.ts +285 -152
  253. package/src/adapters/mysql/tools/__tests__/transactions.test.ts +13 -13
  254. package/src/adapters/mysql/tools/admin/__tests__/backup.test.ts +171 -25
  255. package/src/adapters/mysql/tools/admin/__tests__/maintenance.test.ts +240 -4
  256. package/src/adapters/mysql/tools/admin/__tests__/monitoring-summary.test.ts +274 -0
  257. package/src/adapters/mysql/tools/admin/__tests__/monitoring.test.ts +94 -5
  258. package/src/adapters/mysql/tools/admin/backup.ts +193 -143
  259. package/src/adapters/mysql/tools/admin/maintenance.ts +118 -69
  260. package/src/adapters/mysql/tools/admin/monitoring.ts +201 -125
  261. package/src/adapters/mysql/tools/cluster/__tests__/group-replication.test.ts +69 -0
  262. package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +141 -0
  263. package/src/adapters/mysql/tools/cluster/group-replication.ts +172 -132
  264. package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +231 -157
  265. package/src/adapters/mysql/tools/codemode/__tests__/codemode-tool.test.ts +227 -0
  266. package/src/adapters/mysql/tools/codemode/index.ts +5 -3
  267. package/src/adapters/mysql/tools/core.ts +152 -38
  268. package/src/adapters/mysql/tools/docstore.ts +422 -205
  269. package/src/adapters/mysql/tools/events.ts +334 -233
  270. package/src/adapters/mysql/tools/json/__tests__/core.test.ts +20 -0
  271. package/src/adapters/mysql/tools/json/__tests__/enhanced.test.ts +82 -50
  272. package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +42 -3
  273. package/src/adapters/mysql/tools/json/core.ts +21 -42
  274. package/src/adapters/mysql/tools/json/enhanced.ts +22 -37
  275. package/src/adapters/mysql/tools/json/helpers.ts +21 -25
  276. package/src/adapters/mysql/tools/partitioning.ts +3 -0
  277. package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +98 -5
  278. package/src/adapters/mysql/tools/performance/__tests__/optimization-coverage.test.ts +515 -0
  279. package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +187 -0
  280. package/src/adapters/mysql/tools/performance/analysis.ts +95 -69
  281. package/src/adapters/mysql/tools/performance/optimization.ts +182 -153
  282. package/src/adapters/mysql/tools/proxysql.ts +314 -209
  283. package/src/adapters/mysql/tools/replication.ts +84 -57
  284. package/src/adapters/mysql/tools/roles.ts +274 -226
  285. package/src/adapters/mysql/tools/router.ts +181 -85
  286. package/src/adapters/mysql/tools/schema/__tests__/constraints.test.ts +13 -0
  287. package/src/adapters/mysql/tools/schema/__tests__/management.test.ts +60 -25
  288. package/src/adapters/mysql/tools/schema/__tests__/scheduled_events.test.ts +11 -0
  289. package/src/adapters/mysql/tools/schema/__tests__/triggers.test.ts +25 -4
  290. package/src/adapters/mysql/tools/schema/__tests__/views.test.ts +46 -14
  291. package/src/adapters/mysql/tools/schema/constraints.ts +22 -3
  292. package/src/adapters/mysql/tools/schema/management.ts +60 -15
  293. package/src/adapters/mysql/tools/schema/routines.ts +26 -4
  294. package/src/adapters/mysql/tools/schema/scheduled_events.ts +25 -3
  295. package/src/adapters/mysql/tools/schema/triggers.ts +27 -2
  296. package/src/adapters/mysql/tools/schema/views.ts +46 -8
  297. package/src/adapters/mysql/tools/security/__tests__/audit.test.ts +90 -4
  298. package/src/adapters/mysql/tools/security/audit.ts +113 -39
  299. package/src/adapters/mysql/tools/security/data-protection.ts +293 -233
  300. package/src/adapters/mysql/tools/security/encryption.ts +172 -139
  301. package/src/adapters/mysql/tools/shell/__tests__/backup.test.ts +29 -0
  302. package/src/adapters/mysql/tools/shell/backup.ts +90 -73
  303. package/src/adapters/mysql/tools/shell/restore.ts +62 -48
  304. package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +22 -14
  305. package/src/adapters/mysql/tools/spatial/__tests__/queries.test.ts +65 -51
  306. package/src/adapters/mysql/tools/spatial/geometry.ts +23 -7
  307. package/src/adapters/mysql/tools/spatial/operations.ts +60 -31
  308. package/src/adapters/mysql/tools/spatial/queries.ts +142 -65
  309. package/src/adapters/mysql/tools/spatial/setup.ts +121 -55
  310. package/src/adapters/mysql/tools/stats/__tests__/comparative.test.ts +12 -10
  311. package/src/adapters/mysql/tools/stats/comparative.ts +150 -98
  312. package/src/adapters/mysql/tools/stats/descriptive.ts +204 -127
  313. package/src/adapters/mysql/tools/sysschema/__tests__/error-paths.test.ts +222 -0
  314. package/src/adapters/mysql/tools/sysschema/__tests__/performance.test.ts +45 -0
  315. package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +6 -3
  316. package/src/adapters/mysql/tools/sysschema/activity.ts +52 -27
  317. package/src/adapters/mysql/tools/sysschema/performance.ts +132 -68
  318. package/src/adapters/mysql/tools/sysschema/resources.ts +105 -67
  319. package/src/adapters/mysql/tools/text/__tests__/fulltext.test.ts +45 -17
  320. package/src/adapters/mysql/tools/text/fulltext.ts +27 -38
  321. package/src/adapters/mysql/tools/transactions.ts +49 -24
  322. package/src/adapters/mysql/types/proxysql-types.ts +38 -1
  323. package/src/adapters/mysql/types/router-types.ts +1 -1
  324. package/src/adapters/mysql/types/shell-types.ts +2 -2
  325. package/src/adapters/mysql/types.ts +632 -19
  326. package/src/auth/__tests__/scopes.test.ts +2 -2
  327. package/src/auth/scopes.ts +1 -1
  328. package/src/codemode/__tests__/api.test.ts +417 -0
  329. package/src/codemode/__tests__/sandbox-factory.test.ts +158 -0
  330. package/src/codemode/__tests__/sandbox.test.ts +301 -0
  331. package/src/codemode/__tests__/security.test.ts +368 -0
  332. package/src/codemode/__tests__/worker-sandbox.test.ts +179 -0
  333. package/src/codemode/__tests__/worker-script.test.ts +226 -0
  334. package/src/codemode/api.ts +89 -5
  335. package/src/codemode/sandbox-factory.ts +1 -1
  336. package/src/codemode/types.ts +34 -0
  337. package/src/codemode/worker-sandbox.ts +74 -7
  338. package/src/codemode/worker-script.ts +157 -86
  339. package/src/constants/ServerInstructions.ts +37 -31
  340. package/src/filtering/ToolConstants.ts +1 -2
  341. package/src/filtering/__tests__/ToolFilter.test.ts +9 -9
  342. package/src/pool/ConnectionPool.ts +4 -1
  343. package/src/transports/__tests__/http.test.ts +15 -3
  344. package/src/transports/http.ts +12 -0
  345. package/src/utils/validators.ts +2 -1
  346. package/vitest.config.ts +3 -1
  347. package/CODE_MODE.md +0 -245
@@ -5,6 +5,9 @@
5
5
  * 9 tools total.
6
6
  */
7
7
  import { z } from "zod";
8
+ const IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
9
+ // Valid JSON path: $, $.field, $.field.sub, $.field[0], $[0], $[*]
10
+ const JSON_PATH_RE = /^(\$)((\.([a-zA-Z_][a-zA-Z0-9_]*))|((\[\d+\])|(\[\*\])))*$/;
8
11
  const ListCollectionsSchema = z.object({
9
12
  schema: z.string().optional().describe("Schema name (defaults to current)"),
10
13
  });
@@ -33,6 +36,7 @@ const DropCollectionSchema = z.object({
33
36
  });
34
37
  const FindSchema = z.object({
35
38
  collection: z.string(),
39
+ schema: z.string().optional(),
36
40
  filter: z.string().optional().describe("JSON path expression filter"),
37
41
  fields: z.array(z.string()).optional(),
38
42
  limit: z.number().default(100),
@@ -40,12 +44,14 @@ const FindSchema = z.object({
40
44
  });
41
45
  const AddDocSchema = z.object({
42
46
  collection: z.string(),
47
+ schema: z.string().optional(),
43
48
  documents: z
44
49
  .array(z.record(z.string(), z.unknown()))
45
50
  .describe("Documents to add"),
46
51
  });
47
52
  const ModifyDocSchema = z.object({
48
53
  collection: z.string(),
54
+ schema: z.string().optional(),
49
55
  filter: z
50
56
  .string()
51
57
  .describe("Filter: JSON path for existence ($.name) OR _id value for specific document"),
@@ -54,6 +60,7 @@ const ModifyDocSchema = z.object({
54
60
  });
55
61
  const RemoveDocSchema = z.object({
56
62
  collection: z.string(),
63
+ schema: z.string().optional(),
57
64
  filter: z
58
65
  .string()
59
66
  .describe("Filter: JSON path for existence ($.name) OR _id value for specific document"),
@@ -73,7 +80,12 @@ function parseDocFilter(filter) {
73
80
  // Check for simple field=value pattern
74
81
  const eqMatch = /^([a-zA-Z_][a-zA-Z0-9_]*)=(.+)$/.exec(filter);
75
82
  if (eqMatch) {
76
- const [, field, value] = eqMatch;
83
+ const field = eqMatch[1] ?? "";
84
+ const value = eqMatch[2] ?? "";
85
+ // Defense-in-depth: validate field name against identifier regex
86
+ if (!IDENTIFIER_RE.test(field)) {
87
+ throw new Error(`Invalid field name in filter: "${field}". Field names must be valid identifiers.`);
88
+ }
77
89
  // Try to parse as number
78
90
  const numVal = Number(value);
79
91
  if (!isNaN(numVal)) {
@@ -95,6 +107,7 @@ function parseDocFilter(filter) {
95
107
  }
96
108
  const CreateDocIndexSchema = z.object({
97
109
  collection: z.string(),
110
+ schema: z.string().optional(),
98
111
  name: z.string(),
99
112
  fields: z.array(z.object({
100
113
  path: z.string(),
@@ -110,12 +123,35 @@ const CollectionInfoSchema = z.object({
110
123
  schema: z.string().optional(),
111
124
  });
112
125
  /**
113
- * Check if a collection (table) exists in the current database.
126
+ * Check if a collection (table) exists in the specified (or current) database.
127
+ * Returns a discriminated result distinguishing schema-not-found from collection-not-found.
114
128
  */
115
- async function checkCollectionExists(adapter, collection) {
129
+ async function checkCollectionExists(adapter, collection, schema) {
130
+ // When schema is explicitly provided, check schema existence first
131
+ if (schema) {
132
+ const schemaCheck = await adapter.executeQuery("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?", [schema]);
133
+ if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
134
+ return { exists: false, reason: "schema", name: schema };
135
+ }
136
+ }
116
137
  const result = await adapter.executeQuery(`SELECT 1 FROM information_schema.TABLES
117
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`, [collection]);
118
- return (result.rows?.length ?? 0) > 0;
138
+ WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?`, [schema ?? null, collection]);
139
+ if ((result.rows?.length ?? 0) > 0) {
140
+ return { exists: true };
141
+ }
142
+ return { exists: false, reason: "collection", name: collection };
143
+ }
144
+ /**
145
+ * Build a backtick-escaped qualified table reference.
146
+ */
147
+ function escapeTableRef(name, schema) {
148
+ return schema ? `\`${schema}\`.\`${name}\`` : `\`${name}\``;
149
+ }
150
+ /**
151
+ * Format a ZodError into a human-readable string.
152
+ */
153
+ function formatZodError(err) {
154
+ return err.issues.map((i) => i.message).join("; ");
119
155
  }
120
156
  export function getDocStoreTools(adapter) {
121
157
  return [
@@ -128,31 +164,43 @@ export function getDocStoreTools(adapter) {
128
164
  requiredScopes: ["read"],
129
165
  annotations: { readOnlyHint: true, idempotentHint: true },
130
166
  handler: async (params, _context) => {
131
- const { schema } = ListCollectionsSchema.parse(params);
132
- // P154: Schema existence check when explicitly provided
133
- if (schema) {
134
- const schemaCheck = await adapter.executeQuery("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?", [schema]);
135
- if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
136
- return { exists: false, schema };
167
+ try {
168
+ const { schema } = ListCollectionsSchema.parse(params);
169
+ // P154: Schema existence check when explicitly provided
170
+ if (schema) {
171
+ const schemaCheck = await adapter.executeQuery("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?", [schema]);
172
+ if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
173
+ return { exists: false, schema };
174
+ }
137
175
  }
138
- }
139
- const query = `
176
+ const query = `
140
177
  SELECT TABLE_NAME as name, TABLE_COMMENT as comment, TABLE_ROWS as rowCount
141
178
  FROM information_schema.TABLES
142
179
  WHERE TABLE_SCHEMA = COALESCE(?, DATABASE())
143
180
  AND TABLE_NAME IN (
144
- SELECT TABLE_NAME FROM information_schema.COLUMNS
145
- WHERE COLUMN_NAME = 'doc' AND DATA_TYPE = 'json'
146
- AND TABLE_SCHEMA = COALESCE(?, DATABASE())
181
+ SELECT c1.TABLE_NAME FROM information_schema.COLUMNS c1
182
+ JOIN information_schema.COLUMNS c2
183
+ ON c1.TABLE_SCHEMA = c2.TABLE_SCHEMA AND c1.TABLE_NAME = c2.TABLE_NAME
184
+ WHERE c1.COLUMN_NAME = 'doc' AND c1.DATA_TYPE = 'json'
185
+ AND c2.COLUMN_NAME = '_id'
186
+ AND c1.TABLE_SCHEMA = COALESCE(?, DATABASE())
147
187
  )`;
148
- const result = await adapter.executeQuery(query, [
149
- schema ?? null,
150
- schema ?? null,
151
- ]);
152
- return {
153
- collections: result.rows ?? [],
154
- count: result.rows?.length ?? 0,
155
- };
188
+ const result = await adapter.executeQuery(query, [
189
+ schema ?? null,
190
+ schema ?? null,
191
+ ]);
192
+ return {
193
+ collections: result.rows ?? [],
194
+ count: result.rows?.length ?? 0,
195
+ };
196
+ }
197
+ catch (error) {
198
+ if (error instanceof z.ZodError) {
199
+ return { success: false, error: formatZodError(error) };
200
+ }
201
+ const message = error instanceof Error ? error.message : String(error);
202
+ return { success: false, error: message };
203
+ }
156
204
  },
157
205
  },
158
206
  {
@@ -164,38 +212,67 @@ export function getDocStoreTools(adapter) {
164
212
  requiredScopes: ["write"],
165
213
  annotations: { readOnlyHint: false },
166
214
  handler: async (params, _context) => {
167
- const { name, ifNotExists, validation } = CreateCollectionSchema.parse(params);
168
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))
169
- throw new Error("Invalid collection name");
170
- const createClause = ifNotExists
171
- ? "CREATE TABLE IF NOT EXISTS"
172
- : "CREATE TABLE";
173
- let sql = `${createClause} \`${name}\` (
215
+ try {
216
+ const { name, schema, ifNotExists, validation } = CreateCollectionSchema.parse(params);
217
+ if (!IDENTIFIER_RE.test(name))
218
+ return { success: false, error: "Invalid collection name" };
219
+ if (schema && !IDENTIFIER_RE.test(schema))
220
+ return { success: false, error: "Invalid schema name" };
221
+ const tableRef = escapeTableRef(name, schema);
222
+ // Pre-check existence when ifNotExists is true so we can report accurately
223
+ if (ifNotExists) {
224
+ const check = await checkCollectionExists(adapter, name, schema);
225
+ if (check.exists) {
226
+ return {
227
+ success: true,
228
+ skipped: true,
229
+ collection: name,
230
+ reason: "Collection already exists",
231
+ };
232
+ }
233
+ // If schema doesn't exist, report it even with ifNotExists
234
+ if (check.reason === "schema") {
235
+ return { exists: false, schema: check.name };
236
+ }
237
+ }
238
+ const createClause = ifNotExists
239
+ ? "CREATE TABLE IF NOT EXISTS"
240
+ : "CREATE TABLE";
241
+ let sql = `${createClause} ${tableRef} (
174
242
  doc JSON,
175
243
  _id VARBINARY(32) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$._id'))) STORED PRIMARY KEY,
176
244
  _json_schema JSON GENERATED ALWAYS AS ('{}') VIRTUAL
177
245
  ) ENGINE=InnoDB`;
178
- if (validation?.level && validation.level !== "OFF") {
179
- const schemaJson = JSON.stringify(validation.schema ?? {});
180
- sql = `${createClause} \`${name}\` (
246
+ if (validation?.level && validation.level !== "OFF") {
247
+ const schemaJson = JSON.stringify(validation.schema ?? {});
248
+ sql = `${createClause} ${tableRef} (
181
249
  doc JSON,
182
250
  _id VARBINARY(32) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$._id'))) STORED PRIMARY KEY,
183
251
  CONSTRAINT chk_schema CHECK (JSON_SCHEMA_VALID('${schemaJson}', doc))
184
252
  ) ENGINE=InnoDB`;
185
- }
186
- try {
253
+ }
187
254
  await adapter.executeQuery(sql);
255
+ adapter.clearSchemaCache();
188
256
  return { success: true, collection: name };
189
257
  }
190
258
  catch (error) {
259
+ if (error instanceof z.ZodError) {
260
+ return { success: false, error: formatZodError(error) };
261
+ }
191
262
  const message = error instanceof Error ? error.message : String(error);
263
+ if (message.toLowerCase().includes("unknown database")) {
264
+ return {
265
+ exists: false,
266
+ schema: params?.schema ?? "unknown",
267
+ };
268
+ }
192
269
  if (message.toLowerCase().includes("already exists")) {
193
270
  return {
194
271
  success: false,
195
- reason: `Collection '${name}' already exists`,
272
+ error: `Collection '${params?.name ?? "unknown"}' already exists`,
196
273
  };
197
274
  }
198
- throw error;
275
+ return { success: false, error: message };
199
276
  }
200
277
  },
201
278
  },
@@ -208,33 +285,47 @@ export function getDocStoreTools(adapter) {
208
285
  requiredScopes: ["admin"],
209
286
  annotations: { readOnlyHint: false, destructiveHint: true },
210
287
  handler: async (params, _context) => {
211
- const { name, ifExists } = DropCollectionSchema.parse(params);
212
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))
213
- throw new Error("Invalid collection name");
214
- // Pre-check existence when ifExists is true so we can report accurately
215
- if (ifExists) {
216
- const exists = await checkCollectionExists(adapter, name);
217
- if (!exists) {
218
- return {
219
- success: true,
220
- collection: name,
221
- message: "Collection did not exist",
222
- };
223
- }
224
- }
225
288
  try {
226
- await adapter.executeQuery(`DROP TABLE ${ifExists ? "IF EXISTS " : ""}\`${name}\``);
289
+ const { name, schema, ifExists } = DropCollectionSchema.parse(params);
290
+ if (!IDENTIFIER_RE.test(name))
291
+ return { success: false, error: "Invalid collection name" };
292
+ if (schema && !IDENTIFIER_RE.test(schema))
293
+ return { success: false, error: "Invalid schema name" };
294
+ const tableRef = escapeTableRef(name, schema);
295
+ // P154: Schema existence check when explicitly provided
296
+ if (schema) {
297
+ const schemaCheck = await adapter.executeQuery("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?", [schema]);
298
+ if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
299
+ return { exists: false, schema };
300
+ }
301
+ }
302
+ // Pre-check existence when ifExists is true so we can report accurately
303
+ if (ifExists) {
304
+ const check = await checkCollectionExists(adapter, name, schema);
305
+ if (!check.exists) {
306
+ return {
307
+ success: true,
308
+ collection: name,
309
+ message: "Collection did not exist",
310
+ };
311
+ }
312
+ }
313
+ await adapter.executeQuery(`DROP TABLE ${ifExists ? "IF EXISTS " : ""}${tableRef}`);
314
+ adapter.clearSchemaCache();
227
315
  return { success: true, collection: name };
228
316
  }
229
317
  catch (error) {
318
+ if (error instanceof z.ZodError) {
319
+ return { success: false, error: formatZodError(error) };
320
+ }
230
321
  const message = error instanceof Error ? error.message : String(error);
231
322
  if (message.toLowerCase().includes("unknown table")) {
232
323
  return {
233
324
  success: false,
234
- reason: `Collection '${name}' does not exist`,
325
+ error: `Collection '${params?.name ?? "unknown"}' does not exist`,
235
326
  };
236
327
  }
237
- throw error;
328
+ return { success: false, error: message };
238
329
  }
239
330
  },
240
331
  },
@@ -247,42 +338,63 @@ export function getDocStoreTools(adapter) {
247
338
  requiredScopes: ["read"],
248
339
  annotations: { readOnlyHint: true, idempotentHint: true },
249
340
  handler: async (params, _context) => {
250
- const { collection, filter, fields, limit, offset } = FindSchema.parse(params);
251
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
252
- throw new Error("Invalid collection name");
253
- // Check if collection exists
254
- const tableCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.TABLES
255
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`, [collection]);
256
- if (!tableCheck.rows || tableCheck.rows.length === 0) {
257
- return {
258
- exists: false,
259
- collection,
260
- documents: [],
261
- count: 0,
262
- };
341
+ try {
342
+ const { collection, schema, filter, fields, limit, offset } = FindSchema.parse(params);
343
+ if (!IDENTIFIER_RE.test(collection))
344
+ return { success: false, error: "Invalid collection name" };
345
+ if (schema && !IDENTIFIER_RE.test(schema))
346
+ return { success: false, error: "Invalid schema name" };
347
+ // Check if collection exists (with schema detection)
348
+ const findCheck = await checkCollectionExists(adapter, collection, schema);
349
+ if (!findCheck.exists) {
350
+ if (findCheck.reason === "schema") {
351
+ return { exists: false, schema: findCheck.name };
352
+ }
353
+ return {
354
+ exists: false,
355
+ collection,
356
+ documents: [],
357
+ count: 0,
358
+ };
359
+ }
360
+ let selectClause = "doc";
361
+ if (fields && fields.length > 0) {
362
+ selectClause =
363
+ "JSON_OBJECT(" +
364
+ fields
365
+ .map((f) => `'${f}', JSON_EXTRACT(doc, '$.${f}')`)
366
+ .join(", ") +
367
+ ") as doc";
368
+ }
369
+ const tableRef = escapeTableRef(collection, schema);
370
+ let query = `SELECT ${selectClause} FROM ${tableRef}`;
371
+ if (filter) {
372
+ if (!JSON_PATH_RE.test(filter)) {
373
+ return {
374
+ success: false,
375
+ error: `Invalid JSON path filter: "${filter}". Use a valid JSON path like $.field or $.field.sub`,
376
+ };
377
+ }
378
+ query += ` WHERE JSON_EXTRACT(doc, '${filter}') IS NOT NULL`;
379
+ }
380
+ query += ` LIMIT ${String(limit)} OFFSET ${String(offset)}`;
381
+ const result = await adapter.executeQuery(query);
382
+ const docs = (result.rows ?? []).map((r) => {
383
+ const row = r;
384
+ const docValue = row["doc"];
385
+ return typeof docValue === "string"
386
+ ? JSON.parse(docValue)
387
+ : docValue;
388
+ });
389
+ return { documents: docs, count: docs.length };
263
390
  }
264
- let selectClause = "doc";
265
- if (fields && fields.length > 0) {
266
- selectClause =
267
- "JSON_OBJECT(" +
268
- fields
269
- .map((f) => `'${f}', JSON_EXTRACT(doc, '$.${f}')`)
270
- .join(", ") +
271
- ") as doc";
391
+ catch (error) {
392
+ if (error instanceof z.ZodError) {
393
+ return { success: false, error: formatZodError(error) };
394
+ }
395
+ const message = error instanceof Error ? error.message : String(error);
396
+ return { success: false, error: message };
272
397
  }
273
- let query = `SELECT ${selectClause} FROM \`${collection}\``;
274
- if (filter)
275
- query += ` WHERE JSON_EXTRACT(doc, '${filter}') IS NOT NULL`;
276
- query += ` LIMIT ${String(limit)} OFFSET ${String(offset)}`;
277
- const result = await adapter.executeQuery(query);
278
- const docs = (result.rows ?? []).map((r) => {
279
- const row = r;
280
- const docValue = row["doc"];
281
- return typeof docValue === "string"
282
- ? JSON.parse(docValue)
283
- : docValue;
284
- });
285
- return { documents: docs, count: docs.length };
286
398
  },
287
399
  },
288
400
  {
@@ -294,19 +406,34 @@ export function getDocStoreTools(adapter) {
294
406
  requiredScopes: ["write"],
295
407
  annotations: { readOnlyHint: false },
296
408
  handler: async (params, _context) => {
297
- const { collection, documents } = AddDocSchema.parse(params);
298
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
299
- throw new Error("Invalid collection name");
300
- if (!(await checkCollectionExists(adapter, collection))) {
301
- return { exists: false, collection };
409
+ try {
410
+ const { collection, schema, documents } = AddDocSchema.parse(params);
411
+ if (!IDENTIFIER_RE.test(collection))
412
+ return { success: false, error: "Invalid collection name" };
413
+ if (schema && !IDENTIFIER_RE.test(schema))
414
+ return { success: false, error: "Invalid schema name" };
415
+ const addCheck = await checkCollectionExists(adapter, collection, schema);
416
+ if (!addCheck.exists) {
417
+ return addCheck.reason === "schema"
418
+ ? { exists: false, schema: addCheck.name }
419
+ : { exists: false, collection };
420
+ }
421
+ const tableRef = escapeTableRef(collection, schema);
422
+ let inserted = 0;
423
+ for (const doc of documents) {
424
+ doc["_id"] ??= crypto.randomUUID().replace(/-/g, "");
425
+ await adapter.executeQuery(`INSERT INTO ${tableRef} (doc) VALUES (?)`, [JSON.stringify(doc)]);
426
+ inserted++;
427
+ }
428
+ return { success: true, inserted };
302
429
  }
303
- let inserted = 0;
304
- for (const doc of documents) {
305
- doc["_id"] ??= crypto.randomUUID().replace(/-/g, "");
306
- await adapter.executeQuery(`INSERT INTO \`${collection}\` (doc) VALUES (?)`, [JSON.stringify(doc)]);
307
- inserted++;
430
+ catch (error) {
431
+ if (error instanceof z.ZodError) {
432
+ return { success: false, error: formatZodError(error) };
433
+ }
434
+ const message = error instanceof Error ? error.message : String(error);
435
+ return { success: false, error: message };
308
436
  }
309
- return { success: true, inserted };
310
437
  },
311
438
  },
312
439
  {
@@ -318,29 +445,44 @@ export function getDocStoreTools(adapter) {
318
445
  requiredScopes: ["write"],
319
446
  annotations: { readOnlyHint: false },
320
447
  handler: async (params, _context) => {
321
- const { collection, filter, set, unset } = ModifyDocSchema.parse(params);
322
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
323
- throw new Error("Invalid collection name");
324
- if (!(await checkCollectionExists(adapter, collection))) {
325
- return { exists: false, collection };
326
- }
327
- const updates = [];
328
- if (set) {
329
- for (const [path, value] of Object.entries(set)) {
330
- updates.push(`doc = JSON_SET(doc, '$.${path}', CAST('${JSON.stringify(value)}' AS JSON))`);
448
+ try {
449
+ const { collection, schema, filter, set, unset } = ModifyDocSchema.parse(params);
450
+ if (!IDENTIFIER_RE.test(collection))
451
+ return { success: false, error: "Invalid collection name" };
452
+ if (schema && !IDENTIFIER_RE.test(schema))
453
+ return { success: false, error: "Invalid schema name" };
454
+ const modCheck = await checkCollectionExists(adapter, collection, schema);
455
+ if (!modCheck.exists) {
456
+ return modCheck.reason === "schema"
457
+ ? { exists: false, schema: modCheck.name }
458
+ : { exists: false, collection };
459
+ }
460
+ const updates = [];
461
+ if (set) {
462
+ for (const [path, value] of Object.entries(set)) {
463
+ updates.push(`doc = JSON_SET(doc, '$.${path}', CAST('${JSON.stringify(value)}' AS JSON))`);
464
+ }
331
465
  }
466
+ if (unset) {
467
+ for (const path of unset) {
468
+ updates.push(`doc = JSON_REMOVE(doc, '$.${path}')`);
469
+ }
470
+ }
471
+ if (updates.length === 0)
472
+ return { success: false, error: "No modifications specified" };
473
+ const { where, params: whereParams } = parseDocFilter(filter);
474
+ const tableRef = escapeTableRef(collection, schema);
475
+ const query = `UPDATE ${tableRef} SET ${updates.join(", ")} WHERE ${where}`;
476
+ const result = await adapter.executeQuery(query, whereParams);
477
+ return { success: true, modified: result.rowsAffected ?? 0 };
332
478
  }
333
- if (unset) {
334
- for (const path of unset) {
335
- updates.push(`doc = JSON_REMOVE(doc, '$.${path}')`);
479
+ catch (error) {
480
+ if (error instanceof z.ZodError) {
481
+ return { success: false, error: formatZodError(error) };
336
482
  }
483
+ const message = error instanceof Error ? error.message : String(error);
484
+ return { success: false, error: message };
337
485
  }
338
- if (updates.length === 0)
339
- throw new Error("No modifications specified");
340
- const { where, params: whereParams } = parseDocFilter(filter);
341
- const query = `UPDATE \`${collection}\` SET ${updates.join(", ")} WHERE ${where}`;
342
- const result = await adapter.executeQuery(query, whereParams);
343
- return { success: true, modified: result.rowsAffected ?? 0 };
344
486
  },
345
487
  },
346
488
  {
@@ -352,16 +494,31 @@ export function getDocStoreTools(adapter) {
352
494
  requiredScopes: ["write"],
353
495
  annotations: { readOnlyHint: false, destructiveHint: true },
354
496
  handler: async (params, _context) => {
355
- const { collection, filter } = RemoveDocSchema.parse(params);
356
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
357
- throw new Error("Invalid collection name");
358
- if (!(await checkCollectionExists(adapter, collection))) {
359
- return { exists: false, collection };
497
+ try {
498
+ const { collection, schema, filter } = RemoveDocSchema.parse(params);
499
+ if (!IDENTIFIER_RE.test(collection))
500
+ return { success: false, error: "Invalid collection name" };
501
+ if (schema && !IDENTIFIER_RE.test(schema))
502
+ return { success: false, error: "Invalid schema name" };
503
+ const rmCheck = await checkCollectionExists(adapter, collection, schema);
504
+ if (!rmCheck.exists) {
505
+ return rmCheck.reason === "schema"
506
+ ? { exists: false, schema: rmCheck.name }
507
+ : { exists: false, collection };
508
+ }
509
+ const { where, params: whereParams } = parseDocFilter(filter);
510
+ const tableRef = escapeTableRef(collection, schema);
511
+ const query = `DELETE FROM ${tableRef} WHERE ${where}`;
512
+ const result = await adapter.executeQuery(query, whereParams);
513
+ return { success: true, removed: result.rowsAffected ?? 0 };
514
+ }
515
+ catch (error) {
516
+ if (error instanceof z.ZodError) {
517
+ return { success: false, error: formatZodError(error) };
518
+ }
519
+ const message = error instanceof Error ? error.message : String(error);
520
+ return { success: false, error: message };
360
521
  }
361
- const { where, params: whereParams } = parseDocFilter(filter);
362
- const query = `DELETE FROM \`${collection}\` WHERE ${where}`;
363
- const result = await adapter.executeQuery(query, whereParams);
364
- return { success: true, removed: result.rowsAffected ?? 0 };
365
522
  },
366
523
  },
367
524
  {
@@ -373,38 +530,48 @@ export function getDocStoreTools(adapter) {
373
530
  requiredScopes: ["write"],
374
531
  annotations: { readOnlyHint: false },
375
532
  handler: async (params, _context) => {
376
- const { collection, name, fields, unique } = CreateDocIndexSchema.parse(params);
377
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
378
- throw new Error("Invalid collection name");
379
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))
380
- throw new Error("Invalid index name");
381
- if (!(await checkCollectionExists(adapter, collection))) {
382
- return { exists: false, collection };
383
- }
384
533
  try {
534
+ const { collection, schema, name, fields, unique } = CreateDocIndexSchema.parse(params);
535
+ if (!IDENTIFIER_RE.test(collection))
536
+ return { success: false, error: "Invalid collection name" };
537
+ if (schema && !IDENTIFIER_RE.test(schema))
538
+ return { success: false, error: "Invalid schema name" };
539
+ if (!IDENTIFIER_RE.test(name))
540
+ return { success: false, error: "Invalid index name" };
541
+ const idxCheck = await checkCollectionExists(adapter, collection, schema);
542
+ if (!idxCheck.exists) {
543
+ return idxCheck.reason === "schema"
544
+ ? { exists: false, schema: idxCheck.name }
545
+ : { exists: false, collection };
546
+ }
547
+ const tableRef = escapeTableRef(collection, schema);
385
548
  for (const field of fields) {
386
549
  const colName = `_idx_${field.path.replace(/\./g, "_")}`;
387
550
  const cast = field.type === "TEXT" ? "CHAR(255)" : field.type;
388
- await adapter.executeQuery(`ALTER TABLE \`${collection}\` ADD COLUMN \`${colName}\` ${cast}
551
+ await adapter.executeQuery(`ALTER TABLE ${tableRef} ADD COLUMN \`${colName}\` ${cast}
389
552
  GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$.${field.path}'))) STORED`);
390
553
  }
391
554
  const cols = fields
392
555
  .map((f) => `\`_idx_${f.path.replace(/\./g, "_")}\``)
393
556
  .join(", ");
394
557
  const uniqueClause = unique ? "UNIQUE " : "";
395
- await adapter.executeQuery(`CREATE ${uniqueClause}INDEX \`${name}\` ON \`${collection}\` (${cols})`);
558
+ await adapter.executeQuery(`CREATE ${uniqueClause}INDEX \`${name}\` ON ${tableRef} (${cols})`);
559
+ adapter.clearSchemaCache();
396
560
  return { success: true, index: name };
397
561
  }
398
562
  catch (error) {
563
+ if (error instanceof z.ZodError) {
564
+ return { success: false, error: formatZodError(error) };
565
+ }
399
566
  const message = error instanceof Error ? error.message : String(error);
400
567
  if (message.toLowerCase().includes("duplicate column") ||
401
568
  message.toLowerCase().includes("duplicate key")) {
402
569
  return {
403
570
  success: false,
404
- reason: `Index '${name}' or its generated columns already exist on '${collection}'`,
571
+ error: `Index '${params?.name ?? "unknown"}' or its generated columns already exist on '${params?.collection ?? "unknown"}'`,
405
572
  };
406
573
  }
407
- throw error;
574
+ return { success: false, error: message };
408
575
  }
409
576
  },
410
577
  },
@@ -417,37 +584,47 @@ export function getDocStoreTools(adapter) {
417
584
  requiredScopes: ["read"],
418
585
  annotations: { readOnlyHint: true, idempotentHint: true },
419
586
  handler: async (params, _context) => {
420
- const { collection, schema } = CollectionInfoSchema.parse(params);
421
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
422
- throw new Error("Invalid collection name");
423
- // Check if collection exists
424
- const existsCheck = await adapter.executeQuery(`SELECT 1 FROM information_schema.TABLES
425
- WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?`, [schema ?? null, collection]);
426
- if (!existsCheck.rows || existsCheck.rows.length === 0) {
427
- return { exists: false, collection };
428
- }
429
- // Get accurate row count using COUNT(*) instead of INFORMATION_SCHEMA estimate
430
- const schemaClause = schema
431
- ? `\`${schema}\`.\`${collection}\``
432
- : `\`${collection}\``;
433
- const countResult = await adapter.executeQuery(`SELECT COUNT(*) as rowCount FROM ${schemaClause}`);
434
- const rowCount = countResult.rows?.[0]?.rowCount ?? 0;
435
- const tableInfo = await adapter.executeQuery(`
587
+ try {
588
+ const { collection, schema } = CollectionInfoSchema.parse(params);
589
+ if (!IDENTIFIER_RE.test(collection))
590
+ return { success: false, error: "Invalid collection name" };
591
+ // Check collection existence (with schema detection)
592
+ const infoCheck = await checkCollectionExists(adapter, collection, schema);
593
+ if (!infoCheck.exists) {
594
+ return infoCheck.reason === "schema"
595
+ ? { exists: false, schema: infoCheck.name }
596
+ : { exists: false, collection };
597
+ }
598
+ // Get accurate row count using COUNT(*) instead of INFORMATION_SCHEMA estimate
599
+ const schemaClause = schema
600
+ ? `\`${schema}\`.\`${collection}\``
601
+ : `\`${collection}\``;
602
+ const countResult = await adapter.executeQuery(`SELECT COUNT(*) as rowCount FROM ${schemaClause}`);
603
+ const rowCount = countResult.rows?.[0]?.rowCount ?? 0;
604
+ const tableInfo = await adapter.executeQuery(`
436
605
  SELECT DATA_LENGTH as dataSize, INDEX_LENGTH as indexSize
437
606
  FROM information_schema.TABLES
438
607
  WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?
439
608
  `, [schema ?? null, collection]);
440
- const indexInfo = await adapter.executeQuery(`
609
+ const indexInfo = await adapter.executeQuery(`
441
610
  SELECT INDEX_NAME, COLUMN_NAME, SEQ_IN_INDEX, NON_UNIQUE
442
611
  FROM information_schema.STATISTICS
443
612
  WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?
444
613
  `, [schema ?? null, collection]);
445
- const stats = tableInfo.rows?.[0] ?? {};
446
- return {
447
- collection,
448
- stats: { rowCount, ...stats },
449
- indexes: indexInfo.rows ?? [],
450
- };
614
+ const stats = tableInfo.rows?.[0] ?? {};
615
+ return {
616
+ collection,
617
+ stats: { rowCount, ...stats },
618
+ indexes: indexInfo.rows ?? [],
619
+ };
620
+ }
621
+ catch (error) {
622
+ if (error instanceof z.ZodError) {
623
+ return { success: false, error: formatZodError(error) };
624
+ }
625
+ const message = error instanceof Error ? error.message : String(error);
626
+ return { success: false, error: message };
627
+ }
451
628
  },
452
629
  },
453
630
  ];