@berthojoris/mcp-mysql-server 1.2.6 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -692,11 +692,85 @@ const TOOLS = [
692
692
  required: ['procedure_name'],
693
693
  },
694
694
  },
695
+ // Data Export Tools
696
+ {
697
+ name: 'export_table_to_csv',
698
+ description: 'Export table data to CSV format with optional filtering, pagination, and sorting.',
699
+ inputSchema: {
700
+ type: 'object',
701
+ properties: {
702
+ table_name: {
703
+ type: 'string',
704
+ description: 'Name of the table to export',
705
+ },
706
+ filters: {
707
+ type: 'array',
708
+ description: 'Array of filter conditions',
709
+ items: {
710
+ type: 'object',
711
+ properties: {
712
+ field: { type: 'string' },
713
+ operator: {
714
+ type: 'string',
715
+ enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like', 'in']
716
+ },
717
+ value: {
718
+ description: 'Value to compare against (can be string, number, boolean, or array for "in" operator)'
719
+ },
720
+ },
721
+ required: ['field', 'operator', 'value'],
722
+ },
723
+ },
724
+ pagination: {
725
+ type: 'object',
726
+ properties: {
727
+ page: { type: 'number', description: 'Page number (starting from 1)' },
728
+ limit: { type: 'number', description: 'Number of records per page' },
729
+ },
730
+ },
731
+ sorting: {
732
+ type: 'object',
733
+ properties: {
734
+ field: { type: 'string', description: 'Field name to sort by' },
735
+ direction: { type: 'string', enum: ['asc', 'desc'] },
736
+ },
737
+ },
738
+ include_headers: {
739
+ type: 'boolean',
740
+ description: 'Whether to include column headers in the CSV output',
741
+ },
742
+ },
743
+ required: ['table_name'],
744
+ },
745
+ },
746
+ {
747
+ name: 'export_query_to_csv',
748
+ description: 'Export the results of a SELECT query to CSV format.',
749
+ inputSchema: {
750
+ type: 'object',
751
+ properties: {
752
+ query: {
753
+ type: 'string',
754
+ description: 'SQL SELECT query to execute and export',
755
+ },
756
+ params: {
757
+ type: 'array',
758
+ description: 'Optional array of parameters for parameterized queries',
759
+ items: {},
760
+ },
761
+ include_headers: {
762
+ type: 'boolean',
763
+ description: 'Whether to include column headers in the CSV output',
764
+ },
765
+ },
766
+ required: ['query'],
767
+ },
768
+ },
695
769
  ];
696
770
  // Create the MCP server
697
771
  const server = new index_js_1.Server({
698
772
  name: 'mysql-mcp-server',
699
- version: '1.0.0',
773
+ version: '1.4.1',
700
774
  }, {
701
775
  capabilities: {
702
776
  tools: {},
@@ -718,49 +792,49 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
718
792
  result = await mysqlMCP.listDatabases();
719
793
  break;
720
794
  case 'list_tables':
721
- result = await mysqlMCP.listTables(args);
795
+ result = await mysqlMCP.listTables((args || {}));
722
796
  break;
723
797
  case 'read_table_schema':
724
- result = await mysqlMCP.readTableSchema(args);
798
+ result = await mysqlMCP.readTableSchema((args || {}));
725
799
  break;
726
800
  case 'create_record':
727
- result = await mysqlMCP.createRecord(args);
801
+ result = await mysqlMCP.createRecord((args || {}));
728
802
  break;
729
803
  case 'read_records':
730
- result = await mysqlMCP.readRecords(args);
804
+ result = await mysqlMCP.readRecords((args || {}));
731
805
  break;
732
806
  case 'update_record':
733
- result = await mysqlMCP.updateRecord(args);
807
+ result = await mysqlMCP.updateRecord((args || {}));
734
808
  break;
735
809
  case 'delete_record':
736
- result = await mysqlMCP.deleteRecord(args);
810
+ result = await mysqlMCP.deleteRecord((args || {}));
737
811
  break;
738
812
  case 'bulk_insert':
739
- result = await mysqlMCP.bulkInsert(args);
813
+ result = await mysqlMCP.bulkInsert((args || {}));
740
814
  break;
741
815
  case 'bulk_update':
742
- result = await mysqlMCP.bulkUpdate(args);
816
+ result = await mysqlMCP.bulkUpdate((args || {}));
743
817
  break;
744
818
  case 'bulk_delete':
745
- result = await mysqlMCP.bulkDelete(args);
819
+ result = await mysqlMCP.bulkDelete((args || {}));
746
820
  break;
747
821
  case 'run_query':
748
- result = await mysqlMCP.runQuery(args);
822
+ result = await mysqlMCP.runQuery((args || {}));
749
823
  break;
750
824
  case 'execute_sql':
751
- result = await mysqlMCP.executeSql(args);
825
+ result = await mysqlMCP.executeSql((args || {}));
752
826
  break;
753
827
  case 'create_table':
754
- result = await mysqlMCP.createTable(args);
828
+ result = await mysqlMCP.createTable(args || {});
755
829
  break;
756
830
  case 'alter_table':
757
- result = await mysqlMCP.alterTable(args);
831
+ result = await mysqlMCP.alterTable(args || {});
758
832
  break;
759
833
  case 'drop_table':
760
- result = await mysqlMCP.dropTable(args);
834
+ result = await mysqlMCP.dropTable(args || {});
761
835
  break;
762
836
  case 'execute_ddl':
763
- result = await mysqlMCP.executeDdl(args);
837
+ result = await mysqlMCP.executeDdl((args || {}));
764
838
  break;
765
839
  case 'describe_connection':
766
840
  result = await mysqlMCP.describeConnection();
@@ -769,42 +843,49 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
769
843
  result = await mysqlMCP.testConnection();
770
844
  break;
771
845
  case 'get_table_relationships':
772
- result = await mysqlMCP.getTableRelationships(args);
846
+ result = await mysqlMCP.getTableRelationships((args || {}));
773
847
  break;
774
848
  // Transaction Tools
775
849
  case 'begin_transaction':
776
- result = await mysqlMCP.beginTransaction(args);
850
+ result = await mysqlMCP.beginTransaction((args || {}));
777
851
  break;
778
852
  case 'commit_transaction':
779
- result = await mysqlMCP.commitTransaction(args);
853
+ result = await mysqlMCP.commitTransaction((args || {}));
780
854
  break;
781
855
  case 'rollback_transaction':
782
- result = await mysqlMCP.rollbackTransaction(args);
856
+ result = await mysqlMCP.rollbackTransaction((args || {}));
783
857
  break;
784
858
  case 'get_transaction_status':
785
859
  result = await mysqlMCP.getTransactionStatus();
786
860
  break;
787
861
  case 'execute_in_transaction':
788
- result = await mysqlMCP.executeInTransaction(args);
862
+ result = await mysqlMCP.executeInTransaction((args || {}));
789
863
  break;
790
864
  // Stored Procedure Tools
791
865
  case 'list_stored_procedures':
792
- result = await mysqlMCP.listStoredProcedures(args);
866
+ result = await mysqlMCP.listStoredProcedures((args || {}));
793
867
  break;
794
868
  case 'get_stored_procedure_info':
795
- result = await mysqlMCP.getStoredProcedureInfo(args);
869
+ result = await mysqlMCP.getStoredProcedureInfo((args || {}));
796
870
  break;
797
871
  case 'execute_stored_procedure':
798
- result = await mysqlMCP.executeStoredProcedure(args);
872
+ result = await mysqlMCP.executeStoredProcedure((args || {}));
799
873
  break;
800
874
  case 'create_stored_procedure':
801
- result = await mysqlMCP.createStoredProcedure(args);
875
+ result = await mysqlMCP.createStoredProcedure((args || {}));
802
876
  break;
803
877
  case 'drop_stored_procedure':
804
- result = await mysqlMCP.dropStoredProcedure(args);
878
+ result = await mysqlMCP.dropStoredProcedure((args || {}));
805
879
  break;
806
880
  case 'show_create_procedure':
807
- result = await mysqlMCP.showCreateProcedure(args);
881
+ result = await mysqlMCP.showCreateProcedure((args || {}));
882
+ break;
883
+ // Data Export Tools
884
+ case 'export_table_to_csv':
885
+ result = await mysqlMCP.exportTableToCSV((args || {}));
886
+ break;
887
+ case 'export_query_to_csv':
888
+ result = await mysqlMCP.exportQueryToCSV((args || {}));
808
889
  break;
809
890
  default:
810
891
  throw new Error(`Unknown tool: ${name}`);
@@ -857,6 +938,19 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
857
938
  };
858
939
  }
859
940
  catch (error) {
941
+ // Check if this is a permission error
942
+ if (error.message && error.message.includes('Permission denied')) {
943
+ return {
944
+ content: [
945
+ {
946
+ type: 'text',
947
+ text: `❌ ${error.message}`,
948
+ },
949
+ ],
950
+ isError: true,
951
+ };
952
+ }
953
+ // Handle other errors with generic message
860
954
  return {
861
955
  content: [
862
956
  {
@@ -0,0 +1,33 @@
1
+ import { FilterCondition, Pagination, Sorting } from '../validation/schemas';
2
+ import SecurityLayer from '../security/securityLayer';
3
+ export declare class DataExportTools {
4
+ private db;
5
+ private security;
6
+ constructor(security: SecurityLayer);
7
+ /**
8
+ * Export table data to CSV format
9
+ */
10
+ exportTableToCSV(params: {
11
+ table_name: string;
12
+ filters?: FilterCondition[];
13
+ pagination?: Pagination;
14
+ sorting?: Sorting;
15
+ include_headers?: boolean;
16
+ }): Promise<{
17
+ status: string;
18
+ data?: any;
19
+ error?: string;
20
+ }>;
21
+ /**
22
+ * Export query results to CSV format
23
+ */
24
+ exportQueryToCSV(params: {
25
+ query: string;
26
+ params?: any[];
27
+ include_headers?: boolean;
28
+ }): Promise<{
29
+ status: string;
30
+ data?: any;
31
+ error?: string;
32
+ }>;
33
+ }
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DataExportTools = void 0;
7
+ const connection_1 = __importDefault(require("../db/connection"));
8
+ class DataExportTools {
9
+ constructor(security) {
10
+ this.db = connection_1.default.getInstance();
11
+ this.security = security;
12
+ }
13
+ /**
14
+ * Export table data to CSV format
15
+ */
16
+ async exportTableToCSV(params) {
17
+ try {
18
+ const { table_name, filters = [], pagination, sorting, include_headers = true } = params;
19
+ // Validate table name
20
+ const tableValidation = this.security.validateIdentifier(table_name);
21
+ if (!tableValidation.valid) {
22
+ return {
23
+ status: 'error',
24
+ error: tableValidation.error
25
+ };
26
+ }
27
+ // Build WHERE clause
28
+ let whereClause = '';
29
+ const whereParams = [];
30
+ if (filters && filters.length > 0) {
31
+ const whereConditions = [];
32
+ for (const filter of filters) {
33
+ // Validate field name
34
+ const fieldValidation = this.security.validateIdentifier(filter.field);
35
+ if (!fieldValidation.valid) {
36
+ return {
37
+ status: 'error',
38
+ error: `Invalid field name: ${filter.field}`
39
+ };
40
+ }
41
+ const fieldName = this.security.escapeIdentifier(filter.field);
42
+ switch (filter.operator) {
43
+ case 'eq':
44
+ whereConditions.push(`${fieldName} = ?`);
45
+ whereParams.push(filter.value);
46
+ break;
47
+ case 'neq':
48
+ whereConditions.push(`${fieldName} != ?`);
49
+ whereParams.push(filter.value);
50
+ break;
51
+ case 'gt':
52
+ whereConditions.push(`${fieldName} > ?`);
53
+ whereParams.push(filter.value);
54
+ break;
55
+ case 'gte':
56
+ whereConditions.push(`${fieldName} >= ?`);
57
+ whereParams.push(filter.value);
58
+ break;
59
+ case 'lt':
60
+ whereConditions.push(`${fieldName} < ?`);
61
+ whereParams.push(filter.value);
62
+ break;
63
+ case 'lte':
64
+ whereConditions.push(`${fieldName} <= ?`);
65
+ whereParams.push(filter.value);
66
+ break;
67
+ case 'like':
68
+ whereConditions.push(`${fieldName} LIKE ?`);
69
+ whereParams.push(filter.value);
70
+ break;
71
+ case 'in':
72
+ if (Array.isArray(filter.value)) {
73
+ const placeholders = filter.value.map(() => '?').join(', ');
74
+ whereConditions.push(`${fieldName} IN (${placeholders})`);
75
+ whereParams.push(...filter.value);
76
+ }
77
+ else {
78
+ return {
79
+ status: 'error',
80
+ error: 'IN operator requires an array of values'
81
+ };
82
+ }
83
+ break;
84
+ default:
85
+ return {
86
+ status: 'error',
87
+ error: `Unsupported operator: ${filter.operator}`
88
+ };
89
+ }
90
+ }
91
+ if (whereConditions.length > 0) {
92
+ whereClause = 'WHERE ' + whereConditions.join(' AND ');
93
+ }
94
+ }
95
+ // Build ORDER BY clause
96
+ let orderByClause = '';
97
+ if (sorting) {
98
+ const fieldValidation = this.security.validateIdentifier(sorting.field);
99
+ if (!fieldValidation.valid) {
100
+ return {
101
+ status: 'error',
102
+ error: `Invalid sort field name: ${sorting.field}`
103
+ };
104
+ }
105
+ const fieldName = this.security.escapeIdentifier(sorting.field);
106
+ const direction = sorting.direction.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
107
+ orderByClause = `ORDER BY ${fieldName} ${direction}`;
108
+ }
109
+ // Build LIMIT clause
110
+ let limitClause = '';
111
+ if (pagination) {
112
+ const offset = (pagination.page - 1) * pagination.limit;
113
+ limitClause = `LIMIT ${offset}, ${pagination.limit}`;
114
+ }
115
+ // Construct the query
116
+ const escapedTableName = this.security.escapeIdentifier(table_name);
117
+ const query = `SELECT * FROM ${escapedTableName} ${whereClause} ${orderByClause} ${limitClause}`;
118
+ // Execute query
119
+ const results = await this.db.query(query, whereParams);
120
+ // If no results, return empty CSV
121
+ if (results.length === 0) {
122
+ return {
123
+ status: 'success',
124
+ data: {
125
+ csv: include_headers ? '' : '',
126
+ row_count: 0
127
+ }
128
+ };
129
+ }
130
+ // Generate CSV
131
+ let csv = '';
132
+ // Add headers if requested
133
+ if (include_headers) {
134
+ const headers = Object.keys(results[0]).join(',');
135
+ csv += headers + '\n';
136
+ }
137
+ // Add data rows
138
+ for (const row of results) {
139
+ const values = Object.values(row).map(value => {
140
+ if (value === null)
141
+ return '';
142
+ if (typeof value === 'string') {
143
+ // Escape quotes and wrap in quotes if contains comma or newline
144
+ if (value.includes(',') || value.includes('\n') || value.includes('"')) {
145
+ return `"${value.replace(/"/g, '""')}"`;
146
+ }
147
+ return value;
148
+ }
149
+ return String(value);
150
+ }).join(',');
151
+ csv += values + '\n';
152
+ }
153
+ return {
154
+ status: 'success',
155
+ data: {
156
+ csv: csv,
157
+ row_count: results.length
158
+ }
159
+ };
160
+ }
161
+ catch (error) {
162
+ return {
163
+ status: 'error',
164
+ error: error.message
165
+ };
166
+ }
167
+ }
168
+ /**
169
+ * Export query results to CSV format
170
+ */
171
+ async exportQueryToCSV(params) {
172
+ try {
173
+ const { query, params: queryParams = [], include_headers = true } = params;
174
+ // Validate query is a SELECT statement
175
+ if (!this.security.isReadOnlyQuery(query)) {
176
+ return {
177
+ status: 'error',
178
+ error: 'Only SELECT queries can be exported to CSV'
179
+ };
180
+ }
181
+ // Validate parameters
182
+ const paramValidation = this.security.validateParameters(queryParams);
183
+ if (!paramValidation.valid) {
184
+ return {
185
+ status: 'error',
186
+ error: paramValidation.error
187
+ };
188
+ }
189
+ // Execute query
190
+ const results = await this.db.query(query, queryParams);
191
+ // If no results, return empty CSV
192
+ if (results.length === 0) {
193
+ return {
194
+ status: 'success',
195
+ data: {
196
+ csv: include_headers ? '' : '',
197
+ row_count: 0
198
+ }
199
+ };
200
+ }
201
+ // Generate CSV
202
+ let csv = '';
203
+ // Add headers if requested
204
+ if (include_headers) {
205
+ const headers = Object.keys(results[0]).join(',');
206
+ csv += headers + '\n';
207
+ }
208
+ // Add data rows
209
+ for (const row of results) {
210
+ const values = Object.values(row).map(value => {
211
+ if (value === null)
212
+ return '';
213
+ if (typeof value === 'string') {
214
+ // Escape quotes and wrap in quotes if contains comma or newline
215
+ if (value.includes(',') || value.includes('\n') || value.includes('"')) {
216
+ return `"${value.replace(/"/g, '""')}"`;
217
+ }
218
+ return value;
219
+ }
220
+ return String(value);
221
+ }).join(',');
222
+ csv += values + '\n';
223
+ }
224
+ return {
225
+ status: 'success',
226
+ data: {
227
+ csv: csv,
228
+ row_count: results.length
229
+ }
230
+ };
231
+ }
232
+ catch (error) {
233
+ return {
234
+ status: 'error',
235
+ error: error.message
236
+ };
237
+ }
238
+ }
239
+ }
240
+ exports.DataExportTools = DataExportTools;
@@ -512,6 +512,87 @@ export declare const showCreateProcedureSchema: {
512
512
  };
513
513
  additionalProperties: boolean;
514
514
  };
515
+ export declare const exportTableToCsvSchema: {
516
+ type: string;
517
+ required: string[];
518
+ properties: {
519
+ table_name: {
520
+ type: string;
521
+ };
522
+ filters: {
523
+ type: string;
524
+ items: {
525
+ type: string;
526
+ required: string[];
527
+ properties: {
528
+ field: {
529
+ type: string;
530
+ };
531
+ operator: {
532
+ type: string;
533
+ enum: string[];
534
+ };
535
+ value: {};
536
+ };
537
+ };
538
+ nullable: boolean;
539
+ };
540
+ pagination: {
541
+ type: string;
542
+ properties: {
543
+ page: {
544
+ type: string;
545
+ minimum: number;
546
+ };
547
+ limit: {
548
+ type: string;
549
+ minimum: number;
550
+ maximum: number;
551
+ };
552
+ };
553
+ required: string[];
554
+ nullable: boolean;
555
+ };
556
+ sorting: {
557
+ type: string;
558
+ properties: {
559
+ field: {
560
+ type: string;
561
+ };
562
+ direction: {
563
+ type: string;
564
+ enum: string[];
565
+ };
566
+ };
567
+ required: string[];
568
+ nullable: boolean;
569
+ };
570
+ include_headers: {
571
+ type: string;
572
+ nullable: boolean;
573
+ };
574
+ };
575
+ additionalProperties: boolean;
576
+ };
577
+ export declare const exportQueryToCsvSchema: {
578
+ type: string;
579
+ required: string[];
580
+ properties: {
581
+ query: {
582
+ type: string;
583
+ };
584
+ params: {
585
+ type: string;
586
+ items: {};
587
+ nullable: boolean;
588
+ };
589
+ include_headers: {
590
+ type: string;
591
+ nullable: boolean;
592
+ };
593
+ };
594
+ additionalProperties: boolean;
595
+ };
515
596
  export declare const validateListTables: import("ajv").ValidateFunction<{
516
597
  database: unknown;
517
598
  } & {}>;
@@ -596,3 +677,9 @@ export declare const validateStoredProcedureCreation: import("ajv").ValidateFunc
596
677
  export declare const validateGetTableRelationships: import("ajv").ValidateFunction<{
597
678
  [x: string]: {};
598
679
  }>;
680
+ export declare const validateExportTableToCsv: import("ajv").ValidateFunction<{
681
+ [x: string]: {};
682
+ }>;
683
+ export declare const validateExportQueryToCsv: import("ajv").ValidateFunction<{
684
+ [x: string]: {};
685
+ }>;