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