@neverinfamous/mysql-mcp 2.2.0 → 2.3.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/.github/workflows/docker-publish.yml +1 -2
- package/CHANGELOG.md +85 -0
- package/CODE_MODE.md +245 -0
- package/DOCKER_README.md +59 -36
- package/README.md +65 -42
- package/VERSION +1 -1
- package/dist/adapters/mysql/MySQLAdapter.d.ts +4 -0
- package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.js +9 -0
- package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
- package/dist/adapters/mysql/prompts/index.d.ts +8 -1
- package/dist/adapters/mysql/prompts/index.d.ts.map +1 -1
- package/dist/adapters/mysql/prompts/index.js +8 -1
- package/dist/adapters/mysql/prompts/index.js.map +1 -1
- package/dist/adapters/mysql/prompts/routerSetup.d.ts.map +1 -1
- package/dist/adapters/mysql/prompts/routerSetup.js +5 -0
- package/dist/adapters/mysql/prompts/routerSetup.js.map +1 -1
- package/dist/adapters/mysql/resources/capabilities.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/capabilities.js +6 -5
- package/dist/adapters/mysql/resources/capabilities.js.map +1 -1
- package/dist/adapters/mysql/resources/index.d.ts +9 -1
- package/dist/adapters/mysql/resources/index.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/index.js +9 -1
- package/dist/adapters/mysql/resources/index.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.js +3 -3
- 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 +5 -5
- package/dist/adapters/mysql/tools/admin/maintenance.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 +26 -5
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.d.ts +38 -0
- package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -0
- package/dist/adapters/mysql/tools/codemode/index.js +203 -0
- package/dist/adapters/mysql/tools/codemode/index.js.map +1 -0
- package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/core.js +32 -20
- package/dist/adapters/mysql/tools/core.js.map +1 -1
- package/dist/adapters/mysql/tools/events.js +18 -6
- 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 +5 -5
- package/dist/adapters/mysql/tools/json/core.js.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.js +9 -3
- package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.js +38 -6
- 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 +67 -20
- 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 +36 -6
- package/dist/adapters/mysql/tools/performance/optimization.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 +9 -4
- package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/common.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/common.js +28 -2
- package/dist/adapters/mysql/tools/shell/common.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.js +54 -4
- package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.js +10 -2
- package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.js +18 -0
- package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.js +5 -0
- 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 +6 -4
- package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
- package/dist/adapters/mysql/tools/text/processing.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/text/processing.js +10 -45
- package/dist/adapters/mysql/tools/text/processing.js.map +1 -1
- package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/transactions.js +8 -8
- package/dist/adapters/mysql/tools/transactions.js.map +1 -1
- package/dist/adapters/mysql/types.d.ts +968 -78
- package/dist/adapters/mysql/types.d.ts.map +1 -1
- package/dist/adapters/mysql/types.js +1084 -78
- package/dist/adapters/mysql/types.js.map +1 -1
- package/dist/auth/scopes.d.ts.map +1 -1
- package/dist/auth/scopes.js +1 -0
- package/dist/auth/scopes.js.map +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +12 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/codemode/api.d.ts +69 -0
- package/dist/codemode/api.d.ts.map +1 -0
- package/dist/codemode/api.js +1035 -0
- package/dist/codemode/api.js.map +1 -0
- package/dist/codemode/index.d.ts +13 -0
- package/dist/codemode/index.d.ts.map +1 -0
- package/dist/codemode/index.js +17 -0
- package/dist/codemode/index.js.map +1 -0
- package/dist/codemode/sandbox-factory.d.ts +72 -0
- package/dist/codemode/sandbox-factory.d.ts.map +1 -0
- package/dist/codemode/sandbox-factory.js +88 -0
- package/dist/codemode/sandbox-factory.js.map +1 -0
- package/dist/codemode/sandbox.d.ts +96 -0
- package/dist/codemode/sandbox.d.ts.map +1 -0
- package/dist/codemode/sandbox.js +345 -0
- package/dist/codemode/sandbox.js.map +1 -0
- package/dist/codemode/security.d.ts +44 -0
- package/dist/codemode/security.d.ts.map +1 -0
- package/dist/codemode/security.js +149 -0
- package/dist/codemode/security.js.map +1 -0
- package/dist/codemode/types.d.ts +137 -0
- package/dist/codemode/types.d.ts.map +1 -0
- package/dist/codemode/types.js +46 -0
- package/dist/codemode/types.js.map +1 -0
- package/dist/codemode/worker-sandbox.d.ts +82 -0
- package/dist/codemode/worker-sandbox.d.ts.map +1 -0
- package/dist/codemode/worker-sandbox.js +244 -0
- package/dist/codemode/worker-sandbox.js.map +1 -0
- package/dist/codemode/worker-script.d.ts +8 -0
- package/dist/codemode/worker-script.d.ts.map +1 -0
- package/dist/codemode/worker-script.js +113 -0
- package/dist/codemode/worker-script.js.map +1 -0
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +33 -9
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +11 -11
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +37 -19
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +12 -0
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/server/McpServer.js +1 -1
- package/dist/server/McpServer.js.map +1 -1
- package/dist/types/modules/server.d.ts +2 -0
- package/dist/types/modules/server.d.ts.map +1 -1
- package/dist/types/modules/tools.d.ts +1 -1
- package/dist/types/modules/tools.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +12 -7
- package/releases/v2.2.0-release-notes.md +18 -18
- package/releases/v2.3.0-release-notes.md +191 -0
- package/src/__tests__/perf.test.ts +12 -12
- package/src/adapters/mysql/MySQLAdapter.ts +10 -0
- package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
- package/src/adapters/mysql/prompts/index.ts +8 -1
- package/src/adapters/mysql/prompts/routerSetup.ts +5 -0
- package/src/adapters/mysql/resources/__tests__/capabilities.test.ts +50 -1
- package/src/adapters/mysql/resources/capabilities.ts +6 -4
- package/src/adapters/mysql/resources/index.ts +9 -1
- package/src/adapters/mysql/tools/__tests__/core.test.ts +68 -0
- package/src/adapters/mysql/tools/__tests__/events.test.ts +56 -2
- package/src/adapters/mysql/tools/__tests__/json_core.test.ts +1 -1
- package/src/adapters/mysql/tools/__tests__/json_helpers.test.ts +46 -4
- package/src/adapters/mysql/tools/__tests__/replication.test.ts +144 -42
- package/src/adapters/mysql/tools/__tests__/security.test.ts +39 -0
- package/src/adapters/mysql/tools/__tests__/spatial.test.ts +39 -7
- package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +35 -3
- package/src/adapters/mysql/tools/__tests__/transactions.test.ts +3 -5
- package/src/adapters/mysql/tools/admin/backup.ts +8 -3
- package/src/adapters/mysql/tools/admin/maintenance.ts +8 -4
- package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +35 -0
- package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +26 -5
- package/src/adapters/mysql/tools/codemode/index.ts +249 -0
- package/src/adapters/mysql/tools/core.ts +44 -27
- package/src/adapters/mysql/tools/events.ts +23 -7
- package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +59 -14
- package/src/adapters/mysql/tools/json/core.ts +8 -4
- package/src/adapters/mysql/tools/json/helpers.ts +13 -3
- package/src/adapters/mysql/tools/partitioning.ts +53 -6
- package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +227 -4
- package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +35 -0
- package/src/adapters/mysql/tools/performance/analysis.ts +75 -21
- package/src/adapters/mysql/tools/performance/optimization.ts +44 -6
- package/src/adapters/mysql/tools/security/data-protection.ts +10 -4
- package/src/adapters/mysql/tools/shell/__tests__/common.test.ts +46 -0
- package/src/adapters/mysql/tools/shell/__tests__/restore.test.ts +28 -1
- package/src/adapters/mysql/tools/shell/common.ts +34 -2
- package/src/adapters/mysql/tools/shell/restore.ts +70 -7
- package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +29 -0
- package/src/adapters/mysql/tools/spatial/operations.ts +13 -2
- package/src/adapters/mysql/tools/spatial/setup.ts +23 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +21 -0
- package/src/adapters/mysql/tools/sysschema/resources.ts +5 -0
- package/src/adapters/mysql/tools/text/fulltext.ts +13 -5
- package/src/adapters/mysql/tools/text/processing.ts +20 -49
- package/src/adapters/mysql/tools/transactions.ts +11 -7
- package/src/adapters/mysql/types.ts +1241 -87
- package/src/auth/scopes.ts +1 -0
- package/src/cli/args.ts +14 -0
- package/src/codemode/api.ts +1224 -0
- package/src/codemode/index.ts +51 -0
- package/src/codemode/sandbox-factory.ts +146 -0
- package/src/codemode/sandbox.ts +450 -0
- package/src/codemode/security.ts +188 -0
- package/src/codemode/types.ts +194 -0
- package/src/codemode/worker-sandbox.ts +326 -0
- package/src/codemode/worker-script.ts +144 -0
- package/src/constants/ServerInstructions.ts +33 -9
- package/src/filtering/ToolConstants.ts +37 -19
- package/src/filtering/ToolFilter.ts +15 -0
- package/src/filtering/__tests__/ToolFilter.test.ts +65 -38
- package/src/server/McpServer.ts +1 -1
- package/src/types/modules/server.ts +3 -0
- package/src/types/modules/tools.ts +2 -1
- package/src/utils/logger.ts +2 -1
|
@@ -9,12 +9,19 @@ import type { MySQLAdapter } from "../MySQLAdapter.js";
|
|
|
9
9
|
import type { ToolDefinition, RequestContext } from "../../../types/index.js";
|
|
10
10
|
import {
|
|
11
11
|
ReadQuerySchema,
|
|
12
|
+
ReadQuerySchemaBase,
|
|
12
13
|
WriteQuerySchema,
|
|
14
|
+
WriteQuerySchemaBase,
|
|
13
15
|
CreateTableSchema,
|
|
16
|
+
CreateTableSchemaBase,
|
|
14
17
|
DescribeTableSchema,
|
|
18
|
+
DescribeTableSchemaBase,
|
|
15
19
|
DropTableSchema,
|
|
20
|
+
DropTableSchemaBase,
|
|
16
21
|
CreateIndexSchema,
|
|
22
|
+
CreateIndexSchemaBase,
|
|
17
23
|
GetIndexesSchema,
|
|
24
|
+
GetIndexesSchemaBase,
|
|
18
25
|
ListTablesSchema,
|
|
19
26
|
} from "../types.js";
|
|
20
27
|
|
|
@@ -69,7 +76,7 @@ function createReadQueryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
69
76
|
description:
|
|
70
77
|
"Execute a read-only SQL query (SELECT). Uses prepared statements for safety.",
|
|
71
78
|
group: "core",
|
|
72
|
-
inputSchema:
|
|
79
|
+
inputSchema: ReadQuerySchemaBase,
|
|
73
80
|
requiredScopes: ["read"],
|
|
74
81
|
annotations: {
|
|
75
82
|
readOnlyHint: true,
|
|
@@ -81,16 +88,21 @@ function createReadQueryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
81
88
|
params: queryParams,
|
|
82
89
|
transactionId,
|
|
83
90
|
} = ReadQuerySchema.parse(params);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
try {
|
|
92
|
+
const result = await adapter.executeReadQuery(
|
|
93
|
+
query,
|
|
94
|
+
queryParams,
|
|
95
|
+
transactionId,
|
|
96
|
+
);
|
|
97
|
+
return {
|
|
98
|
+
rows: result.rows,
|
|
99
|
+
rowCount: result.rows?.length ?? 0,
|
|
100
|
+
executionTimeMs: result.executionTimeMs,
|
|
101
|
+
};
|
|
102
|
+
} catch (err: unknown) {
|
|
103
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
104
|
+
return { success: false, error: message.replace(/^.*?:\s*/, "") };
|
|
105
|
+
}
|
|
94
106
|
},
|
|
95
107
|
};
|
|
96
108
|
}
|
|
@@ -105,7 +117,7 @@ function createWriteQueryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
105
117
|
description:
|
|
106
118
|
"Execute a write SQL query (INSERT, UPDATE, DELETE). Uses prepared statements for safety.",
|
|
107
119
|
group: "core",
|
|
108
|
-
inputSchema:
|
|
120
|
+
inputSchema: WriteQuerySchemaBase,
|
|
109
121
|
requiredScopes: ["write"],
|
|
110
122
|
annotations: {
|
|
111
123
|
readOnlyHint: false,
|
|
@@ -116,16 +128,21 @@ function createWriteQueryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
116
128
|
params: queryParams,
|
|
117
129
|
transactionId,
|
|
118
130
|
} = WriteQuerySchema.parse(params);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
try {
|
|
132
|
+
const result = await adapter.executeWriteQuery(
|
|
133
|
+
query,
|
|
134
|
+
queryParams,
|
|
135
|
+
transactionId,
|
|
136
|
+
);
|
|
137
|
+
return {
|
|
138
|
+
rowsAffected: result.rowsAffected,
|
|
139
|
+
lastInsertId: result.lastInsertId?.toString(),
|
|
140
|
+
executionTimeMs: result.executionTimeMs,
|
|
141
|
+
};
|
|
142
|
+
} catch (err: unknown) {
|
|
143
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
144
|
+
return { success: false, error: message.replace(/^.*?:\s*/, "") };
|
|
145
|
+
}
|
|
129
146
|
},
|
|
130
147
|
};
|
|
131
148
|
}
|
|
@@ -172,7 +189,7 @@ function createDescribeTableTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
172
189
|
description:
|
|
173
190
|
"Get detailed information about a table's structure including columns, types, and constraints.",
|
|
174
191
|
group: "core",
|
|
175
|
-
inputSchema:
|
|
192
|
+
inputSchema: DescribeTableSchemaBase,
|
|
176
193
|
requiredScopes: ["read"],
|
|
177
194
|
annotations: {
|
|
178
195
|
readOnlyHint: true,
|
|
@@ -204,7 +221,7 @@ function createCreateTableTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
204
221
|
description:
|
|
205
222
|
"Create a new table with specified columns, engine, and charset.",
|
|
206
223
|
group: "core",
|
|
207
|
-
inputSchema:
|
|
224
|
+
inputSchema: CreateTableSchemaBase,
|
|
208
225
|
requiredScopes: ["write"],
|
|
209
226
|
annotations: {
|
|
210
227
|
readOnlyHint: false,
|
|
@@ -313,7 +330,7 @@ function createDropTableTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
313
330
|
title: "MySQL Drop Table",
|
|
314
331
|
description: "Drop (delete) a table from the database.",
|
|
315
332
|
group: "core",
|
|
316
|
-
inputSchema:
|
|
333
|
+
inputSchema: DropTableSchemaBase,
|
|
317
334
|
requiredScopes: ["admin"],
|
|
318
335
|
annotations: {
|
|
319
336
|
readOnlyHint: false,
|
|
@@ -376,7 +393,7 @@ function createGetIndexesTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
376
393
|
description:
|
|
377
394
|
"Get all indexes for a table including type, columns, and cardinality.",
|
|
378
395
|
group: "core",
|
|
379
|
-
inputSchema:
|
|
396
|
+
inputSchema: GetIndexesSchemaBase,
|
|
380
397
|
requiredScopes: ["read"],
|
|
381
398
|
annotations: {
|
|
382
399
|
readOnlyHint: true,
|
|
@@ -410,7 +427,7 @@ function createCreateIndexTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
410
427
|
description:
|
|
411
428
|
"Create an index on a table. Supports BTREE, HASH, FULLTEXT, and SPATIAL index types.",
|
|
412
429
|
group: "core",
|
|
413
|
-
inputSchema:
|
|
430
|
+
inputSchema: CreateIndexSchemaBase,
|
|
414
431
|
requiredScopes: ["write"],
|
|
415
432
|
annotations: {
|
|
416
433
|
readOnlyHint: false,
|
|
@@ -152,6 +152,22 @@ function createEventCreateTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
152
152
|
throw new Error("Invalid event name");
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// Pre-check event existence for informative messaging
|
|
156
|
+
if (ifNotExists) {
|
|
157
|
+
const existsCheck = await adapter.executeQuery(
|
|
158
|
+
"SELECT EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA = DATABASE() AND EVENT_NAME = ?",
|
|
159
|
+
[name],
|
|
160
|
+
);
|
|
161
|
+
if (existsCheck.rows && existsCheck.rows.length > 0) {
|
|
162
|
+
return {
|
|
163
|
+
success: true,
|
|
164
|
+
skipped: true,
|
|
165
|
+
reason: "Event already exists",
|
|
166
|
+
eventName: name,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
155
171
|
const ifNotExistsClause = ifNotExists ? "IF NOT EXISTS " : "";
|
|
156
172
|
let sql = `CREATE EVENT ${ifNotExistsClause}\`${name}\`\nON SCHEDULE `;
|
|
157
173
|
|
|
@@ -273,6 +289,13 @@ function createEventAlterTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
273
289
|
clauses.push(`ON COMPLETION ${onCompletion}`);
|
|
274
290
|
}
|
|
275
291
|
|
|
292
|
+
if (newName) {
|
|
293
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(newName)) {
|
|
294
|
+
throw new Error("Invalid new event name");
|
|
295
|
+
}
|
|
296
|
+
clauses.push(`RENAME TO \`${newName}\``);
|
|
297
|
+
}
|
|
298
|
+
|
|
276
299
|
if (enabled !== undefined) {
|
|
277
300
|
clauses.push(enabled ? "ENABLE" : "DISABLE");
|
|
278
301
|
}
|
|
@@ -285,13 +308,6 @@ function createEventAlterTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
285
308
|
clauses.push(`DO ${body}`);
|
|
286
309
|
}
|
|
287
310
|
|
|
288
|
-
if (newName) {
|
|
289
|
-
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(newName)) {
|
|
290
|
-
throw new Error("Invalid new event name");
|
|
291
|
-
}
|
|
292
|
-
clauses.push(`RENAME TO \`${newName}\``);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
311
|
if (clauses.length === 0) {
|
|
296
312
|
throw new Error("No modifications specified");
|
|
297
313
|
}
|
|
@@ -54,6 +54,29 @@ describe("JSON Helper Tools", () => {
|
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
describe("createJsonSearchTool", () => {
|
|
58
|
+
it("should search JSON by value", async () => {
|
|
59
|
+
mockAdapter.executeReadQuery.mockResolvedValue(
|
|
60
|
+
createMockQueryResult([{ id: 1, match_path: "$[0]" }]),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const tool = createJsonSearchTool(mockAdapter as unknown as MySQLAdapter);
|
|
64
|
+
await tool.handler(
|
|
65
|
+
{
|
|
66
|
+
table: "data",
|
|
67
|
+
column: "json_col",
|
|
68
|
+
searchValue: "test",
|
|
69
|
+
},
|
|
70
|
+
mockContext,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const call = mockAdapter.executeReadQuery.mock.calls[0][0] as string;
|
|
74
|
+
expect(call).toContain("JSON_SEARCH");
|
|
75
|
+
expect(call).toContain("SELECT id, `json_col`");
|
|
76
|
+
expect(call).not.toContain("SELECT *");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
57
80
|
describe("createJsonUpdateTool", () => {
|
|
58
81
|
it("should update JSON value by ID", async () => {
|
|
59
82
|
mockAdapter.executeWriteQuery.mockResolvedValue({
|
|
@@ -76,28 +99,27 @@ describe("JSON Helper Tools", () => {
|
|
|
76
99
|
expect(mockAdapter.executeWriteQuery).toHaveBeenCalled();
|
|
77
100
|
expect(result.success).toBe(true);
|
|
78
101
|
});
|
|
79
|
-
});
|
|
80
102
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
103
|
+
it("should return reason when no row matches the ID", async () => {
|
|
104
|
+
mockAdapter.executeWriteQuery.mockResolvedValue({
|
|
105
|
+
rowsAffected: 0,
|
|
106
|
+
insertId: 0,
|
|
107
|
+
});
|
|
86
108
|
|
|
87
|
-
const tool =
|
|
88
|
-
await tool.handler(
|
|
109
|
+
const tool = createJsonUpdateTool(mockAdapter as unknown as MySQLAdapter);
|
|
110
|
+
const result = (await tool.handler(
|
|
89
111
|
{
|
|
90
112
|
table: "data",
|
|
91
113
|
column: "json_col",
|
|
92
|
-
|
|
114
|
+
path: "$.a",
|
|
115
|
+
value: 2,
|
|
116
|
+
id: 999,
|
|
93
117
|
},
|
|
94
118
|
mockContext,
|
|
95
|
-
);
|
|
119
|
+
)) as { success: boolean; reason: string };
|
|
96
120
|
|
|
97
|
-
|
|
98
|
-
expect(
|
|
99
|
-
expect(call).toContain("SELECT id, `json_col`");
|
|
100
|
-
expect(call).not.toContain("SELECT *");
|
|
121
|
+
expect(result.success).toBe(false);
|
|
122
|
+
expect(result.reason).toContain("999");
|
|
101
123
|
});
|
|
102
124
|
});
|
|
103
125
|
|
|
@@ -120,6 +142,29 @@ describe("JSON Helper Tools", () => {
|
|
|
120
142
|
expect(mockAdapter.executeReadQuery).toHaveBeenCalled();
|
|
121
143
|
expect(result.valid).toBe(true);
|
|
122
144
|
});
|
|
145
|
+
|
|
146
|
+
it("should pass bare strings directly without auto-conversion", async () => {
|
|
147
|
+
mockAdapter.executeReadQuery.mockResolvedValue(
|
|
148
|
+
createMockQueryResult([{ is_valid: 0 }]),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const tool = createJsonValidateTool(
|
|
152
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
153
|
+
);
|
|
154
|
+
const result = (await tool.handler(
|
|
155
|
+
{
|
|
156
|
+
value: "hello",
|
|
157
|
+
},
|
|
158
|
+
mockContext,
|
|
159
|
+
)) as { valid: boolean };
|
|
160
|
+
|
|
161
|
+
expect(mockAdapter.executeReadQuery).toHaveBeenCalled();
|
|
162
|
+
// The bare string "hello" should be passed directly, not wrapped
|
|
163
|
+
const sqlParam = mockAdapter.executeReadQuery.mock
|
|
164
|
+
.calls[0][1] as string[];
|
|
165
|
+
expect(sqlParam[0]).toBe("hello");
|
|
166
|
+
expect(result.valid).toBe(false);
|
|
167
|
+
});
|
|
123
168
|
});
|
|
124
169
|
|
|
125
170
|
describe("P154 Graceful Error Handling", () => {
|
|
@@ -12,9 +12,13 @@ import type {
|
|
|
12
12
|
} from "../../../../types/index.js";
|
|
13
13
|
import {
|
|
14
14
|
JsonExtractSchema,
|
|
15
|
+
JsonExtractSchemaBase,
|
|
15
16
|
JsonSetSchema,
|
|
17
|
+
JsonSetSchemaBase,
|
|
16
18
|
JsonContainsSchema,
|
|
19
|
+
JsonContainsSchemaBase,
|
|
17
20
|
JsonKeysSchema,
|
|
21
|
+
JsonKeysSchemaBase,
|
|
18
22
|
} from "../../types.js";
|
|
19
23
|
import { z } from "zod";
|
|
20
24
|
import {
|
|
@@ -57,7 +61,7 @@ export function createJsonExtractTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
57
61
|
description:
|
|
58
62
|
"Extract values from JSON columns using JSON path expressions.",
|
|
59
63
|
group: "json",
|
|
60
|
-
inputSchema:
|
|
64
|
+
inputSchema: JsonExtractSchemaBase,
|
|
61
65
|
requiredScopes: ["read"],
|
|
62
66
|
annotations: {
|
|
63
67
|
readOnlyHint: true,
|
|
@@ -100,7 +104,7 @@ export function createJsonSetTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
100
104
|
title: "MySQL JSON Set",
|
|
101
105
|
description: "Set or update values in JSON columns at specified paths.",
|
|
102
106
|
group: "json",
|
|
103
|
-
inputSchema:
|
|
107
|
+
inputSchema: JsonSetSchemaBase,
|
|
104
108
|
requiredScopes: ["write"],
|
|
105
109
|
annotations: {
|
|
106
110
|
readOnlyHint: false,
|
|
@@ -286,7 +290,7 @@ export function createJsonContainsTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
286
290
|
title: "MySQL JSON Contains",
|
|
287
291
|
description: "Find rows where JSON column contains a specified value.",
|
|
288
292
|
group: "json",
|
|
289
|
-
inputSchema:
|
|
293
|
+
inputSchema: JsonContainsSchemaBase,
|
|
290
294
|
requiredScopes: ["read"],
|
|
291
295
|
annotations: {
|
|
292
296
|
readOnlyHint: true,
|
|
@@ -332,7 +336,7 @@ export function createJsonKeysTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
332
336
|
title: "MySQL JSON Keys",
|
|
333
337
|
description: "Get the keys of a JSON object at the specified path.",
|
|
334
338
|
group: "json",
|
|
335
|
-
inputSchema:
|
|
339
|
+
inputSchema: JsonKeysSchemaBase,
|
|
336
340
|
requiredScopes: ["read"],
|
|
337
341
|
annotations: {
|
|
338
342
|
readOnlyHint: true,
|
|
@@ -10,7 +10,11 @@ import type {
|
|
|
10
10
|
ToolDefinition,
|
|
11
11
|
RequestContext,
|
|
12
12
|
} from "../../../../types/index.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
JsonSearchSchema,
|
|
15
|
+
JsonSearchSchemaBase,
|
|
16
|
+
JsonValidateSchema,
|
|
17
|
+
} from "../../types.js";
|
|
14
18
|
import { z } from "zod";
|
|
15
19
|
import {
|
|
16
20
|
validateQualifiedIdentifier,
|
|
@@ -133,7 +137,13 @@ export function createJsonUpdateTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
133
137
|
jsonValue,
|
|
134
138
|
id,
|
|
135
139
|
]);
|
|
136
|
-
|
|
140
|
+
if (result.rowsAffected === 0) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
reason: `No row found with ${idColumn} = ${id}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return { success: true };
|
|
137
147
|
} catch (error) {
|
|
138
148
|
const msg = error instanceof Error ? error.message : String(error);
|
|
139
149
|
if (msg.includes("doesn't exist")) {
|
|
@@ -152,7 +162,7 @@ export function createJsonSearchTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
152
162
|
description:
|
|
153
163
|
"Search for a string value in JSON columns and return matching paths.",
|
|
154
164
|
group: "json",
|
|
155
|
-
inputSchema:
|
|
165
|
+
inputSchema: JsonSearchSchemaBase,
|
|
156
166
|
requiredScopes: ["read"],
|
|
157
167
|
annotations: {
|
|
158
168
|
readOnlyHint: true,
|
|
@@ -5,13 +5,18 @@
|
|
|
5
5
|
* 4 tools: partition_info, add_partition, drop_partition, reorganize_partition.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { ZodError } from "zod";
|
|
8
9
|
import type { MySQLAdapter } from "../MySQLAdapter.js";
|
|
9
10
|
import type { ToolDefinition, RequestContext } from "../../../types/index.js";
|
|
10
11
|
import {
|
|
11
12
|
PartitionInfoSchema,
|
|
13
|
+
PartitionInfoSchemaBase,
|
|
12
14
|
AddPartitionSchema,
|
|
15
|
+
AddPartitionSchemaBase,
|
|
13
16
|
DropPartitionSchema,
|
|
17
|
+
DropPartitionSchemaBase,
|
|
14
18
|
ReorganizePartitionSchema,
|
|
19
|
+
ReorganizePartitionSchemaBase,
|
|
15
20
|
} from "../types.js";
|
|
16
21
|
|
|
17
22
|
/**
|
|
@@ -32,7 +37,7 @@ function createPartitionInfoTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
32
37
|
title: "MySQL Partition Info",
|
|
33
38
|
description: "Get partition information for a table.",
|
|
34
39
|
group: "partitioning",
|
|
35
|
-
inputSchema:
|
|
40
|
+
inputSchema: PartitionInfoSchemaBase,
|
|
36
41
|
requiredScopes: ["read"],
|
|
37
42
|
annotations: {
|
|
38
43
|
readOnlyHint: true,
|
|
@@ -99,7 +104,7 @@ function createAddPartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
99
104
|
title: "MySQL Add Partition",
|
|
100
105
|
description: "Add a new partition to a partitioned table.",
|
|
101
106
|
group: "partitioning",
|
|
102
|
-
inputSchema:
|
|
107
|
+
inputSchema: AddPartitionSchemaBase,
|
|
103
108
|
requiredScopes: ["admin"],
|
|
104
109
|
annotations: {
|
|
105
110
|
readOnlyHint: false,
|
|
@@ -108,6 +113,16 @@ function createAddPartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
108
113
|
const { table, partitionName, partitionType, value } =
|
|
109
114
|
AddPartitionSchema.parse(params);
|
|
110
115
|
|
|
116
|
+
// P154: Check if table exists
|
|
117
|
+
const tableCheck = await adapter.executeQuery(
|
|
118
|
+
`SELECT TABLE_NAME FROM information_schema.TABLES
|
|
119
|
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,
|
|
120
|
+
[table],
|
|
121
|
+
);
|
|
122
|
+
if (!tableCheck.rows || tableCheck.rows.length === 0) {
|
|
123
|
+
return { exists: false, table };
|
|
124
|
+
}
|
|
125
|
+
|
|
111
126
|
let sql: string;
|
|
112
127
|
|
|
113
128
|
switch (partitionType) {
|
|
@@ -171,7 +186,7 @@ function createDropPartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
171
186
|
description:
|
|
172
187
|
"Drop a partition from a partitioned table. Warning: This deletes all data in the partition!",
|
|
173
188
|
group: "partitioning",
|
|
174
|
-
inputSchema:
|
|
189
|
+
inputSchema: DropPartitionSchemaBase,
|
|
175
190
|
requiredScopes: ["admin"],
|
|
176
191
|
annotations: {
|
|
177
192
|
readOnlyHint: false,
|
|
@@ -180,6 +195,16 @@ function createDropPartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
180
195
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
181
196
|
const { table, partitionName } = DropPartitionSchema.parse(params);
|
|
182
197
|
|
|
198
|
+
// P154: Check if table exists
|
|
199
|
+
const tableCheck = await adapter.executeQuery(
|
|
200
|
+
`SELECT TABLE_NAME FROM information_schema.TABLES
|
|
201
|
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,
|
|
202
|
+
[table],
|
|
203
|
+
);
|
|
204
|
+
if (!tableCheck.rows || tableCheck.rows.length === 0) {
|
|
205
|
+
return { exists: false, table };
|
|
206
|
+
}
|
|
207
|
+
|
|
183
208
|
try {
|
|
184
209
|
await adapter.executeQuery(
|
|
185
210
|
`ALTER TABLE \`${table}\` DROP PARTITION \`${partitionName}\``,
|
|
@@ -225,14 +250,36 @@ function createReorganizePartitionTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
225
250
|
title: "MySQL Reorganize Partition",
|
|
226
251
|
description: "Reorganize partitions by splitting or merging them.",
|
|
227
252
|
group: "partitioning",
|
|
228
|
-
inputSchema:
|
|
253
|
+
inputSchema: ReorganizePartitionSchemaBase,
|
|
229
254
|
requiredScopes: ["admin"],
|
|
230
255
|
annotations: {
|
|
231
256
|
readOnlyHint: false,
|
|
232
257
|
},
|
|
233
258
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
234
|
-
|
|
235
|
-
|
|
259
|
+
let parsed;
|
|
260
|
+
try {
|
|
261
|
+
parsed = ReorganizePartitionSchema.parse(params);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error instanceof ZodError) {
|
|
264
|
+
return {
|
|
265
|
+
success: false,
|
|
266
|
+
error:
|
|
267
|
+
"HASH/KEY partitions cannot be reorganized. Only RANGE and LIST partition types support REORGANIZE PARTITION.",
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
const { table, fromPartitions, partitionType, toPartitions } = parsed;
|
|
273
|
+
|
|
274
|
+
// P154: Check if table exists
|
|
275
|
+
const tableCheck = await adapter.executeQuery(
|
|
276
|
+
`SELECT TABLE_NAME FROM information_schema.TABLES
|
|
277
|
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,
|
|
278
|
+
[table],
|
|
279
|
+
);
|
|
280
|
+
if (!tableCheck.rows || tableCheck.rows.length === 0) {
|
|
281
|
+
return { exists: false, table };
|
|
282
|
+
}
|
|
236
283
|
|
|
237
284
|
const fromList = fromPartitions.map((p) => `\`${p}\``).join(", ");
|
|
238
285
|
const toList = toPartitions
|