@neverinfamous/mysql-mcp 2.3.1 → 3.0.1
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.
- package/.dockerignore +1 -0
- package/.gitattributes +18 -0
- package/.github/workflows/codeql.yml +2 -2
- package/.github/workflows/docker-publish.yml +5 -5
- package/CHANGELOG.md +348 -122
- package/DOCKER_README.md +81 -40
- package/README.md +87 -46
- package/VERSION +1 -1
- package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
- package/dist/__tests__/mocks/adapter.js +2 -0
- package/dist/__tests__/mocks/adapter.js.map +1 -1
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
- package/dist/adapters/DatabaseAdapter.js +50 -9
- package/dist/adapters/DatabaseAdapter.js.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.d.ts +6 -0
- package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.js +8 -0
- package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
- package/dist/adapters/mysql/SchemaManager.js +16 -15
- package/dist/adapters/mysql/SchemaManager.js.map +1 -1
- package/dist/adapters/mysql/prompts/index.js +10 -20
- package/dist/adapters/mysql/prompts/index.js.map +1 -1
- package/dist/adapters/mysql/prompts/proxysqlSetup.js +1 -1
- package/dist/adapters/mysql/resources/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/docstore.js +10 -7
- package/dist/adapters/mysql/resources/docstore.js.map +1 -1
- package/dist/adapters/mysql/resources/events.js +11 -8
- package/dist/adapters/mysql/resources/events.js.map +1 -1
- package/dist/adapters/mysql/resources/indexes.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/indexes.js +12 -15
- package/dist/adapters/mysql/resources/indexes.js.map +1 -1
- package/dist/adapters/mysql/resources/innodb.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/innodb.js +20 -17
- package/dist/adapters/mysql/resources/innodb.js.map +1 -1
- package/dist/adapters/mysql/resources/locks.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/locks.js +9 -6
- package/dist/adapters/mysql/resources/locks.js.map +1 -1
- package/dist/adapters/mysql/resources/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/performance.js +15 -15
- package/dist/adapters/mysql/resources/performance.js.map +1 -1
- package/dist/adapters/mysql/resources/spatial.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/spatial.js +9 -6
- package/dist/adapters/mysql/resources/spatial.js.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.js +12 -9
- package/dist/adapters/mysql/resources/sysschema.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.js +170 -121
- package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.js +106 -57
- package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.js +183 -101
- package/dist/adapters/mysql/tools/admin/monitoring.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.js +164 -120
- package/dist/adapters/mysql/tools/cluster/group-replication.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +212 -145
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.js +6 -4
- package/dist/adapters/mysql/tools/codemode/index.js.map +1 -1
- package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/core.js +152 -29
- package/dist/adapters/mysql/tools/core.js.map +1 -1
- package/dist/adapters/mysql/tools/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/docstore.js +340 -163
- package/dist/adapters/mysql/tools/docstore.js.map +1 -1
- package/dist/adapters/mysql/tools/events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/events.js +284 -198
- package/dist/adapters/mysql/tools/events.js.map +1 -1
- package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/core.js +11 -39
- package/dist/adapters/mysql/tools/json/core.js.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.js +15 -33
- package/dist/adapters/mysql/tools/json/enhanced.js.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.js +13 -24
- package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.js +3 -0
- package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.js +89 -60
- package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.js +151 -127
- package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.js +289 -176
- package/dist/adapters/mysql/tools/proxysql.js.map +1 -1
- package/dist/adapters/mysql/tools/replication.js +75 -49
- package/dist/adapters/mysql/tools/replication.js.map +1 -1
- package/dist/adapters/mysql/tools/roles.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/roles.js +224 -182
- package/dist/adapters/mysql/tools/roles.js.map +1 -1
- package/dist/adapters/mysql/tools/router.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/router.js +168 -67
- package/dist/adapters/mysql/tools/router.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.js +21 -3
- package/dist/adapters/mysql/tools/schema/constraints.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.js +61 -14
- package/dist/adapters/mysql/tools/schema/management.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.js +27 -4
- package/dist/adapters/mysql/tools/schema/routines.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.js +24 -3
- package/dist/adapters/mysql/tools/schema/scheduled_events.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.js +23 -2
- package/dist/adapters/mysql/tools/schema/triggers.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.js +47 -7
- package/dist/adapters/mysql/tools/schema/views.js.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.js +102 -34
- package/dist/adapters/mysql/tools/security/audit.js.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.js +264 -205
- package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.js +137 -104
- package/dist/adapters/mysql/tools/security/encryption.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.js +71 -59
- package/dist/adapters/mysql/tools/shell/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.js +61 -47
- package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.js +19 -5
- package/dist/adapters/mysql/tools/spatial/geometry.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.js +42 -17
- package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.js +109 -57
- package/dist/adapters/mysql/tools/spatial/queries.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.js +103 -50
- package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.js +128 -79
- package/dist/adapters/mysql/tools/stats/comparative.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.js +174 -102
- package/dist/adapters/mysql/tools/stats/descriptive.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.js +50 -25
- package/dist/adapters/mysql/tools/sysschema/activity.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.js +121 -66
- package/dist/adapters/mysql/tools/sysschema/performance.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.js +101 -64
- package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.js +18 -32
- package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
- package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/transactions.js +48 -23
- package/dist/adapters/mysql/tools/transactions.js.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.d.ts +15 -0
- package/dist/adapters/mysql/types/proxysql-types.d.ts.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.js +33 -1
- package/dist/adapters/mysql/types/proxysql-types.js.map +1 -1
- package/dist/adapters/mysql/types/router-types.d.ts +1 -1
- package/dist/adapters/mysql/types/router-types.js +1 -1
- package/dist/adapters/mysql/types/router-types.js.map +1 -1
- package/dist/adapters/mysql/types/shell-types.js +2 -2
- package/dist/adapters/mysql/types/shell-types.js.map +1 -1
- package/dist/adapters/mysql/types.d.ts +485 -21
- package/dist/adapters/mysql/types.d.ts.map +1 -1
- package/dist/adapters/mysql/types.js +546 -19
- package/dist/adapters/mysql/types.js.map +1 -1
- package/dist/auth/scopes.js +1 -1
- package/dist/auth/scopes.js.map +1 -1
- package/dist/codemode/api.d.ts +3 -2
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/api.js +80 -5
- package/dist/codemode/api.js.map +1 -1
- package/dist/codemode/sandbox-factory.js +1 -1
- package/dist/codemode/sandbox-factory.js.map +1 -1
- package/dist/codemode/types.d.ts +26 -0
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/codemode/types.js +2 -0
- package/dist/codemode/types.js.map +1 -1
- package/dist/codemode/worker-sandbox.d.ts +4 -2
- package/dist/codemode/worker-sandbox.d.ts.map +1 -1
- package/dist/codemode/worker-sandbox.js +66 -7
- package/dist/codemode/worker-sandbox.js.map +1 -1
- package/dist/codemode/worker-script.d.ts +3 -0
- package/dist/codemode/worker-script.d.ts.map +1 -1
- package/dist/codemode/worker-script.js +128 -75
- package/dist/codemode/worker-script.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +37 -31
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +1 -1
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +1 -2
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/pool/ConnectionPool.d.ts.map +1 -1
- package/dist/pool/ConnectionPool.js.map +1 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +6 -0
- package/dist/transports/http.js.map +1 -1
- package/dist/utils/validators.d.ts +1 -1
- package/dist/utils/validators.d.ts.map +1 -1
- package/dist/utils/validators.js.map +1 -1
- package/package.json +4 -4
- package/releases/v3.0.0-release-notes.md +81 -0
- package/releases/v3.0.1-release-notes.md +20 -0
- package/src/__tests__/mocks/adapter.ts +3 -0
- package/src/__tests__/perf.test.ts +6 -6
- package/src/adapters/DatabaseAdapter.ts +58 -9
- package/src/adapters/__tests__/DatabaseAdapter.test.ts +89 -8
- package/src/adapters/mysql/MySQLAdapter.ts +17 -2
- package/src/adapters/mysql/SchemaManager.ts +21 -21
- package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
- package/src/adapters/mysql/prompts/index.ts +12 -22
- package/src/adapters/mysql/prompts/proxysqlSetup.ts +1 -1
- package/src/adapters/mysql/resources/docstore.ts +13 -10
- package/src/adapters/mysql/resources/events.ts +12 -12
- package/src/adapters/mysql/resources/indexes.ts +17 -19
- package/src/adapters/mysql/resources/innodb.ts +23 -22
- package/src/adapters/mysql/resources/locks.ts +9 -7
- package/src/adapters/mysql/resources/performance.ts +23 -18
- package/src/adapters/mysql/resources/spatial.ts +9 -7
- package/src/adapters/mysql/resources/sysschema.ts +12 -11
- package/src/adapters/mysql/tools/__tests__/core.test.ts +126 -55
- package/src/adapters/mysql/tools/__tests__/docstore.test.ts +459 -88
- package/src/adapters/mysql/tools/__tests__/events.test.ts +281 -103
- package/src/adapters/mysql/tools/__tests__/proxysql.test.ts +128 -28
- package/src/adapters/mysql/tools/__tests__/replication.test.ts +48 -2
- package/src/adapters/mysql/tools/__tests__/roles.test.ts +15 -18
- package/src/adapters/mysql/tools/__tests__/router.test.ts +32 -5
- package/src/adapters/mysql/tools/__tests__/security.test.ts +126 -2
- package/src/adapters/mysql/tools/__tests__/security_injection.test.ts +84 -76
- package/src/adapters/mysql/tools/__tests__/security_integration.test.ts +47 -51
- package/src/adapters/mysql/tools/__tests__/spatial.test.ts +11 -10
- package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +54 -38
- package/src/adapters/mysql/tools/__tests__/stats.test.ts +285 -152
- package/src/adapters/mysql/tools/__tests__/transactions.test.ts +13 -13
- package/src/adapters/mysql/tools/admin/__tests__/backup.test.ts +171 -25
- package/src/adapters/mysql/tools/admin/__tests__/maintenance.test.ts +240 -4
- package/src/adapters/mysql/tools/admin/__tests__/monitoring-summary.test.ts +274 -0
- package/src/adapters/mysql/tools/admin/__tests__/monitoring.test.ts +94 -5
- package/src/adapters/mysql/tools/admin/backup.ts +193 -143
- package/src/adapters/mysql/tools/admin/maintenance.ts +118 -69
- package/src/adapters/mysql/tools/admin/monitoring.ts +201 -125
- package/src/adapters/mysql/tools/cluster/__tests__/group-replication.test.ts +69 -0
- package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +141 -0
- package/src/adapters/mysql/tools/cluster/group-replication.ts +172 -132
- package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +231 -157
- package/src/adapters/mysql/tools/codemode/__tests__/codemode-tool.test.ts +227 -0
- package/src/adapters/mysql/tools/codemode/index.ts +5 -3
- package/src/adapters/mysql/tools/core.ts +152 -38
- package/src/adapters/mysql/tools/docstore.ts +422 -205
- package/src/adapters/mysql/tools/events.ts +334 -233
- package/src/adapters/mysql/tools/json/__tests__/core.test.ts +20 -0
- package/src/adapters/mysql/tools/json/__tests__/enhanced.test.ts +82 -50
- package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +42 -3
- package/src/adapters/mysql/tools/json/core.ts +21 -42
- package/src/adapters/mysql/tools/json/enhanced.ts +22 -37
- package/src/adapters/mysql/tools/json/helpers.ts +21 -25
- package/src/adapters/mysql/tools/partitioning.ts +3 -0
- package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +98 -5
- package/src/adapters/mysql/tools/performance/__tests__/optimization-coverage.test.ts +515 -0
- package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +187 -0
- package/src/adapters/mysql/tools/performance/analysis.ts +95 -69
- package/src/adapters/mysql/tools/performance/optimization.ts +182 -153
- package/src/adapters/mysql/tools/proxysql.ts +314 -209
- package/src/adapters/mysql/tools/replication.ts +84 -57
- package/src/adapters/mysql/tools/roles.ts +274 -226
- package/src/adapters/mysql/tools/router.ts +181 -85
- package/src/adapters/mysql/tools/schema/__tests__/constraints.test.ts +13 -0
- package/src/adapters/mysql/tools/schema/__tests__/management.test.ts +60 -25
- package/src/adapters/mysql/tools/schema/__tests__/scheduled_events.test.ts +11 -0
- package/src/adapters/mysql/tools/schema/__tests__/triggers.test.ts +25 -4
- package/src/adapters/mysql/tools/schema/__tests__/views.test.ts +46 -14
- package/src/adapters/mysql/tools/schema/constraints.ts +22 -3
- package/src/adapters/mysql/tools/schema/management.ts +60 -15
- package/src/adapters/mysql/tools/schema/routines.ts +26 -4
- package/src/adapters/mysql/tools/schema/scheduled_events.ts +25 -3
- package/src/adapters/mysql/tools/schema/triggers.ts +27 -2
- package/src/adapters/mysql/tools/schema/views.ts +46 -8
- package/src/adapters/mysql/tools/security/__tests__/audit.test.ts +90 -4
- package/src/adapters/mysql/tools/security/audit.ts +113 -39
- package/src/adapters/mysql/tools/security/data-protection.ts +293 -233
- package/src/adapters/mysql/tools/security/encryption.ts +172 -139
- package/src/adapters/mysql/tools/shell/__tests__/backup.test.ts +29 -0
- package/src/adapters/mysql/tools/shell/backup.ts +90 -73
- package/src/adapters/mysql/tools/shell/restore.ts +62 -48
- package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +22 -14
- package/src/adapters/mysql/tools/spatial/__tests__/queries.test.ts +65 -51
- package/src/adapters/mysql/tools/spatial/geometry.ts +23 -7
- package/src/adapters/mysql/tools/spatial/operations.ts +60 -31
- package/src/adapters/mysql/tools/spatial/queries.ts +142 -65
- package/src/adapters/mysql/tools/spatial/setup.ts +121 -55
- package/src/adapters/mysql/tools/stats/__tests__/comparative.test.ts +12 -10
- package/src/adapters/mysql/tools/stats/comparative.ts +150 -98
- package/src/adapters/mysql/tools/stats/descriptive.ts +204 -127
- package/src/adapters/mysql/tools/sysschema/__tests__/error-paths.test.ts +222 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/performance.test.ts +45 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +6 -3
- package/src/adapters/mysql/tools/sysschema/activity.ts +52 -27
- package/src/adapters/mysql/tools/sysschema/performance.ts +132 -68
- package/src/adapters/mysql/tools/sysschema/resources.ts +105 -67
- package/src/adapters/mysql/tools/text/__tests__/fulltext.test.ts +45 -17
- package/src/adapters/mysql/tools/text/fulltext.ts +27 -38
- package/src/adapters/mysql/tools/transactions.ts +49 -24
- package/src/adapters/mysql/types/proxysql-types.ts +38 -1
- package/src/adapters/mysql/types/router-types.ts +1 -1
- package/src/adapters/mysql/types/shell-types.ts +2 -2
- package/src/adapters/mysql/types.ts +632 -19
- package/src/auth/__tests__/scopes.test.ts +2 -2
- package/src/auth/scopes.ts +1 -1
- package/src/codemode/__tests__/api.test.ts +417 -0
- package/src/codemode/__tests__/sandbox-factory.test.ts +158 -0
- package/src/codemode/__tests__/sandbox.test.ts +301 -0
- package/src/codemode/__tests__/security.test.ts +368 -0
- package/src/codemode/__tests__/worker-sandbox.test.ts +179 -0
- package/src/codemode/__tests__/worker-script.test.ts +226 -0
- package/src/codemode/api.ts +89 -5
- package/src/codemode/sandbox-factory.ts +1 -1
- package/src/codemode/types.ts +34 -0
- package/src/codemode/worker-sandbox.ts +74 -7
- package/src/codemode/worker-script.ts +157 -86
- package/src/constants/ServerInstructions.ts +37 -31
- package/src/filtering/ToolConstants.ts +1 -2
- package/src/filtering/__tests__/ToolFilter.test.ts +9 -9
- package/src/pool/ConnectionPool.ts +4 -1
- package/src/transports/__tests__/http.test.ts +15 -3
- package/src/transports/http.ts +12 -0
- package/src/utils/validators.ts +2 -1
- package/vitest.config.ts +3 -1
- package/CODE_MODE.md +0 -245
|
@@ -299,6 +299,26 @@ describe("JSON Core Tools", () => {
|
|
|
299
299
|
const call = mockAdapter.executeReadQuery.mock.calls[0][0] as string;
|
|
300
300
|
expect(call).toContain("JSON_KEYS");
|
|
301
301
|
});
|
|
302
|
+
|
|
303
|
+
it("should include count in response", async () => {
|
|
304
|
+
mockAdapter.executeReadQuery.mockResolvedValue(
|
|
305
|
+
createMockQueryResult([
|
|
306
|
+
{ json_keys: '["a", "b"]' },
|
|
307
|
+
{ json_keys: '["c"]' },
|
|
308
|
+
]),
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const tool = createJsonKeysTool(mockAdapter as unknown as MySQLAdapter);
|
|
312
|
+
const result = (await tool.handler(
|
|
313
|
+
{
|
|
314
|
+
table: "data",
|
|
315
|
+
column: "json_col",
|
|
316
|
+
},
|
|
317
|
+
mockContext,
|
|
318
|
+
)) as { rows: unknown[]; count: number };
|
|
319
|
+
|
|
320
|
+
expect(result.count).toBe(2);
|
|
321
|
+
});
|
|
302
322
|
});
|
|
303
323
|
|
|
304
324
|
describe("createJsonArrayAppendTool", () => {
|
|
@@ -506,67 +506,99 @@ describe("JSON Enhanced Tools", () => {
|
|
|
506
506
|
|
|
507
507
|
expect(result.suggestions).toHaveLength(5);
|
|
508
508
|
});
|
|
509
|
-
});
|
|
510
509
|
|
|
511
|
-
|
|
512
|
-
|
|
510
|
+
it("should produce valid DDL for qualified table names", async () => {
|
|
511
|
+
mockAdapter.executeQuery
|
|
512
|
+
.mockResolvedValueOnce(createMockQueryResult([{ key_name: "email" }]))
|
|
513
|
+
.mockResolvedValueOnce(
|
|
514
|
+
createMockQueryResult([{ value_type: "STRING", cardinality: 50 }]),
|
|
515
|
+
);
|
|
513
516
|
|
|
514
|
-
|
|
515
|
-
mockAdapter.executeQuery.mockRejectedValue(tableError);
|
|
516
|
-
const tool = createJsonNormalizeTool(
|
|
517
|
+
const tool = createJsonIndexSuggestTool(
|
|
517
518
|
mockAdapter as unknown as MySQLAdapter,
|
|
518
519
|
);
|
|
519
|
-
const result = await tool.handler(
|
|
520
|
-
{
|
|
520
|
+
const result = (await tool.handler(
|
|
521
|
+
{
|
|
522
|
+
table: "mydb.users",
|
|
523
|
+
column: "data",
|
|
524
|
+
},
|
|
521
525
|
mockContext,
|
|
522
|
-
);
|
|
523
|
-
expect(result).toEqual({ exists: false, table: "nonexistent" });
|
|
524
|
-
});
|
|
526
|
+
)) as { suggestions: { indexDdl: string }[] };
|
|
525
527
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
{ table: "nonexistent", column: "doc" },
|
|
531
|
-
mockContext,
|
|
528
|
+
expect(result.suggestions).toHaveLength(1);
|
|
529
|
+
// Table reference should be properly escaped as `mydb`.`users`
|
|
530
|
+
expect(result.suggestions[0].indexDdl).toContain(
|
|
531
|
+
"ALTER TABLE `mydb`.`users`",
|
|
532
532
|
);
|
|
533
|
-
|
|
533
|
+
// Index name should use only the table basename (no schema prefix)
|
|
534
|
+
expect(result.suggestions[0].indexDdl).toContain("idx_users_email");
|
|
535
|
+
expect(result.suggestions[0].indexDdl).not.toContain("idx_mydb.users");
|
|
534
536
|
});
|
|
535
537
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const tool = createJsonIndexSuggestTool(
|
|
539
|
-
mockAdapter as unknown as MySQLAdapter,
|
|
540
|
-
);
|
|
541
|
-
const result = await tool.handler(
|
|
542
|
-
{ table: "nonexistent", column: "doc" },
|
|
543
|
-
mockContext,
|
|
544
|
-
);
|
|
545
|
-
expect(result).toEqual({ exists: false, table: "nonexistent" });
|
|
546
|
-
});
|
|
538
|
+
describe("P154 Graceful Error Handling", () => {
|
|
539
|
+
const tableError = new Error("Table 'testdb.nonexistent' doesn't exist");
|
|
547
540
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
541
|
+
it("json_normalize should return exists: false for nonexistent table", async () => {
|
|
542
|
+
mockAdapter.executeQuery.mockRejectedValue(tableError);
|
|
543
|
+
const tool = createJsonNormalizeTool(
|
|
544
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
545
|
+
);
|
|
546
|
+
const result = await tool.handler(
|
|
547
|
+
{ table: "nonexistent", column: "doc" },
|
|
548
|
+
mockContext,
|
|
549
|
+
);
|
|
550
|
+
expect(result).toEqual({ exists: false, table: "nonexistent" });
|
|
551
|
+
});
|
|
559
552
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
553
|
+
it("json_stats should return exists: false for nonexistent table", async () => {
|
|
554
|
+
mockAdapter.executeQuery.mockRejectedValue(tableError);
|
|
555
|
+
const tool = createJsonStatsTool(
|
|
556
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
557
|
+
);
|
|
558
|
+
const result = await tool.handler(
|
|
559
|
+
{ table: "nonexistent", column: "doc" },
|
|
560
|
+
mockContext,
|
|
561
|
+
);
|
|
562
|
+
expect(result).toEqual({ exists: false, table: "nonexistent" });
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("json_index_suggest should return exists: false for nonexistent table", async () => {
|
|
566
|
+
mockAdapter.executeQuery.mockRejectedValue(tableError);
|
|
567
|
+
const tool = createJsonIndexSuggestTool(
|
|
568
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
569
|
+
);
|
|
570
|
+
const result = await tool.handler(
|
|
571
|
+
{ table: "nonexistent", column: "doc" },
|
|
572
|
+
mockContext,
|
|
573
|
+
);
|
|
574
|
+
expect(result).toEqual({ exists: false, table: "nonexistent" });
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it("json_merge should return success: false for invalid input", async () => {
|
|
578
|
+
mockAdapter.executeReadQuery.mockRejectedValue(
|
|
579
|
+
new Error("Invalid JSON text"),
|
|
580
|
+
);
|
|
581
|
+
const tool = createJsonMergeTool(
|
|
582
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
583
|
+
);
|
|
584
|
+
const result = await tool.handler(
|
|
585
|
+
{ json1: "not-json", json2: "{}" },
|
|
586
|
+
mockContext,
|
|
587
|
+
);
|
|
588
|
+
expect(result).toEqual({ success: false, error: "Invalid JSON text" });
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it("json_diff should return success: false for invalid input", async () => {
|
|
592
|
+
mockAdapter.executeReadQuery.mockRejectedValue(
|
|
593
|
+
new Error("Invalid JSON text"),
|
|
594
|
+
);
|
|
595
|
+
const tool = createJsonDiffTool(mockAdapter as unknown as MySQLAdapter);
|
|
596
|
+
const result = await tool.handler(
|
|
597
|
+
{ json1: "not-json", json2: "{}" },
|
|
598
|
+
mockContext,
|
|
599
|
+
);
|
|
600
|
+
expect(result).toEqual({ success: false, error: "Invalid JSON text" });
|
|
601
|
+
});
|
|
570
602
|
});
|
|
571
603
|
});
|
|
572
604
|
});
|
|
@@ -52,6 +52,24 @@ describe("JSON Helper Tools", () => {
|
|
|
52
52
|
// Value is parsed from JSON string
|
|
53
53
|
expect(result.value).toEqual({ a: 1 });
|
|
54
54
|
});
|
|
55
|
+
|
|
56
|
+
it("should return rowFound: false for nonexistent row", async () => {
|
|
57
|
+
mockAdapter.executeReadQuery.mockResolvedValue(createMockQueryResult([]));
|
|
58
|
+
|
|
59
|
+
const tool = createJsonGetTool(mockAdapter as unknown as MySQLAdapter);
|
|
60
|
+
const result = (await tool.handler(
|
|
61
|
+
{
|
|
62
|
+
table: "data",
|
|
63
|
+
column: "json_col",
|
|
64
|
+
path: "$.a",
|
|
65
|
+
id: 999,
|
|
66
|
+
},
|
|
67
|
+
mockContext,
|
|
68
|
+
)) as { value: null; rowFound: boolean };
|
|
69
|
+
|
|
70
|
+
expect(result.value).toBeNull();
|
|
71
|
+
expect(result.rowFound).toBe(false);
|
|
72
|
+
});
|
|
55
73
|
});
|
|
56
74
|
|
|
57
75
|
describe("createJsonSearchTool", () => {
|
|
@@ -72,7 +90,7 @@ describe("JSON Helper Tools", () => {
|
|
|
72
90
|
|
|
73
91
|
const call = mockAdapter.executeReadQuery.mock.calls[0][0] as string;
|
|
74
92
|
expect(call).toContain("JSON_SEARCH");
|
|
75
|
-
expect(call).toContain("SELECT id,
|
|
93
|
+
expect(call).toContain("SELECT id, JSON_SEARCH");
|
|
76
94
|
expect(call).not.toContain("SELECT *");
|
|
77
95
|
});
|
|
78
96
|
});
|
|
@@ -116,10 +134,10 @@ describe("JSON Helper Tools", () => {
|
|
|
116
134
|
id: 999,
|
|
117
135
|
},
|
|
118
136
|
mockContext,
|
|
119
|
-
)) as { success: boolean;
|
|
137
|
+
)) as { success: boolean; error: string };
|
|
120
138
|
|
|
121
139
|
expect(result.success).toBe(false);
|
|
122
|
-
expect(result.
|
|
140
|
+
expect(result.error).toContain("999");
|
|
123
141
|
});
|
|
124
142
|
});
|
|
125
143
|
|
|
@@ -165,6 +183,27 @@ describe("JSON Helper Tools", () => {
|
|
|
165
183
|
expect(sqlParam[0]).toBe("hello");
|
|
166
184
|
expect(result.valid).toBe(false);
|
|
167
185
|
});
|
|
186
|
+
|
|
187
|
+
it("should strip Query failed and Execute failed prefixes from errors", async () => {
|
|
188
|
+
mockAdapter.executeReadQuery.mockRejectedValue(
|
|
189
|
+
new Error(
|
|
190
|
+
'Query failed: Execute failed: Invalid JSON text in argument 1 to function cast_as_json: "Missing a name" at position 1.',
|
|
191
|
+
),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const tool = createJsonValidateTool(
|
|
195
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
196
|
+
);
|
|
197
|
+
const result = (await tool.handler({ value: "{bad" }, mockContext)) as {
|
|
198
|
+
valid: boolean;
|
|
199
|
+
error: string;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
expect(result.valid).toBe(false);
|
|
203
|
+
expect(result.error).not.toContain("Query failed");
|
|
204
|
+
expect(result.error).not.toContain("Execute failed");
|
|
205
|
+
expect(result.error).toContain("Invalid JSON text");
|
|
206
|
+
});
|
|
168
207
|
});
|
|
169
208
|
|
|
170
209
|
describe("P154 Graceful Error Handling", () => {
|
|
@@ -15,12 +15,19 @@ import {
|
|
|
15
15
|
JsonExtractSchemaBase,
|
|
16
16
|
JsonSetSchema,
|
|
17
17
|
JsonSetSchemaBase,
|
|
18
|
+
JsonInsertSchema,
|
|
19
|
+
JsonInsertSchemaBase,
|
|
20
|
+
JsonReplaceSchema,
|
|
21
|
+
JsonReplaceSchemaBase,
|
|
22
|
+
JsonRemoveSchema,
|
|
23
|
+
JsonRemoveSchemaBase,
|
|
18
24
|
JsonContainsSchema,
|
|
19
25
|
JsonContainsSchemaBase,
|
|
20
26
|
JsonKeysSchema,
|
|
21
27
|
JsonKeysSchemaBase,
|
|
28
|
+
JsonArrayAppendSchema,
|
|
29
|
+
JsonArrayAppendSchemaBase,
|
|
22
30
|
} from "../../types.js";
|
|
23
|
-
import { z } from "zod";
|
|
24
31
|
import {
|
|
25
32
|
validateIdentifier,
|
|
26
33
|
validateQualifiedIdentifier,
|
|
@@ -136,27 +143,20 @@ export function createJsonSetTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
136
143
|
}
|
|
137
144
|
|
|
138
145
|
export function createJsonInsertTool(adapter: MySQLAdapter): ToolDefinition {
|
|
139
|
-
const schema = z.object({
|
|
140
|
-
table: z.string(),
|
|
141
|
-
column: z.string(),
|
|
142
|
-
path: z.string(),
|
|
143
|
-
value: z.unknown(),
|
|
144
|
-
where: z.string(),
|
|
145
|
-
});
|
|
146
|
-
|
|
147
146
|
return {
|
|
148
147
|
name: "mysql_json_insert",
|
|
149
148
|
title: "MySQL JSON Insert",
|
|
150
149
|
description:
|
|
151
150
|
"Insert values into JSON columns only if the path does not exist.",
|
|
152
151
|
group: "json",
|
|
153
|
-
inputSchema:
|
|
152
|
+
inputSchema: JsonInsertSchemaBase,
|
|
154
153
|
requiredScopes: ["write"],
|
|
155
154
|
annotations: {
|
|
156
155
|
readOnlyHint: false,
|
|
157
156
|
},
|
|
158
157
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
159
|
-
const { table, column, path, value, where } =
|
|
158
|
+
const { table, column, path, value, where } =
|
|
159
|
+
JsonInsertSchema.parse(params);
|
|
160
160
|
|
|
161
161
|
// Validate inputs
|
|
162
162
|
validateQualifiedIdentifier(table, "table");
|
|
@@ -197,26 +197,19 @@ export function createJsonInsertTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
export function createJsonReplaceTool(adapter: MySQLAdapter): ToolDefinition {
|
|
200
|
-
const schema = z.object({
|
|
201
|
-
table: z.string(),
|
|
202
|
-
column: z.string(),
|
|
203
|
-
path: z.string(),
|
|
204
|
-
value: z.unknown(),
|
|
205
|
-
where: z.string(),
|
|
206
|
-
});
|
|
207
|
-
|
|
208
200
|
return {
|
|
209
201
|
name: "mysql_json_replace",
|
|
210
202
|
title: "MySQL JSON Replace",
|
|
211
203
|
description: "Replace values in JSON columns only if the path exists.",
|
|
212
204
|
group: "json",
|
|
213
|
-
inputSchema:
|
|
205
|
+
inputSchema: JsonReplaceSchemaBase,
|
|
214
206
|
requiredScopes: ["write"],
|
|
215
207
|
annotations: {
|
|
216
208
|
readOnlyHint: false,
|
|
217
209
|
},
|
|
218
210
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
219
|
-
const { table, column, path, value, where } =
|
|
211
|
+
const { table, column, path, value, where } =
|
|
212
|
+
JsonReplaceSchema.parse(params);
|
|
220
213
|
|
|
221
214
|
// Validate inputs
|
|
222
215
|
validateQualifiedIdentifier(table, "table");
|
|
@@ -242,25 +235,18 @@ export function createJsonReplaceTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
242
235
|
}
|
|
243
236
|
|
|
244
237
|
export function createJsonRemoveTool(adapter: MySQLAdapter): ToolDefinition {
|
|
245
|
-
const schema = z.object({
|
|
246
|
-
table: z.string(),
|
|
247
|
-
column: z.string(),
|
|
248
|
-
paths: z.array(z.string()),
|
|
249
|
-
where: z.string(),
|
|
250
|
-
});
|
|
251
|
-
|
|
252
238
|
return {
|
|
253
239
|
name: "mysql_json_remove",
|
|
254
240
|
title: "MySQL JSON Remove",
|
|
255
241
|
description: "Remove values from JSON columns at specified paths.",
|
|
256
242
|
group: "json",
|
|
257
|
-
inputSchema:
|
|
243
|
+
inputSchema: JsonRemoveSchemaBase,
|
|
258
244
|
requiredScopes: ["write"],
|
|
259
245
|
annotations: {
|
|
260
246
|
readOnlyHint: false,
|
|
261
247
|
},
|
|
262
248
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
263
|
-
const { table, column, paths, where } =
|
|
249
|
+
const { table, column, paths, where } = JsonRemoveSchema.parse(params);
|
|
264
250
|
|
|
265
251
|
// Validate inputs
|
|
266
252
|
validateQualifiedIdentifier(table, "table");
|
|
@@ -351,10 +337,10 @@ export function createJsonKeysTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
351
337
|
|
|
352
338
|
try {
|
|
353
339
|
const jsonPath = path ?? "$";
|
|
354
|
-
const sql = `SELECT JSON_KEYS(\`${column}\`, ?) as json_keys FROM ${escapeQualifiedTable(table)}`;
|
|
340
|
+
const sql = `SELECT JSON_KEYS(\`${column}\`, ?) as json_keys FROM ${escapeQualifiedTable(table)} HAVING json_keys IS NOT NULL`;
|
|
355
341
|
|
|
356
342
|
const result = await adapter.executeReadQuery(sql, [jsonPath]);
|
|
357
|
-
return { rows: result.rows };
|
|
343
|
+
return { rows: result.rows, count: result.rows?.length ?? 0 };
|
|
358
344
|
} catch (error) {
|
|
359
345
|
const msg = error instanceof Error ? error.message : String(error);
|
|
360
346
|
if (msg.includes("doesn't exist")) {
|
|
@@ -369,26 +355,19 @@ export function createJsonKeysTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
369
355
|
export function createJsonArrayAppendTool(
|
|
370
356
|
adapter: MySQLAdapter,
|
|
371
357
|
): ToolDefinition {
|
|
372
|
-
const schema = z.object({
|
|
373
|
-
table: z.string(),
|
|
374
|
-
column: z.string(),
|
|
375
|
-
path: z.string(),
|
|
376
|
-
value: z.unknown(),
|
|
377
|
-
where: z.string(),
|
|
378
|
-
});
|
|
379
|
-
|
|
380
358
|
return {
|
|
381
359
|
name: "mysql_json_array_append",
|
|
382
360
|
title: "MySQL JSON Array Append",
|
|
383
361
|
description: "Append a value to a JSON array at the specified path.",
|
|
384
362
|
group: "json",
|
|
385
|
-
inputSchema:
|
|
363
|
+
inputSchema: JsonArrayAppendSchemaBase,
|
|
386
364
|
requiredScopes: ["write"],
|
|
387
365
|
annotations: {
|
|
388
366
|
readOnlyHint: false,
|
|
389
367
|
},
|
|
390
368
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
391
|
-
const { table, column, path, value, where } =
|
|
369
|
+
const { table, column, path, value, where } =
|
|
370
|
+
JsonArrayAppendSchema.parse(params);
|
|
392
371
|
|
|
393
372
|
// Validate inputs
|
|
394
373
|
validateQualifiedIdentifier(table, "table");
|
|
@@ -11,16 +11,21 @@ import type {
|
|
|
11
11
|
RequestContext,
|
|
12
12
|
} from "../../../../types/index.js";
|
|
13
13
|
import { z } from "zod";
|
|
14
|
+
import {
|
|
15
|
+
JsonNormalizeSchema,
|
|
16
|
+
JsonNormalizeSchemaBase,
|
|
17
|
+
JsonStatsSchema,
|
|
18
|
+
JsonStatsSchemaBase,
|
|
19
|
+
JsonIndexSuggestSchema,
|
|
20
|
+
JsonIndexSuggestSchemaBase,
|
|
21
|
+
} from "../../types.js";
|
|
14
22
|
import {
|
|
15
23
|
validateQualifiedIdentifier,
|
|
16
24
|
escapeQualifiedTable,
|
|
17
25
|
validateIdentifier,
|
|
18
26
|
} from "../../../../utils/validators.js";
|
|
19
27
|
|
|
20
|
-
//
|
|
21
|
-
// Schemas
|
|
22
|
-
// =============================================================================
|
|
23
|
-
|
|
28
|
+
// Schemas for json_merge and json_diff (no table/column — no aliases needed)
|
|
24
29
|
const JsonMergeSchema = z.object({
|
|
25
30
|
json1: z.string().describe("First JSON document"),
|
|
26
31
|
json2: z.string().describe("Second JSON document"),
|
|
@@ -35,26 +40,6 @@ const JsonDiffSchema = z.object({
|
|
|
35
40
|
json2: z.string().describe("Second JSON document"),
|
|
36
41
|
});
|
|
37
42
|
|
|
38
|
-
const JsonNormalizeSchema = z.object({
|
|
39
|
-
table: z.string().describe("Table name"),
|
|
40
|
-
column: z.string().describe("JSON column name"),
|
|
41
|
-
where: z.string().optional().describe("WHERE clause"),
|
|
42
|
-
limit: z.number().default(100).describe("Maximum rows to process"),
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const JsonStatsSchema = z.object({
|
|
46
|
-
table: z.string().describe("Table name"),
|
|
47
|
-
column: z.string().describe("JSON column name"),
|
|
48
|
-
where: z.string().optional().describe("Optional WHERE clause"),
|
|
49
|
-
sampleSize: z.number().default(1000).describe("Sample size for statistics"),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const JsonIndexSuggestSchema = z.object({
|
|
53
|
-
table: z.string().describe("Table name"),
|
|
54
|
-
column: z.string().describe("JSON column name"),
|
|
55
|
-
sampleSize: z.number().default(100).describe("Sample size to analyze"),
|
|
56
|
-
});
|
|
57
|
-
|
|
58
43
|
// =============================================================================
|
|
59
44
|
// Tool Creation Functions
|
|
60
45
|
// =============================================================================
|
|
@@ -232,7 +217,7 @@ export function createJsonNormalizeTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
232
217
|
description:
|
|
233
218
|
"Normalize JSON column structure by extracting all unique keys across documents.",
|
|
234
219
|
group: "json",
|
|
235
|
-
inputSchema:
|
|
220
|
+
inputSchema: JsonNormalizeSchemaBase,
|
|
236
221
|
requiredScopes: ["read"],
|
|
237
222
|
annotations: {
|
|
238
223
|
readOnlyHint: true,
|
|
@@ -306,7 +291,7 @@ export function createJsonStatsTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
306
291
|
description:
|
|
307
292
|
"Analyze statistics for a JSON column including depth, size, and key frequency.",
|
|
308
293
|
group: "json",
|
|
309
|
-
inputSchema:
|
|
294
|
+
inputSchema: JsonStatsSchemaBase,
|
|
310
295
|
requiredScopes: ["read"],
|
|
311
296
|
annotations: {
|
|
312
297
|
readOnlyHint: true,
|
|
@@ -341,20 +326,20 @@ export function createJsonStatsTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
341
326
|
const row = result.rows?.[0];
|
|
342
327
|
|
|
343
328
|
return {
|
|
344
|
-
totalSampled: row?.["total_rows"],
|
|
345
|
-
nullCount: row?.["null_count"],
|
|
329
|
+
totalSampled: Number(row?.["total_rows"] ?? 0),
|
|
330
|
+
nullCount: Number(row?.["null_count"] ?? 0),
|
|
346
331
|
length: {
|
|
347
|
-
avg: row?.["avg_length"],
|
|
348
|
-
max: row?.["max_length"],
|
|
349
|
-
min: row?.["min_length"],
|
|
332
|
+
avg: Number(row?.["avg_length"] ?? 0),
|
|
333
|
+
max: Number(row?.["max_length"] ?? 0),
|
|
334
|
+
min: Number(row?.["min_length"] ?? 0),
|
|
350
335
|
},
|
|
351
336
|
depth: {
|
|
352
|
-
avg: row?.["avg_depth"],
|
|
353
|
-
max: row?.["max_depth"],
|
|
337
|
+
avg: Number(row?.["avg_depth"] ?? 0),
|
|
338
|
+
max: Number(row?.["max_depth"] ?? 0),
|
|
354
339
|
},
|
|
355
340
|
sizeBytes: {
|
|
356
|
-
avg: row?.["avg_size_bytes"],
|
|
357
|
-
max: row?.["max_size_bytes"],
|
|
341
|
+
avg: Number(row?.["avg_size_bytes"] ?? 0),
|
|
342
|
+
max: Number(row?.["max_size_bytes"] ?? 0),
|
|
358
343
|
},
|
|
359
344
|
sampleSize,
|
|
360
345
|
};
|
|
@@ -378,7 +363,7 @@ export function createJsonIndexSuggestTool(
|
|
|
378
363
|
description:
|
|
379
364
|
"Suggest functional indexes for frequently accessed JSON paths.",
|
|
380
365
|
group: "json",
|
|
381
|
-
inputSchema:
|
|
366
|
+
inputSchema: JsonIndexSuggestSchemaBase,
|
|
382
367
|
requiredScopes: ["read"],
|
|
383
368
|
annotations: {
|
|
384
369
|
readOnlyHint: true,
|
|
@@ -459,7 +444,7 @@ export function createJsonIndexSuggestTool(
|
|
|
459
444
|
path: `$.${key}`,
|
|
460
445
|
type: valueType ?? "UNKNOWN",
|
|
461
446
|
cardinality,
|
|
462
|
-
indexDdl: `ALTER TABLE
|
|
447
|
+
indexDdl: `ALTER TABLE ${escapeQualifiedTable(table)} ADD INDEX idx_${table.split(".").pop()}_${key} ((CAST(JSON_EXTRACT(\`${column}\`, '$.${key}') AS ${dataType})));`,
|
|
463
448
|
});
|
|
464
449
|
}
|
|
465
450
|
}
|
|
@@ -14,8 +14,11 @@ import {
|
|
|
14
14
|
JsonSearchSchema,
|
|
15
15
|
JsonSearchSchemaBase,
|
|
16
16
|
JsonValidateSchema,
|
|
17
|
+
JsonGetSchema,
|
|
18
|
+
JsonGetSchemaBase,
|
|
19
|
+
JsonUpdateSchema,
|
|
20
|
+
JsonUpdateSchemaBase,
|
|
17
21
|
} from "../../types.js";
|
|
18
|
-
import { z } from "zod";
|
|
19
22
|
import {
|
|
20
23
|
validateQualifiedIdentifier,
|
|
21
24
|
escapeQualifiedTable,
|
|
@@ -26,27 +29,19 @@ import {
|
|
|
26
29
|
* Export all JSON helper tool creation functions
|
|
27
30
|
*/
|
|
28
31
|
export function createJsonGetTool(adapter: MySQLAdapter): ToolDefinition {
|
|
29
|
-
const schema = z.object({
|
|
30
|
-
table: z.string(),
|
|
31
|
-
column: z.string(),
|
|
32
|
-
path: z.string(),
|
|
33
|
-
id: z.union([z.string(), z.number()]),
|
|
34
|
-
idColumn: z.string().default("id"),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
32
|
return {
|
|
38
33
|
name: "mysql_json_get",
|
|
39
34
|
title: "MySQL JSON Get",
|
|
40
35
|
description: "Simple JSON value extraction by row ID.",
|
|
41
36
|
group: "json",
|
|
42
|
-
inputSchema:
|
|
37
|
+
inputSchema: JsonGetSchemaBase,
|
|
43
38
|
requiredScopes: ["read"],
|
|
44
39
|
annotations: {
|
|
45
40
|
readOnlyHint: true,
|
|
46
41
|
idempotentHint: true,
|
|
47
42
|
},
|
|
48
43
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
49
|
-
const { table, column, path, id, idColumn } =
|
|
44
|
+
const { table, column, path, id, idColumn } = JsonGetSchema.parse(params);
|
|
50
45
|
|
|
51
46
|
validateQualifiedIdentifier(table, "table");
|
|
52
47
|
validateIdentifier(column, "column");
|
|
@@ -56,6 +51,11 @@ export function createJsonGetTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
56
51
|
const sql = `SELECT JSON_EXTRACT(\`${column}\`, ?) as value FROM ${escapeQualifiedTable(table)} WHERE \`${idColumn}\` = ?`;
|
|
57
52
|
const result = await adapter.executeReadQuery(sql, [path, id]);
|
|
58
53
|
|
|
54
|
+
// No rows = row ID doesn't exist (distinct from null JSON path)
|
|
55
|
+
if (!result.rows || result.rows.length === 0) {
|
|
56
|
+
return { value: null, rowFound: false };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
59
|
const rawValue = result.rows?.[0]?.["value"];
|
|
60
60
|
// Parse JSON value for consistency with mysql_json_extract
|
|
61
61
|
// Return null for missing paths, parse objects/arrays, return primitives as-is
|
|
@@ -88,27 +88,19 @@ export function createJsonGetTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export function createJsonUpdateTool(adapter: MySQLAdapter): ToolDefinition {
|
|
91
|
-
const schema = z.object({
|
|
92
|
-
table: z.string(),
|
|
93
|
-
column: z.string(),
|
|
94
|
-
path: z.string(),
|
|
95
|
-
value: z.unknown(),
|
|
96
|
-
id: z.union([z.string(), z.number()]),
|
|
97
|
-
idColumn: z.string().default("id"),
|
|
98
|
-
});
|
|
99
|
-
|
|
100
91
|
return {
|
|
101
92
|
name: "mysql_json_update",
|
|
102
93
|
title: "MySQL JSON Update",
|
|
103
94
|
description: "Simple JSON value update by row ID.",
|
|
104
95
|
group: "json",
|
|
105
|
-
inputSchema:
|
|
96
|
+
inputSchema: JsonUpdateSchemaBase,
|
|
106
97
|
requiredScopes: ["write"],
|
|
107
98
|
annotations: {
|
|
108
99
|
readOnlyHint: false,
|
|
109
100
|
},
|
|
110
101
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
111
|
-
const { table, column, path, value, id, idColumn } =
|
|
102
|
+
const { table, column, path, value, id, idColumn } =
|
|
103
|
+
JsonUpdateSchema.parse(params);
|
|
112
104
|
|
|
113
105
|
validateQualifiedIdentifier(table, "table");
|
|
114
106
|
validateIdentifier(column, "column");
|
|
@@ -140,7 +132,7 @@ export function createJsonUpdateTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
140
132
|
if (result.rowsAffected === 0) {
|
|
141
133
|
return {
|
|
142
134
|
success: false,
|
|
143
|
-
|
|
135
|
+
error: `No row found with ${idColumn} = ${id}`,
|
|
144
136
|
};
|
|
145
137
|
}
|
|
146
138
|
return { success: true };
|
|
@@ -176,7 +168,7 @@ export function createJsonSearchTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
176
168
|
validateIdentifier(column, "column");
|
|
177
169
|
|
|
178
170
|
try {
|
|
179
|
-
const sql = `SELECT id,
|
|
171
|
+
const sql = `SELECT id, JSON_SEARCH(\`${column}\`, ?, ?) as match_path FROM ${escapeQualifiedTable(table)} WHERE JSON_SEARCH(\`${column}\`, ?, ?) IS NOT NULL`;
|
|
180
172
|
|
|
181
173
|
const result = await adapter.executeReadQuery(sql, [
|
|
182
174
|
mode,
|
|
@@ -220,8 +212,12 @@ export function createJsonValidateTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
220
212
|
} catch (error) {
|
|
221
213
|
// MySQL may throw an error for severely malformed input
|
|
222
214
|
// Return a structured error response instead of propagating
|
|
223
|
-
|
|
215
|
+
let message =
|
|
224
216
|
error instanceof Error ? error.message : "Unknown validation error";
|
|
217
|
+
message = message.replace(
|
|
218
|
+
/^(Query failed:\s*|Execute failed:\s*)+/i,
|
|
219
|
+
"",
|
|
220
|
+
);
|
|
225
221
|
return { valid: false, error: message };
|
|
226
222
|
}
|
|
227
223
|
},
|
|
@@ -146,6 +146,7 @@ function createAddPartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
146
146
|
|
|
147
147
|
try {
|
|
148
148
|
await adapter.executeQuery(sql);
|
|
149
|
+
adapter.clearSchemaCache();
|
|
149
150
|
return { success: true, table, partitionName };
|
|
150
151
|
} catch (error) {
|
|
151
152
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -210,6 +211,7 @@ function createDropPartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
210
211
|
`ALTER TABLE \`${table}\` DROP PARTITION \`${partitionName}\``,
|
|
211
212
|
);
|
|
212
213
|
|
|
214
|
+
adapter.clearSchemaCache();
|
|
213
215
|
return {
|
|
214
216
|
success: true,
|
|
215
217
|
table,
|
|
@@ -296,6 +298,7 @@ function createReorganizePartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
296
298
|
|
|
297
299
|
try {
|
|
298
300
|
await adapter.executeQuery(sql);
|
|
301
|
+
adapter.clearSchemaCache();
|
|
299
302
|
return {
|
|
300
303
|
success: true,
|
|
301
304
|
table,
|