@berthojoris/mcp-mysql-server 1.36.0 → 1.37.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/CHANGELOG.md +13 -0
- package/DOCUMENTATIONS.md +5 -13
- package/README.md +3 -5
- package/dist/config/featureConfig.d.ts +1 -3
- package/dist/config/featureConfig.js +1 -22
- package/dist/index.d.ts +0 -43
- package/dist/index.js +0 -23
- package/dist/mcp-server.js +0 -420
- package/dist/tools/dataExportTools.d.ts +0 -50
- package/dist/tools/dataExportTools.js +0 -424
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to the MySQL MCP Server will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.37.0] - 2026-03-06
|
|
9
|
+
|
|
10
|
+
### Removed
|
|
11
|
+
- **Documentation Categories**: Removed `import_export` and `data_migration` categories from documentation to reduce bundle size
|
|
12
|
+
- **Import/Export Category** (6 tools): `export_query_to_json`, `export_table_to_json`, `export_table_to_sql`, `import_from_csv`, `import_from_json`, `safe_export_table`
|
|
13
|
+
- **Data Migration Category** (5 tools): `clone_table`, `compare_table_structure`, `copy_table_data`, `move_table_data`, `sync_table_data`
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Updated DOCUMENTATIONS.md to remove Data Export and Data Import sections (117 → 106 tools, 21 → 19 categories)
|
|
17
|
+
- Updated README.md with new tool count (106 tools) and removed categories from Documentation Categories table
|
|
18
|
+
- Updated package description to remove "data import/export" reference
|
|
19
|
+
- Bumped package version to `1.37.0`
|
|
20
|
+
|
|
8
21
|
## [1.36.0] - 2026-03-05
|
|
9
22
|
|
|
10
23
|
### Removed
|
package/DOCUMENTATIONS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# MySQL MCP Server - Documentation
|
|
2
2
|
|
|
3
|
-
**Last Updated:** 2026-03-
|
|
4
|
-
**Version:** 1.
|
|
5
|
-
**Total Tools:**
|
|
3
|
+
**Last Updated:** 2026-03-06 00:00:00
|
|
4
|
+
**Version:** 1.37.0
|
|
5
|
+
**Total Tools:** 106
|
|
6
6
|
|
|
7
7
|
Comprehensive documentation for the MySQL MCP Server. For quick start, see [README.md](README.md).
|
|
8
8
|
|
|
@@ -221,21 +221,13 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
221
221
|
- `configure_cache` - Configure cache settings
|
|
222
222
|
- `clear_cache` - Clear cached results
|
|
223
223
|
|
|
224
|
-
### 18.
|
|
225
|
-
- `export_table_to_csv` - Export table to CSV
|
|
226
|
-
- `export_table_to_json` - Export table to JSON
|
|
227
|
-
- `safe_export_table` - Export with PII masking
|
|
228
|
-
|
|
229
|
-
### 19. Utilities (4 tools)
|
|
224
|
+
### 18. Utilities (4 tools)
|
|
230
225
|
- `test_connection` - Test connectivity
|
|
231
226
|
- `describe_connection` - Connection info
|
|
232
227
|
- `read_changelog` - Read changelog
|
|
233
228
|
- `invalidate_table_cache` - Clear table cache
|
|
234
229
|
|
|
235
|
-
###
|
|
236
|
-
- `import_from_json` - Import data from JSON file
|
|
237
|
-
|
|
238
|
-
### 21. Schema Migrations (9 tools)
|
|
230
|
+
### 19. Schema Migrations (9 tools)
|
|
239
231
|
- `init_migrations_table` - Initialize migrations tracking table
|
|
240
232
|
- `create_migration` - Create a new migration
|
|
241
233
|
- `apply_migrations` - Apply pending migrations
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**A production-ready Model Context Protocol (MCP) server for MySQL database integration with AI agents**
|
|
6
6
|
|
|
7
|
-
**Last Updated:** 2026-03-
|
|
7
|
+
**Last Updated:** 2026-03-06 00:00:00
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
|
|
10
10
|
[](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
|
|
@@ -264,8 +264,6 @@ Use documentation categories to fine-tune which tools are exposed (Layer 2):
|
|
|
264
264
|
| `cache_management` | Manage query cache and optimization | `clear_cache, configure_cache_settings, get_cache_config, get_cache_stats, invalidate_cache_for_table` |
|
|
265
265
|
| `query_optimization` | Analyze and optimize SQL queries | `analyze_query, get_optimization_hints` |
|
|
266
266
|
| `backup_restore` | Create backups and restore databases | `backup_database, backup_table, get_create_table_statement, get_database_schema, restore_from_sql` |
|
|
267
|
-
| `import_export` | Import and export data in various formats | `export_query_to_json, export_table_to_json, export_table_to_sql, import_from_csv, import_from_json, safe_export_table` |
|
|
268
|
-
| `data_migration` | Migrate data between databases or systems | `clone_table, compare_table_structure, copy_table_data, move_table_data, sync_table_data` |
|
|
269
267
|
| `schema_migrations` | Version control for database schema changes | `apply_migrations, create_migration, generate_migration_from_diff, get_migration_status, get_schema_version, init_migrations_table, reset_failed_migration, rollback_migration, validate_migrations` |
|
|
270
268
|
| `analysis` | Data analysis and reporting tools | `get_column_statistics, get_database_summary, get_schema_erd, get_schema_rag_context` |
|
|
271
269
|
|
|
@@ -273,7 +271,7 @@ Use documentation categories to fine-tune which tools are exposed (Layer 2):
|
|
|
273
271
|
<summary>Copy/paste list (comma-separated, no spaces)</summary>
|
|
274
272
|
|
|
275
273
|
```text
|
|
276
|
-
database_discovery,crud_operations,bulk_operations,custom_queries,schema_management,utilities,transaction_management,stored_procedures,views_management,triggers_management,functions_management,index_management,constraint_management,table_maintenance,server_management,performance_monitoring,cache_management,query_optimization,backup_restore,
|
|
274
|
+
database_discovery,crud_operations,bulk_operations,custom_queries,schema_management,utilities,transaction_management,stored_procedures,views_management,triggers_management,functions_management,index_management,constraint_management,table_maintenance,server_management,performance_monitoring,cache_management,query_optimization,backup_restore,schema_migrations,analysis
|
|
277
275
|
```
|
|
278
276
|
|
|
279
277
|
</details>
|
|
@@ -284,7 +282,7 @@ Full category → tool mapping (and examples) lives in **[DOCUMENTATIONS.md →
|
|
|
284
282
|
|
|
285
283
|
## Available Tools
|
|
286
284
|
|
|
287
|
-
The server exposes **
|
|
285
|
+
The server exposes **106 tools** organized into categories (CRUD, schema, migrations, and perf/monitoring).
|
|
288
286
|
|
|
289
287
|
- Complete list of tools: **[DOCUMENTATIONS.md → Complete Tools Reference](DOCUMENTATIONS.md#🔧-complete-tools-reference)**
|
|
290
288
|
|
|
@@ -14,7 +14,7 @@ export declare enum ToolCategory {
|
|
|
14
14
|
PROCEDURE = "procedure"
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Documentation categories from README (
|
|
17
|
+
* Documentation categories from README (19 categories)
|
|
18
18
|
* More intuitive and matches user mental model
|
|
19
19
|
*/
|
|
20
20
|
export declare enum DocCategory {
|
|
@@ -37,8 +37,6 @@ export declare enum DocCategory {
|
|
|
37
37
|
CACHE_MANAGEMENT = "cache_management",
|
|
38
38
|
QUERY_OPTIMIZATION = "query_optimization",
|
|
39
39
|
BACKUP_RESTORE = "backup_restore",
|
|
40
|
-
IMPORT_EXPORT = "import_export",
|
|
41
|
-
DATA_MIGRATION = "data_migration",
|
|
42
40
|
SCHEMA_MIGRATIONS = "schema_migrations",
|
|
43
41
|
ANALYSIS = "analysis"
|
|
44
42
|
}
|
|
@@ -24,7 +24,7 @@ var ToolCategory;
|
|
|
24
24
|
ToolCategory["PROCEDURE"] = "procedure";
|
|
25
25
|
})(ToolCategory || (exports.ToolCategory = ToolCategory = {}));
|
|
26
26
|
/**
|
|
27
|
-
* Documentation categories from README (
|
|
27
|
+
* Documentation categories from README (19 categories)
|
|
28
28
|
* More intuitive and matches user mental model
|
|
29
29
|
*/
|
|
30
30
|
var DocCategory;
|
|
@@ -48,8 +48,6 @@ var DocCategory;
|
|
|
48
48
|
DocCategory["CACHE_MANAGEMENT"] = "cache_management";
|
|
49
49
|
DocCategory["QUERY_OPTIMIZATION"] = "query_optimization";
|
|
50
50
|
DocCategory["BACKUP_RESTORE"] = "backup_restore";
|
|
51
|
-
DocCategory["IMPORT_EXPORT"] = "import_export";
|
|
52
|
-
DocCategory["DATA_MIGRATION"] = "data_migration";
|
|
53
51
|
DocCategory["SCHEMA_MIGRATIONS"] = "schema_migrations";
|
|
54
52
|
DocCategory["ANALYSIS"] = "analysis";
|
|
55
53
|
})(DocCategory || (exports.DocCategory = DocCategory = {}));
|
|
@@ -331,19 +329,6 @@ exports.toolDocCategoryMap = {
|
|
|
331
329
|
restoreFromSql: DocCategory.BACKUP_RESTORE,
|
|
332
330
|
getCreateTableStatement: DocCategory.BACKUP_RESTORE,
|
|
333
331
|
getDatabaseSchema: DocCategory.BACKUP_RESTORE,
|
|
334
|
-
// Import/Export
|
|
335
|
-
exportTableToJSON: DocCategory.IMPORT_EXPORT,
|
|
336
|
-
exportQueryToJSON: DocCategory.IMPORT_EXPORT,
|
|
337
|
-
exportTableToSql: DocCategory.IMPORT_EXPORT,
|
|
338
|
-
safe_export_table: DocCategory.IMPORT_EXPORT,
|
|
339
|
-
importFromCSV: DocCategory.IMPORT_EXPORT,
|
|
340
|
-
importFromJSON: DocCategory.IMPORT_EXPORT,
|
|
341
|
-
// Data Migration
|
|
342
|
-
copyTableData: DocCategory.DATA_MIGRATION,
|
|
343
|
-
moveTableData: DocCategory.DATA_MIGRATION,
|
|
344
|
-
cloneTable: DocCategory.DATA_MIGRATION,
|
|
345
|
-
compareTableStructure: DocCategory.DATA_MIGRATION,
|
|
346
|
-
syncTableData: DocCategory.DATA_MIGRATION,
|
|
347
332
|
// Schema Migrations
|
|
348
333
|
initMigrationsTable: DocCategory.SCHEMA_MIGRATIONS,
|
|
349
334
|
createMigration: DocCategory.SCHEMA_MIGRATIONS,
|
|
@@ -390,18 +375,14 @@ const legacyToDocCategoryMap = {
|
|
|
390
375
|
create: [
|
|
391
376
|
DocCategory.CRUD_OPERATIONS,
|
|
392
377
|
DocCategory.BULK_OPERATIONS,
|
|
393
|
-
DocCategory.IMPORT_EXPORT,
|
|
394
|
-
DocCategory.DATA_MIGRATION,
|
|
395
378
|
],
|
|
396
379
|
update: [
|
|
397
380
|
DocCategory.CRUD_OPERATIONS,
|
|
398
381
|
DocCategory.BULK_OPERATIONS,
|
|
399
|
-
DocCategory.DATA_MIGRATION,
|
|
400
382
|
],
|
|
401
383
|
delete: [
|
|
402
384
|
DocCategory.CRUD_OPERATIONS,
|
|
403
385
|
DocCategory.BULK_OPERATIONS,
|
|
404
|
-
DocCategory.DATA_MIGRATION,
|
|
405
386
|
],
|
|
406
387
|
execute: [DocCategory.CUSTOM_QUERIES, DocCategory.SERVER_MANAGEMENT],
|
|
407
388
|
ddl: [
|
|
@@ -412,7 +393,6 @@ const legacyToDocCategoryMap = {
|
|
|
412
393
|
DocCategory.CONSTRAINT_MANAGEMENT,
|
|
413
394
|
DocCategory.TABLE_MAINTENANCE,
|
|
414
395
|
DocCategory.BACKUP_RESTORE,
|
|
415
|
-
DocCategory.DATA_MIGRATION,
|
|
416
396
|
DocCategory.SCHEMA_MIGRATIONS,
|
|
417
397
|
],
|
|
418
398
|
utility: [
|
|
@@ -422,7 +402,6 @@ const legacyToDocCategoryMap = {
|
|
|
422
402
|
DocCategory.CACHE_MANAGEMENT,
|
|
423
403
|
DocCategory.QUERY_OPTIMIZATION,
|
|
424
404
|
DocCategory.BACKUP_RESTORE,
|
|
425
|
-
DocCategory.IMPORT_EXPORT,
|
|
426
405
|
],
|
|
427
406
|
transaction: [DocCategory.TRANSACTION_MANAGEMENT],
|
|
428
407
|
procedure: [DocCategory.STORED_PROCEDURES, DocCategory.FUNCTIONS_MANAGEMENT],
|
package/dist/index.d.ts
CHANGED
|
@@ -321,49 +321,6 @@ export declare class MySQLMCP {
|
|
|
321
321
|
data?: any;
|
|
322
322
|
error?: string;
|
|
323
323
|
}>;
|
|
324
|
-
exportTableToJSON(params: {
|
|
325
|
-
table_name: string;
|
|
326
|
-
filters?: any[];
|
|
327
|
-
pagination?: {
|
|
328
|
-
page: number;
|
|
329
|
-
limit: number;
|
|
330
|
-
};
|
|
331
|
-
sorting?: {
|
|
332
|
-
field: string;
|
|
333
|
-
direction: "asc" | "desc";
|
|
334
|
-
};
|
|
335
|
-
pretty?: boolean;
|
|
336
|
-
database?: string;
|
|
337
|
-
}): Promise<{
|
|
338
|
-
status: string;
|
|
339
|
-
data?: any;
|
|
340
|
-
error?: string;
|
|
341
|
-
}>;
|
|
342
|
-
importFromCSV(params: {
|
|
343
|
-
table_name: string;
|
|
344
|
-
csv_data: string;
|
|
345
|
-
has_headers?: boolean;
|
|
346
|
-
column_mapping?: Record<string, string>;
|
|
347
|
-
skip_errors?: boolean;
|
|
348
|
-
batch_size?: number;
|
|
349
|
-
database?: string;
|
|
350
|
-
}): Promise<{
|
|
351
|
-
status: string;
|
|
352
|
-
data?: any;
|
|
353
|
-
error?: string;
|
|
354
|
-
}>;
|
|
355
|
-
importFromJSON(params: {
|
|
356
|
-
table_name: string;
|
|
357
|
-
json_data: string;
|
|
358
|
-
column_mapping?: Record<string, string>;
|
|
359
|
-
skip_errors?: boolean;
|
|
360
|
-
batch_size?: number;
|
|
361
|
-
database?: string;
|
|
362
|
-
}): Promise<{
|
|
363
|
-
status: string;
|
|
364
|
-
data?: any;
|
|
365
|
-
error?: string;
|
|
366
|
-
}>;
|
|
367
324
|
/**
|
|
368
325
|
* Initialize the migrations tracking table
|
|
369
326
|
*/
|
package/dist/index.js
CHANGED
|
@@ -346,29 +346,6 @@ class MySQLMCP {
|
|
|
346
346
|
}
|
|
347
347
|
return await this.dataExportTools.exportTableToCSV(params);
|
|
348
348
|
}
|
|
349
|
-
// Extended Data Export Tools (JSON, SQL)
|
|
350
|
-
async exportTableToJSON(params) {
|
|
351
|
-
const check = this.checkToolEnabled("exportTableToJSON");
|
|
352
|
-
if (!check.enabled) {
|
|
353
|
-
return { status: "error", error: check.error };
|
|
354
|
-
}
|
|
355
|
-
return await this.dataExportTools.exportTableToJSON(params);
|
|
356
|
-
}
|
|
357
|
-
// Data Import Tools
|
|
358
|
-
async importFromCSV(params) {
|
|
359
|
-
const check = this.checkToolEnabled("importFromCSV");
|
|
360
|
-
if (!check.enabled) {
|
|
361
|
-
return { status: "error", error: check.error };
|
|
362
|
-
}
|
|
363
|
-
return await this.dataExportTools.importFromCSV(params);
|
|
364
|
-
}
|
|
365
|
-
async importFromJSON(params) {
|
|
366
|
-
const check = this.checkToolEnabled("importFromJSON");
|
|
367
|
-
if (!check.enabled) {
|
|
368
|
-
return { status: "error", error: check.error };
|
|
369
|
-
}
|
|
370
|
-
return await this.dataExportTools.importFromJSON(params);
|
|
371
|
-
}
|
|
372
349
|
// ==========================================
|
|
373
350
|
// Schema Versioning and Migrations Tools
|
|
374
351
|
// ==========================================
|
package/dist/mcp-server.js
CHANGED
|
@@ -484,33 +484,6 @@ const TOOLS = [
|
|
|
484
484
|
required: ["query"],
|
|
485
485
|
},
|
|
486
486
|
},
|
|
487
|
-
{
|
|
488
|
-
name: "safe_export_table",
|
|
489
|
-
description: "🔒 SECURITY: Exports table data to CSV with automatic PII/sensitive data masking. Redacts emails, credit cards, passwords, etc. Use this instead of export_table_to_csv when handling sensitive data or sharing exports externally.",
|
|
490
|
-
inputSchema: {
|
|
491
|
-
type: "object",
|
|
492
|
-
properties: {
|
|
493
|
-
table_name: {
|
|
494
|
-
type: "string",
|
|
495
|
-
description: "Name of the table to export",
|
|
496
|
-
},
|
|
497
|
-
masking_profile: {
|
|
498
|
-
type: "string",
|
|
499
|
-
enum: ["soft", "partial", "strict"],
|
|
500
|
-
description: "Masking profile to apply (default: strict). strict=redact all PII/secrets, partial=partial mask PII, soft=mask secrets only.",
|
|
501
|
-
},
|
|
502
|
-
limit: {
|
|
503
|
-
type: "number",
|
|
504
|
-
description: "Maximum number of rows to export (default: 1000, max: 10000)",
|
|
505
|
-
},
|
|
506
|
-
include_headers: {
|
|
507
|
-
type: "boolean",
|
|
508
|
-
description: "Whether to include CSV headers (default: true)",
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
required: ["table_name"],
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
487
|
{
|
|
515
488
|
name: "repair_query",
|
|
516
489
|
description: "🔧 Diagnoses SQL query errors and suggests fixes. Analyzes syntax errors, missing columns/tables, and logic issues. Provide the query and optional error message to get repair recommendations. Use when a query fails or needs debugging.",
|
|
@@ -2145,386 +2118,6 @@ const TOOLS = [
|
|
|
2145
2118
|
},
|
|
2146
2119
|
},
|
|
2147
2120
|
},
|
|
2148
|
-
// Extended Data Export Tools (JSON, SQL)
|
|
2149
|
-
{
|
|
2150
|
-
name: "export_table_to_json",
|
|
2151
|
-
description: "Export table data to JSON format with optional filtering, pagination, and sorting.",
|
|
2152
|
-
inputSchema: {
|
|
2153
|
-
type: "object",
|
|
2154
|
-
properties: {
|
|
2155
|
-
table_name: {
|
|
2156
|
-
type: "string",
|
|
2157
|
-
description: "Name of the table to export",
|
|
2158
|
-
},
|
|
2159
|
-
filters: {
|
|
2160
|
-
type: "array",
|
|
2161
|
-
description: "Array of filter conditions",
|
|
2162
|
-
items: {
|
|
2163
|
-
type: "object",
|
|
2164
|
-
properties: {
|
|
2165
|
-
field: { type: "string" },
|
|
2166
|
-
operator: {
|
|
2167
|
-
type: "string",
|
|
2168
|
-
enum: ["eq", "neq", "gt", "gte", "lt", "lte", "like", "in"],
|
|
2169
|
-
},
|
|
2170
|
-
value: {},
|
|
2171
|
-
},
|
|
2172
|
-
required: ["field", "operator", "value"],
|
|
2173
|
-
},
|
|
2174
|
-
},
|
|
2175
|
-
pagination: {
|
|
2176
|
-
type: "object",
|
|
2177
|
-
properties: {
|
|
2178
|
-
page: { type: "number" },
|
|
2179
|
-
limit: { type: "number" },
|
|
2180
|
-
},
|
|
2181
|
-
},
|
|
2182
|
-
sorting: {
|
|
2183
|
-
type: "object",
|
|
2184
|
-
properties: {
|
|
2185
|
-
field: { type: "string" },
|
|
2186
|
-
direction: { type: "string", enum: ["asc", "desc"] },
|
|
2187
|
-
},
|
|
2188
|
-
},
|
|
2189
|
-
pretty: {
|
|
2190
|
-
type: "boolean",
|
|
2191
|
-
description: "Pretty print JSON output (default: true)",
|
|
2192
|
-
},
|
|
2193
|
-
database: {
|
|
2194
|
-
type: "string",
|
|
2195
|
-
description: "Optional: specific database name",
|
|
2196
|
-
},
|
|
2197
|
-
},
|
|
2198
|
-
required: ["table_name"],
|
|
2199
|
-
},
|
|
2200
|
-
},
|
|
2201
|
-
{
|
|
2202
|
-
name: "export_query_to_json",
|
|
2203
|
-
description: "Export the results of a SELECT query to JSON format.",
|
|
2204
|
-
inputSchema: {
|
|
2205
|
-
type: "object",
|
|
2206
|
-
properties: {
|
|
2207
|
-
query: {
|
|
2208
|
-
type: "string",
|
|
2209
|
-
description: "SQL SELECT query to execute and export",
|
|
2210
|
-
},
|
|
2211
|
-
params: {
|
|
2212
|
-
type: "array",
|
|
2213
|
-
description: "Optional array of parameters for parameterized queries",
|
|
2214
|
-
items: {},
|
|
2215
|
-
},
|
|
2216
|
-
pretty: {
|
|
2217
|
-
type: "boolean",
|
|
2218
|
-
description: "Pretty print JSON output (default: true)",
|
|
2219
|
-
},
|
|
2220
|
-
},
|
|
2221
|
-
required: ["query"],
|
|
2222
|
-
},
|
|
2223
|
-
},
|
|
2224
|
-
{
|
|
2225
|
-
name: "export_table_to_sql",
|
|
2226
|
-
description: "Export table data to SQL INSERT statements with optional CREATE TABLE.",
|
|
2227
|
-
inputSchema: {
|
|
2228
|
-
type: "object",
|
|
2229
|
-
properties: {
|
|
2230
|
-
table_name: {
|
|
2231
|
-
type: "string",
|
|
2232
|
-
description: "Name of the table to export",
|
|
2233
|
-
},
|
|
2234
|
-
filters: {
|
|
2235
|
-
type: "array",
|
|
2236
|
-
description: "Array of filter conditions",
|
|
2237
|
-
items: {
|
|
2238
|
-
type: "object",
|
|
2239
|
-
properties: {
|
|
2240
|
-
field: { type: "string" },
|
|
2241
|
-
operator: {
|
|
2242
|
-
type: "string",
|
|
2243
|
-
enum: ["eq", "neq", "gt", "gte", "lt", "lte", "like", "in"],
|
|
2244
|
-
},
|
|
2245
|
-
value: {},
|
|
2246
|
-
},
|
|
2247
|
-
required: ["field", "operator", "value"],
|
|
2248
|
-
},
|
|
2249
|
-
},
|
|
2250
|
-
include_create_table: {
|
|
2251
|
-
type: "boolean",
|
|
2252
|
-
description: "Include CREATE TABLE statement (default: false)",
|
|
2253
|
-
},
|
|
2254
|
-
batch_size: {
|
|
2255
|
-
type: "number",
|
|
2256
|
-
description: "Number of rows per INSERT statement (default: 100)",
|
|
2257
|
-
},
|
|
2258
|
-
database: {
|
|
2259
|
-
type: "string",
|
|
2260
|
-
description: "Optional: specific database name",
|
|
2261
|
-
},
|
|
2262
|
-
},
|
|
2263
|
-
required: ["table_name"],
|
|
2264
|
-
},
|
|
2265
|
-
},
|
|
2266
|
-
// Data Import Tools
|
|
2267
|
-
{
|
|
2268
|
-
name: "import_from_csv",
|
|
2269
|
-
description: "Import data from CSV string into a table. Requires 'create' permission.",
|
|
2270
|
-
inputSchema: {
|
|
2271
|
-
type: "object",
|
|
2272
|
-
properties: {
|
|
2273
|
-
table_name: {
|
|
2274
|
-
type: "string",
|
|
2275
|
-
description: "Name of the table to import into",
|
|
2276
|
-
},
|
|
2277
|
-
csv_data: {
|
|
2278
|
-
type: "string",
|
|
2279
|
-
description: "CSV data as a string",
|
|
2280
|
-
},
|
|
2281
|
-
has_headers: {
|
|
2282
|
-
type: "boolean",
|
|
2283
|
-
description: "CSV has header row (default: true)",
|
|
2284
|
-
},
|
|
2285
|
-
column_mapping: {
|
|
2286
|
-
type: "object",
|
|
2287
|
-
description: "Optional: map CSV columns to table columns {csv_col: table_col}",
|
|
2288
|
-
additionalProperties: { type: "string" },
|
|
2289
|
-
},
|
|
2290
|
-
skip_errors: {
|
|
2291
|
-
type: "boolean",
|
|
2292
|
-
description: "Continue on row errors (default: false)",
|
|
2293
|
-
},
|
|
2294
|
-
batch_size: {
|
|
2295
|
-
type: "number",
|
|
2296
|
-
description: "Number of rows per batch insert (default: 100)",
|
|
2297
|
-
},
|
|
2298
|
-
database: {
|
|
2299
|
-
type: "string",
|
|
2300
|
-
description: "Optional: specific database name",
|
|
2301
|
-
},
|
|
2302
|
-
},
|
|
2303
|
-
required: ["table_name", "csv_data"],
|
|
2304
|
-
},
|
|
2305
|
-
},
|
|
2306
|
-
{
|
|
2307
|
-
name: "import_from_json",
|
|
2308
|
-
description: "Import data from JSON array string into a table. Requires 'create' permission.",
|
|
2309
|
-
inputSchema: {
|
|
2310
|
-
type: "object",
|
|
2311
|
-
properties: {
|
|
2312
|
-
table_name: {
|
|
2313
|
-
type: "string",
|
|
2314
|
-
description: "Name of the table to import into",
|
|
2315
|
-
},
|
|
2316
|
-
json_data: {
|
|
2317
|
-
type: "string",
|
|
2318
|
-
description: "JSON array of objects as a string",
|
|
2319
|
-
},
|
|
2320
|
-
column_mapping: {
|
|
2321
|
-
type: "object",
|
|
2322
|
-
description: "Optional: map JSON keys to table columns {json_key: table_col}",
|
|
2323
|
-
additionalProperties: { type: "string" },
|
|
2324
|
-
},
|
|
2325
|
-
skip_errors: {
|
|
2326
|
-
type: "boolean",
|
|
2327
|
-
description: "Continue on row errors (default: false)",
|
|
2328
|
-
},
|
|
2329
|
-
batch_size: {
|
|
2330
|
-
type: "number",
|
|
2331
|
-
description: "Number of rows per batch insert (default: 100)",
|
|
2332
|
-
},
|
|
2333
|
-
database: {
|
|
2334
|
-
type: "string",
|
|
2335
|
-
description: "Optional: specific database name",
|
|
2336
|
-
},
|
|
2337
|
-
},
|
|
2338
|
-
required: ["table_name", "json_data"],
|
|
2339
|
-
},
|
|
2340
|
-
},
|
|
2341
|
-
// Data Migration Tools
|
|
2342
|
-
{
|
|
2343
|
-
name: "copy_table_data",
|
|
2344
|
-
description: "Copy data from one table to another with optional column mapping and filtering. Requires 'create' permission.",
|
|
2345
|
-
inputSchema: {
|
|
2346
|
-
type: "object",
|
|
2347
|
-
properties: {
|
|
2348
|
-
source_table: {
|
|
2349
|
-
type: "string",
|
|
2350
|
-
description: "Name of the source table to copy from",
|
|
2351
|
-
},
|
|
2352
|
-
target_table: {
|
|
2353
|
-
type: "string",
|
|
2354
|
-
description: "Name of the target table to copy to",
|
|
2355
|
-
},
|
|
2356
|
-
column_mapping: {
|
|
2357
|
-
type: "object",
|
|
2358
|
-
description: "Optional: map source columns to target columns {source_col: target_col}",
|
|
2359
|
-
additionalProperties: { type: "string" },
|
|
2360
|
-
},
|
|
2361
|
-
filters: {
|
|
2362
|
-
type: "array",
|
|
2363
|
-
description: "Optional: array of filter conditions for source data",
|
|
2364
|
-
items: {
|
|
2365
|
-
type: "object",
|
|
2366
|
-
properties: {
|
|
2367
|
-
field: { type: "string" },
|
|
2368
|
-
operator: {
|
|
2369
|
-
type: "string",
|
|
2370
|
-
enum: ["eq", "neq", "gt", "gte", "lt", "lte", "like", "in"],
|
|
2371
|
-
},
|
|
2372
|
-
value: {},
|
|
2373
|
-
},
|
|
2374
|
-
required: ["field", "operator", "value"],
|
|
2375
|
-
},
|
|
2376
|
-
},
|
|
2377
|
-
batch_size: {
|
|
2378
|
-
type: "number",
|
|
2379
|
-
description: "Number of rows per batch insert (default: 1000)",
|
|
2380
|
-
},
|
|
2381
|
-
database: {
|
|
2382
|
-
type: "string",
|
|
2383
|
-
description: "Optional: specific database name",
|
|
2384
|
-
},
|
|
2385
|
-
},
|
|
2386
|
-
required: ["source_table", "target_table"],
|
|
2387
|
-
},
|
|
2388
|
-
},
|
|
2389
|
-
{
|
|
2390
|
-
name: "move_table_data",
|
|
2391
|
-
description: "Move data from one table to another (copy then delete from source). Requires 'create' and 'delete' permissions.",
|
|
2392
|
-
inputSchema: {
|
|
2393
|
-
type: "object",
|
|
2394
|
-
properties: {
|
|
2395
|
-
source_table: {
|
|
2396
|
-
type: "string",
|
|
2397
|
-
description: "Name of the source table to move from",
|
|
2398
|
-
},
|
|
2399
|
-
target_table: {
|
|
2400
|
-
type: "string",
|
|
2401
|
-
description: "Name of the target table to move to",
|
|
2402
|
-
},
|
|
2403
|
-
column_mapping: {
|
|
2404
|
-
type: "object",
|
|
2405
|
-
description: "Optional: map source columns to target columns {source_col: target_col}",
|
|
2406
|
-
additionalProperties: { type: "string" },
|
|
2407
|
-
},
|
|
2408
|
-
filters: {
|
|
2409
|
-
type: "array",
|
|
2410
|
-
description: "Optional: array of filter conditions for source data",
|
|
2411
|
-
items: {
|
|
2412
|
-
type: "object",
|
|
2413
|
-
properties: {
|
|
2414
|
-
field: { type: "string" },
|
|
2415
|
-
operator: {
|
|
2416
|
-
type: "string",
|
|
2417
|
-
enum: ["eq", "neq", "gt", "gte", "lt", "lte", "like", "in"],
|
|
2418
|
-
},
|
|
2419
|
-
value: {},
|
|
2420
|
-
},
|
|
2421
|
-
required: ["field", "operator", "value"],
|
|
2422
|
-
},
|
|
2423
|
-
},
|
|
2424
|
-
batch_size: {
|
|
2425
|
-
type: "number",
|
|
2426
|
-
description: "Number of rows per batch (default: 1000)",
|
|
2427
|
-
},
|
|
2428
|
-
database: {
|
|
2429
|
-
type: "string",
|
|
2430
|
-
description: "Optional: specific database name",
|
|
2431
|
-
},
|
|
2432
|
-
},
|
|
2433
|
-
required: ["source_table", "target_table"],
|
|
2434
|
-
},
|
|
2435
|
-
},
|
|
2436
|
-
{
|
|
2437
|
-
name: "clone_table",
|
|
2438
|
-
description: "Clone a table structure with optional data. Requires 'ddl' permission.",
|
|
2439
|
-
inputSchema: {
|
|
2440
|
-
type: "object",
|
|
2441
|
-
properties: {
|
|
2442
|
-
source_table: {
|
|
2443
|
-
type: "string",
|
|
2444
|
-
description: "Name of the source table to clone",
|
|
2445
|
-
},
|
|
2446
|
-
new_table_name: {
|
|
2447
|
-
type: "string",
|
|
2448
|
-
description: "Name of the new table to create",
|
|
2449
|
-
},
|
|
2450
|
-
include_data: {
|
|
2451
|
-
type: "boolean",
|
|
2452
|
-
description: "Include table data in the clone (default: false)",
|
|
2453
|
-
},
|
|
2454
|
-
include_indexes: {
|
|
2455
|
-
type: "boolean",
|
|
2456
|
-
description: "Include indexes in the clone (default: true)",
|
|
2457
|
-
},
|
|
2458
|
-
database: {
|
|
2459
|
-
type: "string",
|
|
2460
|
-
description: "Optional: specific database name",
|
|
2461
|
-
},
|
|
2462
|
-
},
|
|
2463
|
-
required: ["source_table", "new_table_name"],
|
|
2464
|
-
},
|
|
2465
|
-
},
|
|
2466
|
-
{
|
|
2467
|
-
name: "compare_table_structure",
|
|
2468
|
-
description: "Compare the structure of two tables and identify differences in columns, types, and indexes.",
|
|
2469
|
-
inputSchema: {
|
|
2470
|
-
type: "object",
|
|
2471
|
-
properties: {
|
|
2472
|
-
table1: {
|
|
2473
|
-
type: "string",
|
|
2474
|
-
description: "Name of the first table",
|
|
2475
|
-
},
|
|
2476
|
-
table2: {
|
|
2477
|
-
type: "string",
|
|
2478
|
-
description: "Name of the second table",
|
|
2479
|
-
},
|
|
2480
|
-
database: {
|
|
2481
|
-
type: "string",
|
|
2482
|
-
description: "Optional: specific database name",
|
|
2483
|
-
},
|
|
2484
|
-
},
|
|
2485
|
-
required: ["table1", "table2"],
|
|
2486
|
-
},
|
|
2487
|
-
},
|
|
2488
|
-
{
|
|
2489
|
-
name: "sync_table_data",
|
|
2490
|
-
description: "Synchronize data between two tables based on a key column. Supports insert-only, update-only, or upsert modes.",
|
|
2491
|
-
inputSchema: {
|
|
2492
|
-
type: "object",
|
|
2493
|
-
properties: {
|
|
2494
|
-
source_table: {
|
|
2495
|
-
type: "string",
|
|
2496
|
-
description: "Name of the source table",
|
|
2497
|
-
},
|
|
2498
|
-
target_table: {
|
|
2499
|
-
type: "string",
|
|
2500
|
-
description: "Name of the target table",
|
|
2501
|
-
},
|
|
2502
|
-
key_column: {
|
|
2503
|
-
type: "string",
|
|
2504
|
-
description: "Primary key or unique column to match records",
|
|
2505
|
-
},
|
|
2506
|
-
columns_to_sync: {
|
|
2507
|
-
type: "array",
|
|
2508
|
-
items: { type: "string" },
|
|
2509
|
-
description: "Optional: specific columns to sync (default: all columns)",
|
|
2510
|
-
},
|
|
2511
|
-
sync_mode: {
|
|
2512
|
-
type: "string",
|
|
2513
|
-
enum: ["insert_only", "update_only", "upsert"],
|
|
2514
|
-
description: "Sync mode: insert_only (new records), update_only (existing), upsert (both). Default: upsert",
|
|
2515
|
-
},
|
|
2516
|
-
batch_size: {
|
|
2517
|
-
type: "number",
|
|
2518
|
-
description: "Number of rows per batch (default: 1000)",
|
|
2519
|
-
},
|
|
2520
|
-
database: {
|
|
2521
|
-
type: "string",
|
|
2522
|
-
description: "Optional: specific database name",
|
|
2523
|
-
},
|
|
2524
|
-
},
|
|
2525
|
-
required: ["source_table", "target_table", "key_column"],
|
|
2526
|
-
},
|
|
2527
|
-
},
|
|
2528
2121
|
// Schema Versioning and Migrations Tools
|
|
2529
2122
|
{
|
|
2530
2123
|
name: "init_migrations_table",
|
|
@@ -3330,17 +2923,6 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
3330
2923
|
case "explain_query":
|
|
3331
2924
|
result = await mysqlMCP.explainQuery((args || {}));
|
|
3332
2925
|
break;
|
|
3333
|
-
// Extended Data Export Tools
|
|
3334
|
-
case "export_table_to_json":
|
|
3335
|
-
result = await mysqlMCP.exportTableToJSON((args || {}));
|
|
3336
|
-
break;
|
|
3337
|
-
case "safe_export_table":
|
|
3338
|
-
result = await mysqlMCP.safeExportTable((args || {}));
|
|
3339
|
-
break;
|
|
3340
|
-
// Data Import Tools
|
|
3341
|
-
case "import_from_json":
|
|
3342
|
-
result = await mysqlMCP.importFromJSON((args || {}));
|
|
3343
|
-
break;
|
|
3344
2926
|
// Schema Versioning and Migrations Tools
|
|
3345
2927
|
case "init_migrations_table":
|
|
3346
2928
|
result = await mysqlMCP.initMigrationsTable((args || {}));
|
|
@@ -3527,8 +3109,6 @@ function validateToolArguments(name, args) {
|
|
|
3527
3109
|
return { valid: false, errors: [validation.error || 'Invalid table name'] };
|
|
3528
3110
|
}
|
|
3529
3111
|
return { valid: true };
|
|
3530
|
-
case "import_from_json":
|
|
3531
|
-
return (0, inputValidation_js_1.validateImportFromJSON)(args);
|
|
3532
3112
|
default:
|
|
3533
3113
|
return { valid: true }; // For tools without specific validation
|
|
3534
3114
|
}
|
|
@@ -26,54 +26,4 @@ export declare class DataExportTools {
|
|
|
26
26
|
data?: any;
|
|
27
27
|
error?: string;
|
|
28
28
|
}>;
|
|
29
|
-
/**
|
|
30
|
-
* Export table data to JSON format
|
|
31
|
-
*/
|
|
32
|
-
exportTableToJSON(params: {
|
|
33
|
-
table_name: string;
|
|
34
|
-
filters?: FilterCondition[];
|
|
35
|
-
pagination?: Pagination;
|
|
36
|
-
sorting?: Sorting;
|
|
37
|
-
pretty?: boolean;
|
|
38
|
-
database?: string;
|
|
39
|
-
}): Promise<{
|
|
40
|
-
status: string;
|
|
41
|
-
data?: any;
|
|
42
|
-
error?: string;
|
|
43
|
-
}>;
|
|
44
|
-
/**
|
|
45
|
-
* Import data from CSV string
|
|
46
|
-
*/
|
|
47
|
-
importFromCSV(params: {
|
|
48
|
-
table_name: string;
|
|
49
|
-
csv_data: string;
|
|
50
|
-
has_headers?: boolean;
|
|
51
|
-
column_mapping?: Record<string, string>;
|
|
52
|
-
skip_errors?: boolean;
|
|
53
|
-
batch_size?: number;
|
|
54
|
-
database?: string;
|
|
55
|
-
}): Promise<{
|
|
56
|
-
status: string;
|
|
57
|
-
data?: any;
|
|
58
|
-
error?: string;
|
|
59
|
-
}>;
|
|
60
|
-
/**
|
|
61
|
-
* Parse CSV string into array of arrays
|
|
62
|
-
*/
|
|
63
|
-
private parseCSV;
|
|
64
|
-
/**
|
|
65
|
-
* Import data from JSON string
|
|
66
|
-
*/
|
|
67
|
-
importFromJSON(params: {
|
|
68
|
-
table_name: string;
|
|
69
|
-
json_data: string;
|
|
70
|
-
column_mapping?: Record<string, string>;
|
|
71
|
-
skip_errors?: boolean;
|
|
72
|
-
batch_size?: number;
|
|
73
|
-
database?: string;
|
|
74
|
-
}): Promise<{
|
|
75
|
-
status: string;
|
|
76
|
-
data?: any;
|
|
77
|
-
error?: string;
|
|
78
|
-
}>;
|
|
79
29
|
}
|
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.DataExportTools = void 0;
|
|
7
7
|
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
8
|
const config_1 = require("../config/config");
|
|
9
|
-
const inputValidation_1 = require("../validation/inputValidation");
|
|
10
9
|
class DataExportTools {
|
|
11
10
|
constructor(security) {
|
|
12
11
|
this.db = connection_1.default.getInstance();
|
|
@@ -222,428 +221,5 @@ class DataExportTools {
|
|
|
222
221
|
};
|
|
223
222
|
}
|
|
224
223
|
}
|
|
225
|
-
/**
|
|
226
|
-
* Export table data to JSON format
|
|
227
|
-
*/
|
|
228
|
-
async exportTableToJSON(params) {
|
|
229
|
-
try {
|
|
230
|
-
const { table_name, filters = [], pagination, sorting, pretty = true, database, } = params;
|
|
231
|
-
// Validate database access
|
|
232
|
-
const dbValidation = this.validateDatabaseAccess(database);
|
|
233
|
-
if (!dbValidation.valid) {
|
|
234
|
-
return { status: "error", error: dbValidation.error };
|
|
235
|
-
}
|
|
236
|
-
// Validate table name
|
|
237
|
-
const tableValidation = this.security.validateIdentifier(table_name);
|
|
238
|
-
if (!tableValidation.valid) {
|
|
239
|
-
return { status: "error", error: tableValidation.error };
|
|
240
|
-
}
|
|
241
|
-
// Build WHERE clause
|
|
242
|
-
let whereClause = "";
|
|
243
|
-
const whereParams = [];
|
|
244
|
-
if (filters && filters.length > 0) {
|
|
245
|
-
const whereConditions = [];
|
|
246
|
-
for (const filter of filters) {
|
|
247
|
-
const fieldValidation = this.security.validateIdentifier(filter.field);
|
|
248
|
-
if (!fieldValidation.valid) {
|
|
249
|
-
return {
|
|
250
|
-
status: "error",
|
|
251
|
-
error: `Invalid field name: ${filter.field}`,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
const fieldName = this.security.escapeIdentifier(filter.field);
|
|
255
|
-
switch (filter.operator) {
|
|
256
|
-
case "eq":
|
|
257
|
-
whereConditions.push(`${fieldName} = ?`);
|
|
258
|
-
whereParams.push(filter.value);
|
|
259
|
-
break;
|
|
260
|
-
case "neq":
|
|
261
|
-
whereConditions.push(`${fieldName} != ?`);
|
|
262
|
-
whereParams.push(filter.value);
|
|
263
|
-
break;
|
|
264
|
-
case "gt":
|
|
265
|
-
whereConditions.push(`${fieldName} > ?`);
|
|
266
|
-
whereParams.push(filter.value);
|
|
267
|
-
break;
|
|
268
|
-
case "gte":
|
|
269
|
-
whereConditions.push(`${fieldName} >= ?`);
|
|
270
|
-
whereParams.push(filter.value);
|
|
271
|
-
break;
|
|
272
|
-
case "lt":
|
|
273
|
-
whereConditions.push(`${fieldName} < ?`);
|
|
274
|
-
whereParams.push(filter.value);
|
|
275
|
-
break;
|
|
276
|
-
case "lte":
|
|
277
|
-
whereConditions.push(`${fieldName} <= ?`);
|
|
278
|
-
whereParams.push(filter.value);
|
|
279
|
-
break;
|
|
280
|
-
case "like":
|
|
281
|
-
whereConditions.push(`${fieldName} LIKE ?`);
|
|
282
|
-
whereParams.push(filter.value);
|
|
283
|
-
break;
|
|
284
|
-
case "in":
|
|
285
|
-
if (Array.isArray(filter.value)) {
|
|
286
|
-
const placeholders = filter.value.map(() => "?").join(", ");
|
|
287
|
-
whereConditions.push(`${fieldName} IN (${placeholders})`);
|
|
288
|
-
whereParams.push(...filter.value);
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
return {
|
|
292
|
-
status: "error",
|
|
293
|
-
error: "IN operator requires an array of values",
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
297
|
-
default:
|
|
298
|
-
return {
|
|
299
|
-
status: "error",
|
|
300
|
-
error: `Unsupported operator: ${filter.operator}`,
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (whereConditions.length > 0) {
|
|
305
|
-
whereClause = "WHERE " + whereConditions.join(" AND ");
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
// Build ORDER BY clause
|
|
309
|
-
let orderByClause = "";
|
|
310
|
-
if (sorting) {
|
|
311
|
-
const fieldValidation = this.security.validateIdentifier(sorting.field);
|
|
312
|
-
if (!fieldValidation.valid) {
|
|
313
|
-
return {
|
|
314
|
-
status: "error",
|
|
315
|
-
error: `Invalid sort field name: ${sorting.field}`,
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
const fieldName = this.security.escapeIdentifier(sorting.field);
|
|
319
|
-
const direction = sorting.direction.toUpperCase() === "DESC" ? "DESC" : "ASC";
|
|
320
|
-
orderByClause = `ORDER BY ${fieldName} ${direction}`;
|
|
321
|
-
}
|
|
322
|
-
// Build LIMIT clause
|
|
323
|
-
let limitClause = "";
|
|
324
|
-
if (pagination) {
|
|
325
|
-
const offset = (pagination.page - 1) * pagination.limit;
|
|
326
|
-
limitClause = `LIMIT ${offset}, ${pagination.limit}`;
|
|
327
|
-
}
|
|
328
|
-
// Construct and execute query
|
|
329
|
-
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
330
|
-
const query = `SELECT * FROM ${escapedTableName} ${whereClause} ${orderByClause} ${limitClause}`;
|
|
331
|
-
const results = await this.db.query(query, whereParams);
|
|
332
|
-
// Generate JSON
|
|
333
|
-
const json = pretty
|
|
334
|
-
? JSON.stringify(results, null, 2)
|
|
335
|
-
: JSON.stringify(results);
|
|
336
|
-
return {
|
|
337
|
-
status: "success",
|
|
338
|
-
data: {
|
|
339
|
-
json: json,
|
|
340
|
-
row_count: results.length,
|
|
341
|
-
table_name: table_name,
|
|
342
|
-
},
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
catch (error) {
|
|
346
|
-
return {
|
|
347
|
-
status: "error",
|
|
348
|
-
error: error.message,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Import data from CSV string
|
|
354
|
-
*/
|
|
355
|
-
async importFromCSV(params) {
|
|
356
|
-
try {
|
|
357
|
-
const { table_name, csv_data, has_headers = true, column_mapping, skip_errors = false, batch_size = 100, database, } = params;
|
|
358
|
-
// Validate database access
|
|
359
|
-
const dbValidation = this.validateDatabaseAccess(database);
|
|
360
|
-
if (!dbValidation.valid) {
|
|
361
|
-
return { status: "error", error: dbValidation.error };
|
|
362
|
-
}
|
|
363
|
-
// Validate table name
|
|
364
|
-
const tableValidation = this.security.validateIdentifier(table_name);
|
|
365
|
-
if (!tableValidation.valid) {
|
|
366
|
-
return { status: "error", error: tableValidation.error };
|
|
367
|
-
}
|
|
368
|
-
// Parse CSV
|
|
369
|
-
const rows = this.parseCSV(csv_data);
|
|
370
|
-
if (rows.length === 0) {
|
|
371
|
-
return { status: "error", error: "CSV data is empty" };
|
|
372
|
-
}
|
|
373
|
-
let headers;
|
|
374
|
-
let dataRows;
|
|
375
|
-
if (has_headers) {
|
|
376
|
-
headers = rows[0];
|
|
377
|
-
dataRows = rows.slice(1);
|
|
378
|
-
}
|
|
379
|
-
else {
|
|
380
|
-
// If no headers, we need column_mapping or use column indexes
|
|
381
|
-
if (!column_mapping) {
|
|
382
|
-
return {
|
|
383
|
-
status: "error",
|
|
384
|
-
error: "Column mapping is required when CSV has no headers",
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
headers = Object.keys(column_mapping);
|
|
388
|
-
dataRows = rows;
|
|
389
|
-
}
|
|
390
|
-
// Apply column mapping if provided
|
|
391
|
-
const finalHeaders = column_mapping
|
|
392
|
-
? headers.map((h) => column_mapping[h] || h)
|
|
393
|
-
: headers;
|
|
394
|
-
// Validate all column names
|
|
395
|
-
for (const col of finalHeaders) {
|
|
396
|
-
const colValidation = this.security.validateIdentifier(col);
|
|
397
|
-
if (!colValidation.valid) {
|
|
398
|
-
return { status: "error", error: `Invalid column name: ${col}` };
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
402
|
-
const escapedColumns = finalHeaders
|
|
403
|
-
.map((c) => this.security.escapeIdentifier(c))
|
|
404
|
-
.join(", ");
|
|
405
|
-
let successCount = 0;
|
|
406
|
-
let errorCount = 0;
|
|
407
|
-
const errors = [];
|
|
408
|
-
let queryCount = 0;
|
|
409
|
-
// Insert in batches
|
|
410
|
-
for (let i = 0; i < dataRows.length; i += batch_size) {
|
|
411
|
-
const batch = dataRows.slice(i, i + batch_size);
|
|
412
|
-
try {
|
|
413
|
-
const values = batch
|
|
414
|
-
.map((row) => {
|
|
415
|
-
const rowValues = row.map((val) => {
|
|
416
|
-
if (val === "" || val === null || val === undefined)
|
|
417
|
-
return "NULL";
|
|
418
|
-
return this.escapeValue(val);
|
|
419
|
-
});
|
|
420
|
-
return `(${rowValues.join(", ")})`;
|
|
421
|
-
})
|
|
422
|
-
.join(", ");
|
|
423
|
-
const query = `INSERT INTO ${escapedTableName} (${escapedColumns}) VALUES ${values}`;
|
|
424
|
-
await this.db.query(query);
|
|
425
|
-
queryCount++;
|
|
426
|
-
successCount += batch.length;
|
|
427
|
-
}
|
|
428
|
-
catch (error) {
|
|
429
|
-
if (skip_errors) {
|
|
430
|
-
// Try inserting rows one by one
|
|
431
|
-
for (let j = 0; j < batch.length; j++) {
|
|
432
|
-
try {
|
|
433
|
-
const rowValues = batch[j].map((val) => {
|
|
434
|
-
if (val === "" || val === null || val === undefined)
|
|
435
|
-
return "NULL";
|
|
436
|
-
return this.escapeValue(val);
|
|
437
|
-
});
|
|
438
|
-
const query = `INSERT INTO ${escapedTableName} (${escapedColumns}) VALUES (${rowValues.join(", ")})`;
|
|
439
|
-
await this.db.query(query);
|
|
440
|
-
queryCount++;
|
|
441
|
-
successCount++;
|
|
442
|
-
}
|
|
443
|
-
catch (rowError) {
|
|
444
|
-
errorCount++;
|
|
445
|
-
errors.push({
|
|
446
|
-
row: i + j + (has_headers ? 2 : 1),
|
|
447
|
-
error: rowError.message,
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
return {
|
|
454
|
-
status: "error",
|
|
455
|
-
error: `Import failed at row ${i + 1}: ${error.message}`,
|
|
456
|
-
data: { rows_imported: successCount },
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return {
|
|
462
|
-
status: errorCount > 0 ? "partial" : "success",
|
|
463
|
-
data: {
|
|
464
|
-
message: errorCount > 0
|
|
465
|
-
? `Import completed with ${errorCount} errors`
|
|
466
|
-
: "Import completed successfully",
|
|
467
|
-
rows_imported: successCount,
|
|
468
|
-
rows_failed: errorCount,
|
|
469
|
-
errors: errors.length > 0 ? errors.slice(0, 10) : undefined,
|
|
470
|
-
},
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
catch (error) {
|
|
474
|
-
return {
|
|
475
|
-
status: "error",
|
|
476
|
-
error: error.message,
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Parse CSV string into array of arrays
|
|
482
|
-
*/
|
|
483
|
-
parseCSV(csv) {
|
|
484
|
-
const rows = [];
|
|
485
|
-
const lines = csv.split(/\r?\n/);
|
|
486
|
-
for (const line of lines) {
|
|
487
|
-
if (!line.trim())
|
|
488
|
-
continue;
|
|
489
|
-
const row = [];
|
|
490
|
-
let current = "";
|
|
491
|
-
let inQuotes = false;
|
|
492
|
-
for (let i = 0; i < line.length; i++) {
|
|
493
|
-
const char = line[i];
|
|
494
|
-
const nextChar = line[i + 1];
|
|
495
|
-
if (inQuotes) {
|
|
496
|
-
if (char === '"' && nextChar === '"') {
|
|
497
|
-
current += '"';
|
|
498
|
-
i++; // Skip next quote
|
|
499
|
-
}
|
|
500
|
-
else if (char === '"') {
|
|
501
|
-
inQuotes = false;
|
|
502
|
-
}
|
|
503
|
-
else {
|
|
504
|
-
current += char;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
else {
|
|
508
|
-
if (char === '"') {
|
|
509
|
-
inQuotes = true;
|
|
510
|
-
}
|
|
511
|
-
else if (char === ",") {
|
|
512
|
-
row.push(current);
|
|
513
|
-
current = "";
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
current += char;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
row.push(current);
|
|
521
|
-
rows.push(row);
|
|
522
|
-
}
|
|
523
|
-
return rows;
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Import data from JSON string
|
|
527
|
-
*/
|
|
528
|
-
async importFromJSON(params) {
|
|
529
|
-
// Validate input parameters using the new validation function
|
|
530
|
-
const validation = (0, inputValidation_1.validateImportFromJSON)(params);
|
|
531
|
-
if (!validation.valid) {
|
|
532
|
-
return {
|
|
533
|
-
status: "error",
|
|
534
|
-
error: `Validation failed: ${validation.errors?.join(', ') || 'Invalid parameters'}`
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
try {
|
|
538
|
-
const { table_name, json_data, column_mapping, skip_errors = false, batch_size = 100, database, } = params;
|
|
539
|
-
// Validate database access
|
|
540
|
-
const dbValidation = this.validateDatabaseAccess(database);
|
|
541
|
-
if (!dbValidation.valid) {
|
|
542
|
-
return { status: "error", error: dbValidation.error };
|
|
543
|
-
}
|
|
544
|
-
// Validate table name using security layer
|
|
545
|
-
const tableValidation = this.security.validateIdentifier(table_name);
|
|
546
|
-
if (!tableValidation.valid) {
|
|
547
|
-
return { status: "error", error: tableValidation.error };
|
|
548
|
-
}
|
|
549
|
-
// Parse JSON - this should already be validated by validateImportFromJSON
|
|
550
|
-
let data;
|
|
551
|
-
try {
|
|
552
|
-
data = JSON.parse(json_data);
|
|
553
|
-
}
|
|
554
|
-
catch (e) {
|
|
555
|
-
return { status: "error", error: "Invalid JSON data" };
|
|
556
|
-
}
|
|
557
|
-
if (!Array.isArray(data)) {
|
|
558
|
-
return {
|
|
559
|
-
status: "error",
|
|
560
|
-
error: "JSON data must be an array of objects",
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
if (data.length === 0) {
|
|
564
|
-
return { status: "error", error: "JSON data is empty" };
|
|
565
|
-
}
|
|
566
|
-
// Get columns from first object
|
|
567
|
-
let columns = Object.keys(data[0]);
|
|
568
|
-
// Apply column mapping if provided
|
|
569
|
-
if (column_mapping) {
|
|
570
|
-
columns = columns.map((c) => column_mapping[c] || c);
|
|
571
|
-
}
|
|
572
|
-
// Validate all column names
|
|
573
|
-
for (const col of columns) {
|
|
574
|
-
const colValidation = this.security.validateIdentifier(col);
|
|
575
|
-
if (!colValidation.valid) {
|
|
576
|
-
return { status: "error", error: `Invalid column name: ${col}` };
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
580
|
-
const originalColumns = Object.keys(data[0]);
|
|
581
|
-
const escapedColumns = columns
|
|
582
|
-
.map((c) => this.security.escapeIdentifier(c))
|
|
583
|
-
.join(", ");
|
|
584
|
-
let successCount = 0;
|
|
585
|
-
let errorCount = 0;
|
|
586
|
-
const errors = [];
|
|
587
|
-
let queryCount = 0;
|
|
588
|
-
// Insert in batches
|
|
589
|
-
for (let i = 0; i < data.length; i += batch_size) {
|
|
590
|
-
const batch = data.slice(i, i + batch_size);
|
|
591
|
-
try {
|
|
592
|
-
const values = batch
|
|
593
|
-
.map((row) => {
|
|
594
|
-
const rowValues = originalColumns.map((col) => this.escapeValue(row[col]));
|
|
595
|
-
return `(${rowValues.join(", ")})`;
|
|
596
|
-
})
|
|
597
|
-
.join(", ");
|
|
598
|
-
const query = `INSERT INTO ${escapedTableName} (${escapedColumns}) VALUES ${values}`;
|
|
599
|
-
await this.db.query(query);
|
|
600
|
-
queryCount++;
|
|
601
|
-
successCount += batch.length;
|
|
602
|
-
}
|
|
603
|
-
catch (error) {
|
|
604
|
-
if (skip_errors) {
|
|
605
|
-
// Try inserting rows one by one
|
|
606
|
-
for (let j = 0; j < batch.length; j++) {
|
|
607
|
-
try {
|
|
608
|
-
const rowValues = originalColumns.map((col) => this.escapeValue(batch[j][col]));
|
|
609
|
-
const query = `INSERT INTO ${escapedTableName} (${escapedColumns}) VALUES (${rowValues.join(", ")})`;
|
|
610
|
-
await this.db.query(query);
|
|
611
|
-
queryCount++;
|
|
612
|
-
successCount++;
|
|
613
|
-
}
|
|
614
|
-
catch (rowError) {
|
|
615
|
-
errorCount++;
|
|
616
|
-
errors.push({ row: i + j + 1, error: rowError.message });
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
else {
|
|
621
|
-
return {
|
|
622
|
-
status: "error",
|
|
623
|
-
error: `Import failed at row ${i + 1}: ${error.message}`,
|
|
624
|
-
data: { rows_imported: successCount },
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return {
|
|
630
|
-
status: errorCount > 0 ? "partial" : "success",
|
|
631
|
-
data: {
|
|
632
|
-
message: errorCount > 0
|
|
633
|
-
? `Import completed with ${errorCount} errors`
|
|
634
|
-
: "Import completed successfully",
|
|
635
|
-
rows_imported: successCount,
|
|
636
|
-
rows_failed: errorCount,
|
|
637
|
-
errors: errors.length > 0 ? errors.slice(0, 10) : undefined,
|
|
638
|
-
},
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
catch (error) {
|
|
642
|
-
return {
|
|
643
|
-
status: "error",
|
|
644
|
-
error: error.message,
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
224
|
}
|
|
649
225
|
exports.DataExportTools = DataExportTools;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@berthojoris/mcp-mysql-server",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Model Context Protocol server for MySQL database integration with dynamic per-project permissions, backup/restore,
|
|
3
|
+
"version": "1.37.0",
|
|
4
|
+
"description": "Model Context Protocol server for MySQL database integration with dynamic per-project permissions, backup/restore, and schema migration capabilities",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "commonjs",
|