@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
@@ -143,9 +143,11 @@ describe("Handler Execution", () => {
143
143
  it("should reject invalid collection names", async () => {
144
144
  const tool = tools.find((t) => t.name === "mysql_doc_create_collection")!;
145
145
 
146
- await expect(
147
- tool.handler({ name: "invalid-name" }, mockContext),
148
- ).rejects.toThrow("Invalid collection name");
146
+ const result = await tool.handler({ name: "invalid-name" }, mockContext);
147
+ expect(result).toEqual({
148
+ success: false,
149
+ error: "Invalid collection name",
150
+ });
149
151
  });
150
152
 
151
153
  it("should add validation when specified", async () => {
@@ -175,7 +177,9 @@ describe("Handler Execution", () => {
175
177
  });
176
178
 
177
179
  it("should support ifNotExists parameter", async () => {
178
- mockAdapter.executeQuery.mockResolvedValue(createMockQueryResult([]));
180
+ mockAdapter.executeQuery
181
+ .mockResolvedValueOnce(createMockQueryResult([])) // checkCollectionExists → false
182
+ .mockResolvedValue(createMockQueryResult([])); // CREATE TABLE succeeds
179
183
 
180
184
  const tool = tools.find((t) => t.name === "mysql_doc_create_collection")!;
181
185
  await tool.handler(
@@ -183,8 +187,30 @@ describe("Handler Execution", () => {
183
187
  mockContext,
184
188
  );
185
189
 
186
- const call = mockAdapter.executeQuery.mock.calls[0][0] as string;
187
- expect(call).toContain("CREATE TABLE IF NOT EXISTS");
190
+ const calls = mockAdapter.executeQuery.mock.calls;
191
+ const createCall = calls[calls.length - 1][0] as string;
192
+ expect(createCall).toContain("CREATE TABLE IF NOT EXISTS");
193
+ });
194
+
195
+ it("should return skipped when collection already exists with ifNotExists", async () => {
196
+ mockAdapter.executeQuery.mockResolvedValueOnce(
197
+ createMockQueryResult([{ "1": 1 }]), // checkCollectionExists → true
198
+ );
199
+
200
+ const tool = tools.find((t) => t.name === "mysql_doc_create_collection")!;
201
+ const result = await tool.handler(
202
+ { name: "my_collection", ifNotExists: true },
203
+ mockContext,
204
+ );
205
+
206
+ expect(result).toEqual({
207
+ success: true,
208
+ skipped: true,
209
+ collection: "my_collection",
210
+ reason: "Collection already exists",
211
+ });
212
+ // Should NOT have called CREATE TABLE
213
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
188
214
  });
189
215
 
190
216
  it("should not use IF NOT EXISTS by default", async () => {
@@ -194,7 +220,8 @@ describe("Handler Execution", () => {
194
220
  await tool.handler({ name: "my_collection" }, mockContext);
195
221
 
196
222
  const call = mockAdapter.executeQuery.mock.calls[0][0] as string;
197
- expect(call).toContain("CREATE TABLE `my_collection`");
223
+ expect(call).toContain("CREATE TABLE");
224
+ expect(call).toContain("`my_collection`");
198
225
  expect(call).not.toContain("IF NOT EXISTS");
199
226
  });
200
227
 
@@ -207,14 +234,33 @@ describe("Handler Execution", () => {
207
234
  const result = (await tool.handler(
208
235
  { name: "my_collection" },
209
236
  mockContext,
210
- )) as { success: boolean; reason: string };
237
+ )) as { success: boolean; error: string };
211
238
 
212
239
  expect(result).toHaveProperty("success", false);
213
240
  expect(result).toHaveProperty(
214
- "reason",
241
+ "error",
215
242
  "Collection 'my_collection' already exists",
216
243
  );
217
244
  });
245
+
246
+ it("should return exists: false for nonexistent schema", async () => {
247
+ mockAdapter.executeQuery.mockRejectedValue(
248
+ new Error(
249
+ "Query failed: Execute failed: Unknown database 'fake_schema'",
250
+ ),
251
+ );
252
+
253
+ const tool = tools.find((t) => t.name === "mysql_doc_create_collection")!;
254
+ const result = await tool.handler(
255
+ { name: "my_collection", schema: "fake_schema" },
256
+ mockContext,
257
+ );
258
+
259
+ expect(result).toEqual({
260
+ exists: false,
261
+ schema: "fake_schema",
262
+ });
263
+ });
218
264
  });
219
265
 
220
266
  describe("mysql_doc_drop_collection", () => {
@@ -260,9 +306,14 @@ describe("Handler Execution", () => {
260
306
 
261
307
  it("should reject invalid collection names", async () => {
262
308
  const tool = tools.find((t) => t.name === "mysql_doc_drop_collection")!;
263
- await expect(
264
- tool.handler({ name: "bad;drop table users" }, mockContext),
265
- ).rejects.toThrow("Invalid collection name");
309
+ const result = await tool.handler(
310
+ { name: "bad;drop table users" },
311
+ mockContext,
312
+ );
313
+ expect(result).toEqual({
314
+ success: false,
315
+ error: "Invalid collection name",
316
+ });
266
317
  });
267
318
 
268
319
  it("should return graceful error when collection does not exist", async () => {
@@ -274,20 +325,34 @@ describe("Handler Execution", () => {
274
325
  const result = (await tool.handler(
275
326
  { name: "nonexistent", ifExists: false },
276
327
  mockContext,
277
- )) as { success: boolean; reason: string };
328
+ )) as { success: boolean; error: string };
278
329
 
279
330
  expect(result).toHaveProperty("success", false);
280
331
  expect(result).toHaveProperty(
281
- "reason",
332
+ "error",
282
333
  "Collection 'nonexistent' does not exist",
283
334
  );
284
335
  });
336
+
337
+ it("should return exists: false for nonexistent schema", async () => {
338
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // schema does not exist
339
+
340
+ const tool = tools.find((t) => t.name === "mysql_doc_drop_collection")!;
341
+ const result = await tool.handler(
342
+ { name: "users", schema: "nonexistent_schema" },
343
+ mockContext,
344
+ );
345
+
346
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
347
+ expect(result).toEqual({
348
+ exists: false,
349
+ schema: "nonexistent_schema",
350
+ });
351
+ });
285
352
  });
286
353
 
287
354
  describe("mysql_doc_find", () => {
288
- it("should query documents with filters", async () => {
289
- // First call: collection existence check
290
- // Second call: actual document query
355
+ it("should query documents with valid filter", async () => {
291
356
  mockAdapter.executeQuery
292
357
  .mockResolvedValueOnce(createMockQueryResult([{ "1": 1 }])) // collection exists
293
358
  .mockResolvedValueOnce(
@@ -298,19 +363,86 @@ describe("Handler Execution", () => {
298
363
  const result = await tool.handler(
299
364
  {
300
365
  collection: "users",
301
- filter: "$.age > 20",
366
+ filter: "$.age",
302
367
  },
303
368
  mockContext,
304
369
  );
305
370
 
306
371
  expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(2);
307
372
  const call = mockAdapter.executeQuery.mock.calls[1][0] as string;
308
- expect(call).toContain(
309
- "WHERE JSON_EXTRACT(doc, '$.age > 20') IS NOT NULL",
310
- );
373
+ expect(call).toContain("WHERE JSON_EXTRACT(doc, '$.age') IS NOT NULL");
311
374
  expect(result).toHaveProperty("documents");
312
375
  });
313
376
 
377
+ it("should reject SQL injection in filter", async () => {
378
+ mockAdapter.executeQuery.mockResolvedValueOnce(
379
+ createMockQueryResult([{ "1": 1 }]),
380
+ ); // collection exists
381
+
382
+ const tool = tools.find((t) => t.name === "mysql_doc_find")!;
383
+ const result = await tool.handler(
384
+ {
385
+ collection: "users",
386
+ filter: "$') IS NOT NULL OR 1=1 -- ",
387
+ },
388
+ mockContext,
389
+ );
390
+
391
+ expect(result).toHaveProperty("success", false);
392
+ expect(result).toHaveProperty("error");
393
+ // Should NOT have executed the document query
394
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
395
+ });
396
+
397
+ it("should reject invalid JSON path in filter", async () => {
398
+ mockAdapter.executeQuery.mockResolvedValueOnce(
399
+ createMockQueryResult([{ "1": 1 }]),
400
+ ); // collection exists
401
+
402
+ const tool = tools.find((t) => t.name === "mysql_doc_find")!;
403
+ const result = await tool.handler(
404
+ {
405
+ collection: "users",
406
+ filter: "$.age > 20",
407
+ },
408
+ mockContext,
409
+ );
410
+
411
+ expect(result).toHaveProperty("success", false);
412
+ expect(result).toHaveProperty("error");
413
+ });
414
+
415
+ it("should use schema parameter for collection lookup", async () => {
416
+ mockAdapter.executeQuery
417
+ .mockResolvedValueOnce(
418
+ createMockQueryResult([{ SCHEMA_NAME: "otherdb" }]),
419
+ ) // schema exists
420
+ .mockResolvedValueOnce(createMockQueryResult([{ "1": 1 }])) // collection exists
421
+ .mockResolvedValueOnce(createMockQueryResult([]));
422
+
423
+ const tool = tools.find((t) => t.name === "mysql_doc_find")!;
424
+ await tool.handler(
425
+ { collection: "my_coll", schema: "otherdb" },
426
+ mockContext,
427
+ );
428
+
429
+ // First call: schema existence check
430
+ expect(mockAdapter.executeQuery).toHaveBeenNthCalledWith(
431
+ 1,
432
+ expect.stringContaining("SCHEMATA"),
433
+ ["otherdb"],
434
+ );
435
+ // Second call: collection existence check with schema
436
+ expect(mockAdapter.executeQuery).toHaveBeenNthCalledWith(
437
+ 2,
438
+ expect.any(String),
439
+ ["otherdb", "my_coll"],
440
+ );
441
+ // Query should use qualified table ref
442
+ const queryCall = mockAdapter.executeQuery.mock.calls[2][0] as string;
443
+ expect(queryCall).toContain("`otherdb`.`my_coll`");
444
+ });
445
+
314
446
  it("should return exists: false without error field for nonexistent collection", async () => {
315
447
  mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // collection does not exist
316
448
 
@@ -386,9 +518,14 @@ describe("Handler Execution", () => {
386
518
 
387
519
  it("should validate collection name", async () => {
388
520
  const tool = tools.find((t) => t.name === "mysql_doc_find")!;
389
- await expect(
390
- tool.handler({ collection: "invalid-name; --" }, mockContext),
391
- ).rejects.toThrow("Invalid collection name");
521
+ const result = await tool.handler(
522
+ { collection: "invalid-name; --" },
523
+ mockContext,
524
+ );
525
+ expect(result).toEqual({
526
+ success: false,
527
+ error: "Invalid collection name",
528
+ });
392
529
  });
393
530
 
394
531
  it("should return graceful response when collection does not exist", async () => {
@@ -410,6 +547,22 @@ describe("Handler Execution", () => {
410
547
  expect(result).not.toHaveProperty("error");
411
548
  expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
412
549
  });
550
+
551
+ it("should return exists: false for nonexistent schema", async () => {
552
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // schema does not exist
553
+
554
+ const tool = tools.find((t) => t.name === "mysql_doc_find")!;
555
+ const result = await tool.handler(
556
+ { collection: "users", schema: "nonexistent_schema" },
557
+ mockContext,
558
+ );
559
+
560
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
561
+ expect(result).toEqual({
562
+ exists: false,
563
+ schema: "nonexistent_schema",
564
+ });
565
+ });
413
566
  });
414
567
 
415
568
  describe("mysql_doc_add", () => {
@@ -454,15 +607,17 @@ describe("Handler Execution", () => {
454
607
 
455
608
  it("should reject invalid collection names", async () => {
456
609
  const tool = tools.find((t) => t.name === "mysql_doc_add")!;
457
- await expect(
458
- tool.handler(
459
- {
460
- collection: "invalid-name",
461
- documents: [{ name: "test" }],
462
- },
463
- mockContext,
464
- ),
465
- ).rejects.toThrow("Invalid collection name");
610
+ const result = await tool.handler(
611
+ {
612
+ collection: "invalid-name",
613
+ documents: [{ name: "test" }],
614
+ },
615
+ mockContext,
616
+ );
617
+ expect(result).toEqual({
618
+ success: false,
619
+ error: "Invalid collection name",
620
+ });
466
621
  });
467
622
 
468
623
  it("should return graceful response when collection does not exist", async () => {
@@ -481,6 +636,61 @@ describe("Handler Execution", () => {
481
636
  expect(result).toHaveProperty("collection", "nonexistent");
482
637
  expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
483
638
  });
639
+
640
+ it("should return exists: false for nonexistent schema", async () => {
641
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // schema does not exist
642
+
643
+ const tool = tools.find((t) => t.name === "mysql_doc_add")!;
644
+ const result = await tool.handler(
645
+ {
646
+ collection: "users",
647
+ schema: "nonexistent_schema",
648
+ documents: [{ name: "test" }],
649
+ },
650
+ mockContext,
651
+ );
652
+
653
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
654
+ expect(result).toEqual({
655
+ exists: false,
656
+ schema: "nonexistent_schema",
657
+ });
658
+ });
659
+
660
+ it("should use schema parameter for collection lookup", async () => {
661
+ mockAdapter.executeQuery
662
+ .mockResolvedValueOnce(
663
+ createMockQueryResult([{ SCHEMA_NAME: "otherdb" }]),
664
+ ) // schema exists
665
+ .mockResolvedValueOnce(createMockQueryResult([{ "1": 1 }])) // collection exists
666
+ .mockResolvedValue(createMockQueryResult([]));
667
+
668
+ const tool = tools.find((t) => t.name === "mysql_doc_add")!;
669
+ await tool.handler(
670
+ {
671
+ collection: "my_coll",
672
+ schema: "otherdb",
673
+ documents: [{ name: "test" }],
674
+ },
675
+ mockContext,
676
+ );
677
+
678
+ // First call: schema existence check
679
+ expect(mockAdapter.executeQuery).toHaveBeenNthCalledWith(
680
+ 1,
681
+ expect.stringContaining("SCHEMATA"),
682
+ ["otherdb"],
683
+ );
684
+ // Second call: collection existence check with schema
685
+ expect(mockAdapter.executeQuery).toHaveBeenNthCalledWith(
686
+ 2,
687
+ expect.any(String),
688
+ ["otherdb", "my_coll"],
689
+ );
690
+ // Insert should use qualified table ref
691
+ const insertCall = mockAdapter.executeQuery.mock.calls[2][0] as string;
692
+ expect(insertCall).toContain("`otherdb`.`my_coll`");
693
+ });
484
694
  });
485
695
 
486
696
  describe("mysql_doc_modify", () => {
@@ -546,32 +756,36 @@ describe("Handler Execution", () => {
546
756
  expect(call).toContain("UPDATE `users` SET");
547
757
  });
548
758
 
549
- it("should throw if no modifications specified", async () => {
759
+ it("should return error if no modifications specified", async () => {
550
760
  const tool = tools.find((t) => t.name === "mysql_doc_modify")!;
551
761
 
552
- await expect(
553
- tool.handler(
554
- {
555
- collection: "users",
556
- filter: "$.active",
557
- },
558
- mockContext,
559
- ),
560
- ).rejects.toThrow("No modifications specified");
762
+ const result = await tool.handler(
763
+ {
764
+ collection: "users",
765
+ filter: "$.active",
766
+ },
767
+ mockContext,
768
+ );
769
+ expect(result).toEqual({
770
+ success: false,
771
+ error: "No modifications specified",
772
+ });
561
773
  });
562
774
 
563
775
  it("should reject invalid collection names", async () => {
564
776
  const tool = tools.find((t) => t.name === "mysql_doc_modify")!;
565
- await expect(
566
- tool.handler(
567
- {
568
- collection: "invalid-name",
569
- filter: "$.id",
570
- set: { a: 1 },
571
- },
572
- mockContext,
573
- ),
574
- ).rejects.toThrow("Invalid collection name");
777
+ const result = await tool.handler(
778
+ {
779
+ collection: "invalid-name",
780
+ filter: "$.id",
781
+ set: { a: 1 },
782
+ },
783
+ mockContext,
784
+ );
785
+ expect(result).toEqual({
786
+ success: false,
787
+ error: "Invalid collection name",
788
+ });
575
789
  });
576
790
 
577
791
  it("should return graceful response when collection does not exist", async () => {
@@ -591,6 +805,50 @@ describe("Handler Execution", () => {
591
805
  expect(result).toHaveProperty("collection", "nonexistent");
592
806
  expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
593
807
  });
808
+
809
+ it("should return exists: false for nonexistent schema", async () => {
810
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // schema does not exist
811
+
812
+ const tool = tools.find((t) => t.name === "mysql_doc_modify")!;
813
+ const result = await tool.handler(
814
+ {
815
+ collection: "users",
816
+ schema: "nonexistent_schema",
817
+ filter: "$.name",
818
+ set: { status: "active" },
819
+ },
820
+ mockContext,
821
+ );
822
+
823
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
824
+ expect(result).toEqual({
825
+ exists: false,
826
+ schema: "nonexistent_schema",
827
+ });
828
+ });
829
+
830
+ it("should use schema parameter for collection lookup", async () => {
831
+ mockAdapter.executeQuery
832
+ .mockResolvedValueOnce(
833
+ createMockQueryResult([{ SCHEMA_NAME: "otherdb" }]),
834
+ ) // schema exists
835
+ .mockResolvedValueOnce(createMockQueryResult([{ "1": 1 }])) // collection exists
836
+ .mockResolvedValueOnce(createMockQueryResult([], 2));
837
+
838
+ const tool = tools.find((t) => t.name === "mysql_doc_modify")!;
839
+ await tool.handler(
840
+ {
841
+ collection: "my_coll",
842
+ schema: "otherdb",
843
+ filter: "$.name",
844
+ set: { status: "active" },
845
+ },
846
+ mockContext,
847
+ );
848
+
849
+ const updateCall = mockAdapter.executeQuery.mock.calls[2][0] as string;
850
+ expect(updateCall).toContain("`otherdb`.`my_coll`");
851
+ });
594
852
  });
595
853
 
596
854
  describe("mysql_doc_remove", () => {
@@ -617,15 +875,17 @@ describe("Handler Execution", () => {
617
875
 
618
876
  it("should reject invalid collection names", async () => {
619
877
  const tool = tools.find((t) => t.name === "mysql_doc_remove")!;
620
- await expect(
621
- tool.handler(
622
- {
623
- collection: "invalid-name",
624
- filter: "$.id",
625
- },
626
- mockContext,
627
- ),
628
- ).rejects.toThrow("Invalid collection name");
878
+ const result = await tool.handler(
879
+ {
880
+ collection: "invalid-name",
881
+ filter: "$.id",
882
+ },
883
+ mockContext,
884
+ );
885
+ expect(result).toEqual({
886
+ success: false,
887
+ error: "Invalid collection name",
888
+ });
629
889
  });
630
890
 
631
891
  it("should return graceful response when collection does not exist", async () => {
@@ -644,6 +904,48 @@ describe("Handler Execution", () => {
644
904
  expect(result).toHaveProperty("collection", "nonexistent");
645
905
  expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
646
906
  });
907
+
908
+ it("should return exists: false for nonexistent schema", async () => {
909
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // schema does not exist
910
+
911
+ const tool = tools.find((t) => t.name === "mysql_doc_remove")!;
912
+ const result = await tool.handler(
913
+ {
914
+ collection: "users",
915
+ schema: "nonexistent_schema",
916
+ filter: "$.id",
917
+ },
918
+ mockContext,
919
+ );
920
+
921
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
922
+ expect(result).toEqual({
923
+ exists: false,
924
+ schema: "nonexistent_schema",
925
+ });
926
+ });
927
+
928
+ it("should use schema parameter for collection lookup", async () => {
929
+ mockAdapter.executeQuery
930
+ .mockResolvedValueOnce(
931
+ createMockQueryResult([{ SCHEMA_NAME: "otherdb" }]),
932
+ ) // schema exists
933
+ .mockResolvedValueOnce(createMockQueryResult([{ "1": 1 }])) // collection exists
934
+ .mockResolvedValueOnce(createMockQueryResult([], 1));
935
+
936
+ const tool = tools.find((t) => t.name === "mysql_doc_remove")!;
937
+ await tool.handler(
938
+ {
939
+ collection: "my_coll",
940
+ schema: "otherdb",
941
+ filter: "$.name",
942
+ },
943
+ mockContext,
944
+ );
945
+
946
+ const deleteCall = mockAdapter.executeQuery.mock.calls[2][0] as string;
947
+ expect(deleteCall).toContain("`otherdb`.`my_coll`");
948
+ });
647
949
  });
648
950
 
649
951
  describe("mysql_doc_create_index", () => {
@@ -719,30 +1021,31 @@ describe("Handler Execution", () => {
719
1021
 
720
1022
  it("should reject invalid collection names", async () => {
721
1023
  const tool = tools.find((t) => t.name === "mysql_doc_create_index")!;
722
- await expect(
723
- tool.handler(
724
- {
725
- collection: "invalid-name",
726
- name: "index",
727
- fields: [{ path: "email", type: "TEXT" }],
728
- },
729
- mockContext,
730
- ),
731
- ).rejects.toThrow("Invalid collection name");
1024
+ const result = await tool.handler(
1025
+ {
1026
+ collection: "invalid-name",
1027
+ name: "index",
1028
+ fields: [{ path: "email", type: "TEXT" }],
1029
+ },
1030
+ mockContext,
1031
+ );
1032
+ expect(result).toEqual({
1033
+ success: false,
1034
+ error: "Invalid collection name",
1035
+ });
732
1036
  });
733
1037
 
734
1038
  it("should reject invalid index names", async () => {
735
1039
  const tool = tools.find((t) => t.name === "mysql_doc_create_index")!;
736
- await expect(
737
- tool.handler(
738
- {
739
- collection: "valid_coll",
740
- name: "invalid-index",
741
- fields: [{ path: "email", type: "TEXT" }],
742
- },
743
- mockContext,
744
- ),
745
- ).rejects.toThrow("Invalid index name");
1040
+ const result = await tool.handler(
1041
+ {
1042
+ collection: "valid_coll",
1043
+ name: "invalid-index",
1044
+ fields: [{ path: "email", type: "TEXT" }],
1045
+ },
1046
+ mockContext,
1047
+ );
1048
+ expect(result).toEqual({ success: false, error: "Invalid index name" });
746
1049
  });
747
1050
 
748
1051
  it("should return graceful response when collection does not exist", async () => {
@@ -763,6 +1066,53 @@ describe("Handler Execution", () => {
763
1066
  expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
764
1067
  });
765
1068
 
1069
+ it("should return exists: false for nonexistent schema", async () => {
1070
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // schema does not exist
1071
+
1072
+ const tool = tools.find((t) => t.name === "mysql_doc_create_index")!;
1073
+ const result = await tool.handler(
1074
+ {
1075
+ collection: "users",
1076
+ schema: "nonexistent_schema",
1077
+ name: "idx_test",
1078
+ fields: [{ path: "email", type: "TEXT" }],
1079
+ },
1080
+ mockContext,
1081
+ );
1082
+
1083
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
1084
+ expect(result).toEqual({
1085
+ exists: false,
1086
+ schema: "nonexistent_schema",
1087
+ });
1088
+ });
1089
+
1090
+ it("should use schema parameter for collection lookup", async () => {
1091
+ mockAdapter.executeQuery
1092
+ .mockResolvedValueOnce(
1093
+ createMockQueryResult([{ SCHEMA_NAME: "otherdb" }]),
1094
+ ) // schema exists
1095
+ .mockResolvedValueOnce(createMockQueryResult([{ "1": 1 }])) // collection exists
1096
+ .mockResolvedValue(createMockQueryResult([]));
1097
+
1098
+ const tool = tools.find((t) => t.name === "mysql_doc_create_index")!;
1099
+ await tool.handler(
1100
+ {
1101
+ collection: "my_coll",
1102
+ schema: "otherdb",
1103
+ name: "idx_name",
1104
+ fields: [{ path: "name", type: "TEXT" }],
1105
+ },
1106
+ mockContext,
1107
+ );
1108
+
1109
+ // ALTER TABLE and CREATE INDEX should use qualified table ref
1110
+ const alterCall = mockAdapter.executeQuery.mock.calls[2][0] as string;
1111
+ expect(alterCall).toContain("`otherdb`.`my_coll`");
1112
+ const indexCall = mockAdapter.executeQuery.mock.calls[3][0] as string;
1113
+ expect(indexCall).toContain("`otherdb`.`my_coll`");
1114
+ });
1115
+
766
1116
  it("should return graceful error on duplicate column", async () => {
767
1117
  mockAdapter.executeQuery
768
1118
  .mockResolvedValueOnce(createMockQueryResult([{ "1": 1 }])) // collection exists
@@ -776,10 +1126,10 @@ describe("Handler Execution", () => {
776
1126
  fields: [{ path: "email", type: "TEXT" }],
777
1127
  },
778
1128
  mockContext,
779
- )) as { success: boolean; reason: string };
1129
+ )) as { success: boolean; error: string };
780
1130
 
781
1131
  expect(result).toHaveProperty("success", false);
782
- expect(result.reason).toContain("already exist");
1132
+ expect(result.error).toContain("already exist");
783
1133
  });
784
1134
  });
785
1135
 
@@ -806,13 +1156,18 @@ describe("Handler Execution", () => {
806
1156
 
807
1157
  it("should reject invalid collection names", async () => {
808
1158
  const tool = tools.find((t) => t.name === "mysql_doc_collection_info")!;
809
- await expect(
810
- tool.handler({ collection: "invalid-nam$" }, mockContext),
811
- ).rejects.toThrow("Invalid collection name");
1159
+ const result = await tool.handler(
1160
+ { collection: "invalid-nam$" },
1161
+ mockContext,
1162
+ );
1163
+ expect(result).toEqual({
1164
+ success: false,
1165
+ error: "Invalid collection name",
1166
+ });
812
1167
  });
813
1168
 
814
1169
  it("should return graceful response when collection does not exist", async () => {
815
- mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // collection does not exist
1170
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // collection does not exist (no schema → skip schema check)
816
1171
 
817
1172
  const tool = tools.find((t) => t.name === "mysql_doc_collection_info")!;
818
1173
  const result = (await tool.handler(
@@ -824,5 +1179,21 @@ describe("Handler Execution", () => {
824
1179
  expect(result).toHaveProperty("collection", "nonexistent");
825
1180
  expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
826
1181
  });
1182
+
1183
+ it("should return exists: false for nonexistent schema", async () => {
1184
+ mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([])); // schema does not exist
1185
+
1186
+ const tool = tools.find((t) => t.name === "mysql_doc_collection_info")!;
1187
+ const result = await tool.handler(
1188
+ { collection: "users", schema: "nonexistent_schema" },
1189
+ mockContext,
1190
+ );
1191
+
1192
+ expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
1193
+ expect(result).toEqual({
1194
+ exists: false,
1195
+ schema: "nonexistent_schema",
1196
+ });
1197
+ });
827
1198
  });
828
1199
  });