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