@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
@@ -9,6 +9,12 @@ import { z } from "zod";
9
9
  import type { MySQLAdapter } from "../MySQLAdapter.js";
10
10
  import type { ToolDefinition, RequestContext } from "../../../types/index.js";
11
11
 
12
+ const IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
13
+
14
+ // Valid JSON path: $, $.field, $.field.sub, $.field[0], $[0], $[*]
15
+ const JSON_PATH_RE =
16
+ /^(\$)((\.([a-zA-Z_][a-zA-Z0-9_]*))|((\[\d+\])|(\[\*\])))*$/;
17
+
12
18
  const ListCollectionsSchema = z.object({
13
19
  schema: z.string().optional().describe("Schema name (defaults to current)"),
14
20
  });
@@ -40,6 +46,7 @@ const DropCollectionSchema = z.object({
40
46
 
41
47
  const FindSchema = z.object({
42
48
  collection: z.string(),
49
+ schema: z.string().optional(),
43
50
  filter: z.string().optional().describe("JSON path expression filter"),
44
51
  fields: z.array(z.string()).optional(),
45
52
  limit: z.number().default(100),
@@ -48,6 +55,7 @@ const FindSchema = z.object({
48
55
 
49
56
  const AddDocSchema = z.object({
50
57
  collection: z.string(),
58
+ schema: z.string().optional(),
51
59
  documents: z
52
60
  .array(z.record(z.string(), z.unknown()))
53
61
  .describe("Documents to add"),
@@ -55,6 +63,7 @@ const AddDocSchema = z.object({
55
63
 
56
64
  const ModifyDocSchema = z.object({
57
65
  collection: z.string(),
66
+ schema: z.string().optional(),
58
67
  filter: z
59
68
  .string()
60
69
  .describe(
@@ -66,6 +75,7 @@ const ModifyDocSchema = z.object({
66
75
 
67
76
  const RemoveDocSchema = z.object({
68
77
  collection: z.string(),
78
+ schema: z.string().optional(),
69
79
  filter: z
70
80
  .string()
71
81
  .describe(
@@ -89,7 +99,14 @@ function parseDocFilter(filter: string): { where: string; params: unknown[] } {
89
99
  // Check for simple field=value pattern
90
100
  const eqMatch = /^([a-zA-Z_][a-zA-Z0-9_]*)=(.+)$/.exec(filter);
91
101
  if (eqMatch) {
92
- const [, field, value] = eqMatch;
102
+ const field = eqMatch[1] ?? "";
103
+ const value = eqMatch[2] ?? "";
104
+ // Defense-in-depth: validate field name against identifier regex
105
+ if (!IDENTIFIER_RE.test(field)) {
106
+ throw new Error(
107
+ `Invalid field name in filter: "${field}". Field names must be valid identifiers.`,
108
+ );
109
+ }
93
110
  // Try to parse as number
94
111
  const numVal = Number(value);
95
112
  if (!isNaN(numVal)) {
@@ -115,6 +132,7 @@ function parseDocFilter(filter: string): { where: string; params: unknown[] } {
115
132
 
116
133
  const CreateDocIndexSchema = z.object({
117
134
  collection: z.string(),
135
+ schema: z.string().optional(),
118
136
  name: z.string(),
119
137
  fields: z.array(
120
138
  z.object({
@@ -134,18 +152,50 @@ const CollectionInfoSchema = z.object({
134
152
  });
135
153
 
136
154
  /**
137
- * Check if a collection (table) exists in the current database.
155
+ * Check if a collection (table) exists in the specified (or current) database.
156
+ * Returns a discriminated result distinguishing schema-not-found from collection-not-found.
138
157
  */
139
158
  async function checkCollectionExists(
140
159
  adapter: MySQLAdapter,
141
160
  collection: string,
142
- ): Promise<boolean> {
161
+ schema?: string,
162
+ ): Promise<
163
+ | { exists: true }
164
+ | { exists: false; reason: "schema" | "collection"; name: string }
165
+ > {
166
+ // When schema is explicitly provided, check schema existence first
167
+ if (schema) {
168
+ const schemaCheck = await adapter.executeQuery(
169
+ "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?",
170
+ [schema],
171
+ );
172
+ if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
173
+ return { exists: false, reason: "schema", name: schema };
174
+ }
175
+ }
143
176
  const result = await adapter.executeQuery(
144
177
  `SELECT 1 FROM information_schema.TABLES
145
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,
146
- [collection],
178
+ WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?`,
179
+ [schema ?? null, collection],
147
180
  );
148
- return (result.rows?.length ?? 0) > 0;
181
+ if ((result.rows?.length ?? 0) > 0) {
182
+ return { exists: true };
183
+ }
184
+ return { exists: false, reason: "collection", name: collection };
185
+ }
186
+
187
+ /**
188
+ * Build a backtick-escaped qualified table reference.
189
+ */
190
+ function escapeTableRef(name: string, schema?: string): string {
191
+ return schema ? `\`${schema}\`.\`${name}\`` : `\`${name}\``;
192
+ }
193
+
194
+ /**
195
+ * Format a ZodError into a human-readable string.
196
+ */
197
+ function formatZodError(err: z.ZodError): string {
198
+ return err.issues.map((i) => i.message).join("; ");
149
199
  }
150
200
 
151
201
  export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
@@ -159,36 +209,48 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
159
209
  requiredScopes: ["read"],
160
210
  annotations: { readOnlyHint: true, idempotentHint: true },
161
211
  handler: async (params: unknown, _context: RequestContext) => {
162
- const { schema } = ListCollectionsSchema.parse(params);
212
+ try {
213
+ const { schema } = ListCollectionsSchema.parse(params);
163
214
 
164
- // P154: Schema existence check when explicitly provided
165
- if (schema) {
166
- const schemaCheck = await adapter.executeQuery(
167
- "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?",
168
- [schema],
169
- );
170
- if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
171
- return { exists: false, schema };
215
+ // P154: Schema existence check when explicitly provided
216
+ if (schema) {
217
+ const schemaCheck = await adapter.executeQuery(
218
+ "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?",
219
+ [schema],
220
+ );
221
+ if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
222
+ return { exists: false, schema };
223
+ }
172
224
  }
173
- }
174
225
 
175
- const query = `
226
+ const query = `
176
227
  SELECT TABLE_NAME as name, TABLE_COMMENT as comment, TABLE_ROWS as rowCount
177
228
  FROM information_schema.TABLES
178
229
  WHERE TABLE_SCHEMA = COALESCE(?, DATABASE())
179
230
  AND TABLE_NAME IN (
180
- SELECT TABLE_NAME FROM information_schema.COLUMNS
181
- WHERE COLUMN_NAME = 'doc' AND DATA_TYPE = 'json'
182
- AND TABLE_SCHEMA = COALESCE(?, DATABASE())
231
+ SELECT c1.TABLE_NAME FROM information_schema.COLUMNS c1
232
+ JOIN information_schema.COLUMNS c2
233
+ ON c1.TABLE_SCHEMA = c2.TABLE_SCHEMA AND c1.TABLE_NAME = c2.TABLE_NAME
234
+ WHERE c1.COLUMN_NAME = 'doc' AND c1.DATA_TYPE = 'json'
235
+ AND c2.COLUMN_NAME = '_id'
236
+ AND c1.TABLE_SCHEMA = COALESCE(?, DATABASE())
183
237
  )`;
184
- const result = await adapter.executeQuery(query, [
185
- schema ?? null,
186
- schema ?? null,
187
- ]);
188
- return {
189
- collections: result.rows ?? [],
190
- count: result.rows?.length ?? 0,
191
- };
238
+ const result = await adapter.executeQuery(query, [
239
+ schema ?? null,
240
+ schema ?? null,
241
+ ]);
242
+ return {
243
+ collections: result.rows ?? [],
244
+ count: result.rows?.length ?? 0,
245
+ };
246
+ } catch (error: unknown) {
247
+ if (error instanceof z.ZodError) {
248
+ return { success: false, error: formatZodError(error) };
249
+ }
250
+ const message =
251
+ error instanceof Error ? error.message : String(error);
252
+ return { success: false, error: message };
253
+ }
192
254
  },
193
255
  },
194
256
  {
@@ -200,43 +262,74 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
200
262
  requiredScopes: ["write"],
201
263
  annotations: { readOnlyHint: false },
202
264
  handler: async (params: unknown, _context: RequestContext) => {
203
- const { name, ifNotExists, validation } =
204
- CreateCollectionSchema.parse(params);
205
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))
206
- throw new Error("Invalid collection name");
265
+ try {
266
+ const { name, schema, ifNotExists, validation } =
267
+ CreateCollectionSchema.parse(params);
268
+ if (!IDENTIFIER_RE.test(name))
269
+ return { success: false, error: "Invalid collection name" };
270
+ if (schema && !IDENTIFIER_RE.test(schema))
271
+ return { success: false, error: "Invalid schema name" };
272
+
273
+ const tableRef = escapeTableRef(name, schema);
274
+
275
+ // Pre-check existence when ifNotExists is true so we can report accurately
276
+ if (ifNotExists) {
277
+ const check = await checkCollectionExists(adapter, name, schema);
278
+ if (check.exists) {
279
+ return {
280
+ success: true,
281
+ skipped: true,
282
+ collection: name,
283
+ reason: "Collection already exists",
284
+ };
285
+ }
286
+ // If schema doesn't exist, report it even with ifNotExists
287
+ if (check.reason === "schema") {
288
+ return { exists: false, schema: check.name };
289
+ }
290
+ }
207
291
 
208
- const createClause = ifNotExists
209
- ? "CREATE TABLE IF NOT EXISTS"
210
- : "CREATE TABLE";
292
+ const createClause = ifNotExists
293
+ ? "CREATE TABLE IF NOT EXISTS"
294
+ : "CREATE TABLE";
211
295
 
212
- let sql = `${createClause} \`${name}\` (
296
+ let sql = `${createClause} ${tableRef} (
213
297
  doc JSON,
214
298
  _id VARBINARY(32) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$._id'))) STORED PRIMARY KEY,
215
299
  _json_schema JSON GENERATED ALWAYS AS ('{}') VIRTUAL
216
300
  ) ENGINE=InnoDB`;
217
301
 
218
- if (validation?.level && validation.level !== "OFF") {
219
- const schemaJson = JSON.stringify(validation.schema ?? {});
220
- sql = `${createClause} \`${name}\` (
302
+ if (validation?.level && validation.level !== "OFF") {
303
+ const schemaJson = JSON.stringify(validation.schema ?? {});
304
+ sql = `${createClause} ${tableRef} (
221
305
  doc JSON,
222
306
  _id VARBINARY(32) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$._id'))) STORED PRIMARY KEY,
223
307
  CONSTRAINT chk_schema CHECK (JSON_SCHEMA_VALID('${schemaJson}', doc))
224
308
  ) ENGINE=InnoDB`;
225
- }
309
+ }
226
310
 
227
- try {
228
311
  await adapter.executeQuery(sql);
312
+ adapter.clearSchemaCache();
229
313
  return { success: true, collection: name };
230
314
  } catch (error: unknown) {
315
+ if (error instanceof z.ZodError) {
316
+ return { success: false, error: formatZodError(error) };
317
+ }
231
318
  const message =
232
319
  error instanceof Error ? error.message : String(error);
320
+ if (message.toLowerCase().includes("unknown database")) {
321
+ return {
322
+ exists: false,
323
+ schema: (params as { schema?: string })?.schema ?? "unknown",
324
+ };
325
+ }
233
326
  if (message.toLowerCase().includes("already exists")) {
234
327
  return {
235
328
  success: false,
236
- reason: `Collection '${name}' already exists`,
329
+ error: `Collection '${(params as { name?: string })?.name ?? "unknown"}' already exists`,
237
330
  };
238
331
  }
239
- throw error;
332
+ return { success: false, error: message };
240
333
  }
241
334
  },
242
335
  },
@@ -249,37 +342,56 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
249
342
  requiredScopes: ["admin"],
250
343
  annotations: { readOnlyHint: false, destructiveHint: true },
251
344
  handler: async (params: unknown, _context: RequestContext) => {
252
- const { name, ifExists } = DropCollectionSchema.parse(params);
253
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))
254
- throw new Error("Invalid collection name");
255
-
256
- // Pre-check existence when ifExists is true so we can report accurately
257
- if (ifExists) {
258
- const exists = await checkCollectionExists(adapter, name);
259
- if (!exists) {
260
- return {
261
- success: true,
262
- collection: name,
263
- message: "Collection did not exist",
264
- };
345
+ try {
346
+ const { name, schema, ifExists } = DropCollectionSchema.parse(params);
347
+ if (!IDENTIFIER_RE.test(name))
348
+ return { success: false, error: "Invalid collection name" };
349
+ if (schema && !IDENTIFIER_RE.test(schema))
350
+ return { success: false, error: "Invalid schema name" };
351
+
352
+ const tableRef = escapeTableRef(name, schema);
353
+
354
+ // P154: Schema existence check when explicitly provided
355
+ if (schema) {
356
+ const schemaCheck = await adapter.executeQuery(
357
+ "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?",
358
+ [schema],
359
+ );
360
+ if (!schemaCheck.rows || schemaCheck.rows.length === 0) {
361
+ return { exists: false, schema };
362
+ }
363
+ }
364
+
365
+ // Pre-check existence when ifExists is true so we can report accurately
366
+ if (ifExists) {
367
+ const check = await checkCollectionExists(adapter, name, schema);
368
+ if (!check.exists) {
369
+ return {
370
+ success: true,
371
+ collection: name,
372
+ message: "Collection did not exist",
373
+ };
374
+ }
265
375
  }
266
- }
267
376
 
268
- try {
269
377
  await adapter.executeQuery(
270
- `DROP TABLE ${ifExists ? "IF EXISTS " : ""}\`${name}\``,
378
+ `DROP TABLE ${ifExists ? "IF EXISTS " : ""}${tableRef}`,
271
379
  );
380
+ adapter.clearSchemaCache();
272
381
  return { success: true, collection: name };
273
382
  } catch (error: unknown) {
383
+ if (error instanceof z.ZodError) {
384
+ return { success: false, error: formatZodError(error) };
385
+ }
274
386
  const message =
275
387
  error instanceof Error ? error.message : String(error);
276
388
  if (message.toLowerCase().includes("unknown table")) {
277
389
  return {
278
390
  success: false,
279
- reason: `Collection '${name}' does not exist`,
391
+ error: `Collection '${(params as { name?: string })?.name ?? "unknown"}' does not exist`,
280
392
  };
281
393
  }
282
- throw error;
394
+ return { success: false, error: message };
283
395
  }
284
396
  },
285
397
  },
@@ -292,50 +404,72 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
292
404
  requiredScopes: ["read"],
293
405
  annotations: { readOnlyHint: true, idempotentHint: true },
294
406
  handler: async (params: unknown, _context: RequestContext) => {
295
- const { collection, filter, fields, limit, offset } =
296
- FindSchema.parse(params);
297
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
298
- throw new Error("Invalid collection name");
299
-
300
- // Check if collection exists
301
- const tableCheck = await adapter.executeQuery(
302
- `SELECT 1 FROM information_schema.TABLES
303
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,
304
- [collection],
305
- );
306
- if (!tableCheck.rows || tableCheck.rows.length === 0) {
307
- return {
308
- exists: false,
407
+ try {
408
+ const { collection, schema, filter, fields, limit, offset } =
409
+ FindSchema.parse(params);
410
+ if (!IDENTIFIER_RE.test(collection))
411
+ return { success: false, error: "Invalid collection name" };
412
+ if (schema && !IDENTIFIER_RE.test(schema))
413
+ return { success: false, error: "Invalid schema name" };
414
+
415
+ // Check if collection exists (with schema detection)
416
+ const findCheck = await checkCollectionExists(
417
+ adapter,
309
418
  collection,
310
- documents: [],
311
- count: 0,
312
- };
313
- }
419
+ schema,
420
+ );
421
+ if (!findCheck.exists) {
422
+ if (findCheck.reason === "schema") {
423
+ return { exists: false, schema: findCheck.name };
424
+ }
425
+ return {
426
+ exists: false,
427
+ collection,
428
+ documents: [],
429
+ count: 0,
430
+ };
431
+ }
314
432
 
315
- let selectClause = "doc";
316
- if (fields && fields.length > 0) {
317
- selectClause =
318
- "JSON_OBJECT(" +
319
- fields
320
- .map((f) => `'${f}', JSON_EXTRACT(doc, '$.${f}')`)
321
- .join(", ") +
322
- ") as doc";
323
- }
433
+ let selectClause = "doc";
434
+ if (fields && fields.length > 0) {
435
+ selectClause =
436
+ "JSON_OBJECT(" +
437
+ fields
438
+ .map((f) => `'${f}', JSON_EXTRACT(doc, '$.${f}')`)
439
+ .join(", ") +
440
+ ") as doc";
441
+ }
324
442
 
325
- let query = `SELECT ${selectClause} FROM \`${collection}\``;
326
- if (filter)
327
- query += ` WHERE JSON_EXTRACT(doc, '${filter}') IS NOT NULL`;
328
- query += ` LIMIT ${String(limit)} OFFSET ${String(offset)}`;
329
-
330
- const result = await adapter.executeQuery(query);
331
- const docs = (result.rows ?? []).map((r) => {
332
- const row = r;
333
- const docValue = row["doc"];
334
- return typeof docValue === "string"
335
- ? (JSON.parse(docValue) as Record<string, unknown>)
336
- : docValue;
337
- });
338
- return { documents: docs, count: docs.length };
443
+ const tableRef = escapeTableRef(collection, schema);
444
+ let query = `SELECT ${selectClause} FROM ${tableRef}`;
445
+ if (filter) {
446
+ if (!JSON_PATH_RE.test(filter)) {
447
+ return {
448
+ success: false,
449
+ error: `Invalid JSON path filter: "${filter}". Use a valid JSON path like $.field or $.field.sub`,
450
+ };
451
+ }
452
+ query += ` WHERE JSON_EXTRACT(doc, '${filter}') IS NOT NULL`;
453
+ }
454
+ query += ` LIMIT ${String(limit)} OFFSET ${String(offset)}`;
455
+
456
+ const result = await adapter.executeQuery(query);
457
+ const docs = (result.rows ?? []).map((r) => {
458
+ const row = r;
459
+ const docValue = row["doc"];
460
+ return typeof docValue === "string"
461
+ ? (JSON.parse(docValue) as Record<string, unknown>)
462
+ : docValue;
463
+ });
464
+ return { documents: docs, count: docs.length };
465
+ } catch (error: unknown) {
466
+ if (error instanceof z.ZodError) {
467
+ return { success: false, error: formatZodError(error) };
468
+ }
469
+ const message =
470
+ error instanceof Error ? error.message : String(error);
471
+ return { success: false, error: message };
472
+ }
339
473
  },
340
474
  },
341
475
  {
@@ -347,24 +481,43 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
347
481
  requiredScopes: ["write"],
348
482
  annotations: { readOnlyHint: false },
349
483
  handler: async (params: unknown, _context: RequestContext) => {
350
- const { collection, documents } = AddDocSchema.parse(params);
351
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
352
- throw new Error("Invalid collection name");
353
-
354
- if (!(await checkCollectionExists(adapter, collection))) {
355
- return { exists: false, collection };
356
- }
357
-
358
- let inserted = 0;
359
- for (const doc of documents) {
360
- doc["_id"] ??= crypto.randomUUID().replace(/-/g, "");
361
- await adapter.executeQuery(
362
- `INSERT INTO \`${collection}\` (doc) VALUES (?)`,
363
- [JSON.stringify(doc)],
484
+ try {
485
+ const { collection, schema, documents } = AddDocSchema.parse(params);
486
+ if (!IDENTIFIER_RE.test(collection))
487
+ return { success: false, error: "Invalid collection name" };
488
+ if (schema && !IDENTIFIER_RE.test(schema))
489
+ return { success: false, error: "Invalid schema name" };
490
+
491
+ const addCheck = await checkCollectionExists(
492
+ adapter,
493
+ collection,
494
+ schema,
364
495
  );
365
- inserted++;
496
+ if (!addCheck.exists) {
497
+ return addCheck.reason === "schema"
498
+ ? { exists: false, schema: addCheck.name }
499
+ : { exists: false, collection };
500
+ }
501
+
502
+ const tableRef = escapeTableRef(collection, schema);
503
+ let inserted = 0;
504
+ for (const doc of documents) {
505
+ doc["_id"] ??= crypto.randomUUID().replace(/-/g, "");
506
+ await adapter.executeQuery(
507
+ `INSERT INTO ${tableRef} (doc) VALUES (?)`,
508
+ [JSON.stringify(doc)],
509
+ );
510
+ inserted++;
511
+ }
512
+ return { success: true, inserted };
513
+ } catch (error: unknown) {
514
+ if (error instanceof z.ZodError) {
515
+ return { success: false, error: formatZodError(error) };
516
+ }
517
+ const message =
518
+ error instanceof Error ? error.message : String(error);
519
+ return { success: false, error: message };
366
520
  }
367
- return { success: true, inserted };
368
521
  },
369
522
  },
370
523
  {
@@ -376,35 +529,55 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
376
529
  requiredScopes: ["write"],
377
530
  annotations: { readOnlyHint: false },
378
531
  handler: async (params: unknown, _context: RequestContext) => {
379
- const { collection, filter, set, unset } =
380
- ModifyDocSchema.parse(params);
381
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
382
- throw new Error("Invalid collection name");
383
-
384
- if (!(await checkCollectionExists(adapter, collection))) {
385
- return { exists: false, collection };
386
- }
532
+ try {
533
+ const { collection, schema, filter, set, unset } =
534
+ ModifyDocSchema.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
+
540
+ const modCheck = await checkCollectionExists(
541
+ adapter,
542
+ collection,
543
+ schema,
544
+ );
545
+ if (!modCheck.exists) {
546
+ return modCheck.reason === "schema"
547
+ ? { exists: false, schema: modCheck.name }
548
+ : { exists: false, collection };
549
+ }
387
550
 
388
- const updates: string[] = [];
389
- if (set) {
390
- for (const [path, value] of Object.entries(set)) {
391
- updates.push(
392
- `doc = JSON_SET(doc, '$.${path}', CAST('${JSON.stringify(value)}' AS JSON))`,
393
- );
551
+ const updates: string[] = [];
552
+ if (set) {
553
+ for (const [path, value] of Object.entries(set)) {
554
+ updates.push(
555
+ `doc = JSON_SET(doc, '$.${path}', CAST('${JSON.stringify(value)}' AS JSON))`,
556
+ );
557
+ }
394
558
  }
395
- }
396
- if (unset) {
397
- for (const path of unset) {
398
- updates.push(`doc = JSON_REMOVE(doc, '$.${path}')`);
559
+ if (unset) {
560
+ for (const path of unset) {
561
+ updates.push(`doc = JSON_REMOVE(doc, '$.${path}')`);
562
+ }
399
563
  }
400
- }
401
564
 
402
- if (updates.length === 0) throw new Error("No modifications specified");
565
+ if (updates.length === 0)
566
+ return { success: false, error: "No modifications specified" };
403
567
 
404
- const { where, params: whereParams } = parseDocFilter(filter);
405
- const query = `UPDATE \`${collection}\` SET ${updates.join(", ")} WHERE ${where}`;
406
- const result = await adapter.executeQuery(query, whereParams);
407
- return { success: true, modified: result.rowsAffected ?? 0 };
568
+ const { where, params: whereParams } = parseDocFilter(filter);
569
+ const tableRef = escapeTableRef(collection, schema);
570
+ const query = `UPDATE ${tableRef} SET ${updates.join(", ")} WHERE ${where}`;
571
+ const result = await adapter.executeQuery(query, whereParams);
572
+ return { success: true, modified: result.rowsAffected ?? 0 };
573
+ } catch (error: unknown) {
574
+ if (error instanceof z.ZodError) {
575
+ return { success: false, error: formatZodError(error) };
576
+ }
577
+ const message =
578
+ error instanceof Error ? error.message : String(error);
579
+ return { success: false, error: message };
580
+ }
408
581
  },
409
582
  },
410
583
  {
@@ -416,18 +589,37 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
416
589
  requiredScopes: ["write"],
417
590
  annotations: { readOnlyHint: false, destructiveHint: true },
418
591
  handler: async (params: unknown, _context: RequestContext) => {
419
- const { collection, filter } = RemoveDocSchema.parse(params);
420
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
421
- throw new Error("Invalid collection name");
592
+ try {
593
+ const { collection, schema, filter } = RemoveDocSchema.parse(params);
594
+ if (!IDENTIFIER_RE.test(collection))
595
+ return { success: false, error: "Invalid collection name" };
596
+ if (schema && !IDENTIFIER_RE.test(schema))
597
+ return { success: false, error: "Invalid schema name" };
598
+
599
+ const rmCheck = await checkCollectionExists(
600
+ adapter,
601
+ collection,
602
+ schema,
603
+ );
604
+ if (!rmCheck.exists) {
605
+ return rmCheck.reason === "schema"
606
+ ? { exists: false, schema: rmCheck.name }
607
+ : { exists: false, collection };
608
+ }
422
609
 
423
- if (!(await checkCollectionExists(adapter, collection))) {
424
- return { exists: false, collection };
610
+ const { where, params: whereParams } = parseDocFilter(filter);
611
+ const tableRef = escapeTableRef(collection, schema);
612
+ const query = `DELETE FROM ${tableRef} WHERE ${where}`;
613
+ const result = await adapter.executeQuery(query, whereParams);
614
+ return { success: true, removed: result.rowsAffected ?? 0 };
615
+ } catch (error: unknown) {
616
+ if (error instanceof z.ZodError) {
617
+ return { success: false, error: formatZodError(error) };
618
+ }
619
+ const message =
620
+ error instanceof Error ? error.message : String(error);
621
+ return { success: false, error: message };
425
622
  }
426
-
427
- const { where, params: whereParams } = parseDocFilter(filter);
428
- const query = `DELETE FROM \`${collection}\` WHERE ${where}`;
429
- const result = await adapter.executeQuery(query, whereParams);
430
- return { success: true, removed: result.rowsAffected ?? 0 };
431
623
  },
432
624
  },
433
625
  {
@@ -439,23 +631,33 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
439
631
  requiredScopes: ["write"],
440
632
  annotations: { readOnlyHint: false },
441
633
  handler: async (params: unknown, _context: RequestContext) => {
442
- const { collection, name, fields, unique } =
443
- CreateDocIndexSchema.parse(params);
444
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
445
- throw new Error("Invalid collection name");
446
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))
447
- throw new Error("Invalid index name");
448
-
449
- if (!(await checkCollectionExists(adapter, collection))) {
450
- return { exists: false, collection };
451
- }
452
-
453
634
  try {
635
+ const { collection, schema, name, fields, unique } =
636
+ CreateDocIndexSchema.parse(params);
637
+ if (!IDENTIFIER_RE.test(collection))
638
+ return { success: false, error: "Invalid collection name" };
639
+ if (schema && !IDENTIFIER_RE.test(schema))
640
+ return { success: false, error: "Invalid schema name" };
641
+ if (!IDENTIFIER_RE.test(name))
642
+ return { success: false, error: "Invalid index name" };
643
+
644
+ const idxCheck = await checkCollectionExists(
645
+ adapter,
646
+ collection,
647
+ schema,
648
+ );
649
+ if (!idxCheck.exists) {
650
+ return idxCheck.reason === "schema"
651
+ ? { exists: false, schema: idxCheck.name }
652
+ : { exists: false, collection };
653
+ }
654
+
655
+ const tableRef = escapeTableRef(collection, schema);
454
656
  for (const field of fields) {
455
657
  const colName = `_idx_${field.path.replace(/\./g, "_")}`;
456
658
  const cast = field.type === "TEXT" ? "CHAR(255)" : field.type;
457
659
  await adapter.executeQuery(
458
- `ALTER TABLE \`${collection}\` ADD COLUMN \`${colName}\` ${cast}
660
+ `ALTER TABLE ${tableRef} ADD COLUMN \`${colName}\` ${cast}
459
661
  GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$.${field.path}'))) STORED`,
460
662
  );
461
663
  }
@@ -465,11 +667,15 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
465
667
  .join(", ");
466
668
  const uniqueClause = unique ? "UNIQUE " : "";
467
669
  await adapter.executeQuery(
468
- `CREATE ${uniqueClause}INDEX \`${name}\` ON \`${collection}\` (${cols})`,
670
+ `CREATE ${uniqueClause}INDEX \`${name}\` ON ${tableRef} (${cols})`,
469
671
  );
470
672
 
673
+ adapter.clearSchemaCache();
471
674
  return { success: true, index: name };
472
675
  } catch (error: unknown) {
676
+ if (error instanceof z.ZodError) {
677
+ return { success: false, error: formatZodError(error) };
678
+ }
473
679
  const message =
474
680
  error instanceof Error ? error.message : String(error);
475
681
  if (
@@ -478,10 +684,10 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
478
684
  ) {
479
685
  return {
480
686
  success: false,
481
- reason: `Index '${name}' or its generated columns already exist on '${collection}'`,
687
+ error: `Index '${(params as { name?: string })?.name ?? "unknown"}' or its generated columns already exist on '${(params as { collection?: string })?.collection ?? "unknown"}'`,
482
688
  };
483
689
  }
484
- throw error;
690
+ return { success: false, error: message };
485
691
  }
486
692
  },
487
693
  },
@@ -494,54 +700,65 @@ export function getDocStoreTools(adapter: MySQLAdapter): ToolDefinition[] {
494
700
  requiredScopes: ["read"],
495
701
  annotations: { readOnlyHint: true, idempotentHint: true },
496
702
  handler: async (params: unknown, _context: RequestContext) => {
497
- const { collection, schema } = CollectionInfoSchema.parse(params);
498
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection))
499
- throw new Error("Invalid collection name");
500
-
501
- // Check if collection exists
502
- const existsCheck = await adapter.executeQuery(
503
- `SELECT 1 FROM information_schema.TABLES
504
- WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?`,
505
- [schema ?? null, collection],
506
- );
507
- if (!existsCheck.rows || existsCheck.rows.length === 0) {
508
- return { exists: false, collection };
509
- }
703
+ try {
704
+ const { collection, schema } = CollectionInfoSchema.parse(params);
705
+ if (!IDENTIFIER_RE.test(collection))
706
+ return { success: false, error: "Invalid collection name" };
707
+
708
+ // Check collection existence (with schema detection)
709
+ const infoCheck = await checkCollectionExists(
710
+ adapter,
711
+ collection,
712
+ schema,
713
+ );
714
+ if (!infoCheck.exists) {
715
+ return infoCheck.reason === "schema"
716
+ ? { exists: false, schema: infoCheck.name }
717
+ : { exists: false, collection };
718
+ }
719
+
720
+ // Get accurate row count using COUNT(*) instead of INFORMATION_SCHEMA estimate
721
+ const schemaClause = schema
722
+ ? `\`${schema}\`.\`${collection}\``
723
+ : `\`${collection}\``;
724
+ const countResult = await adapter.executeQuery(
725
+ `SELECT COUNT(*) as rowCount FROM ${schemaClause}`,
726
+ );
727
+ const rowCount =
728
+ (countResult.rows?.[0] as { rowCount: number })?.rowCount ?? 0;
510
729
 
511
- // Get accurate row count using COUNT(*) instead of INFORMATION_SCHEMA estimate
512
- const schemaClause = schema
513
- ? `\`${schema}\`.\`${collection}\``
514
- : `\`${collection}\``;
515
- const countResult = await adapter.executeQuery(
516
- `SELECT COUNT(*) as rowCount FROM ${schemaClause}`,
517
- );
518
- const rowCount =
519
- (countResult.rows?.[0] as { rowCount: number })?.rowCount ?? 0;
520
-
521
- const tableInfo = await adapter.executeQuery(
522
- `
730
+ const tableInfo = await adapter.executeQuery(
731
+ `
523
732
  SELECT DATA_LENGTH as dataSize, INDEX_LENGTH as indexSize
524
733
  FROM information_schema.TABLES
525
734
  WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?
526
735
  `,
527
- [schema ?? null, collection],
528
- );
736
+ [schema ?? null, collection],
737
+ );
529
738
 
530
- const indexInfo = await adapter.executeQuery(
531
- `
739
+ const indexInfo = await adapter.executeQuery(
740
+ `
532
741
  SELECT INDEX_NAME, COLUMN_NAME, SEQ_IN_INDEX, NON_UNIQUE
533
742
  FROM information_schema.STATISTICS
534
743
  WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ?
535
744
  `,
536
- [schema ?? null, collection],
537
- );
538
-
539
- const stats = tableInfo.rows?.[0] ?? {};
540
- return {
541
- collection,
542
- stats: { rowCount, ...stats },
543
- indexes: indexInfo.rows ?? [],
544
- };
745
+ [schema ?? null, collection],
746
+ );
747
+
748
+ const stats = tableInfo.rows?.[0] ?? {};
749
+ return {
750
+ collection,
751
+ stats: { rowCount, ...stats },
752
+ indexes: indexInfo.rows ?? [],
753
+ };
754
+ } catch (error: unknown) {
755
+ if (error instanceof z.ZodError) {
756
+ return { success: false, error: formatZodError(error) };
757
+ }
758
+ const message =
759
+ error instanceof Error ? error.message : String(error);
760
+ return { success: false, error: message };
761
+ }
545
762
  },
546
763
  },
547
764
  ];