@berthojoris/mcp-mysql-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,748 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
+ const index_js_2 = require("./index.js");
8
+ // Get permissions from environment variable (set by bin/mcp-mysql.js)
9
+ // Priority: MCP_PERMISSIONS (from command line) > MCP_CONFIG (from .env) > all permissions
10
+ const permissions = process.env.MCP_PERMISSIONS || process.env.MCP_CONFIG || '';
11
+ // Initialize the MySQL MCP instance with permissions
12
+ const mysqlMCP = new index_js_2.MySQLMCP(permissions);
13
+ // Log the effective permissions to stderr
14
+ if (permissions) {
15
+ console.error(`Active permissions: ${permissions}`);
16
+ }
17
+ else {
18
+ console.error('Active permissions: all (default)');
19
+ }
20
+ // Define all available tools with their schemas
21
+ const TOOLS = [
22
+ {
23
+ name: 'list_databases',
24
+ description: 'Lists all databases available on the MySQL server.',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {},
28
+ },
29
+ },
30
+ {
31
+ name: 'list_tables',
32
+ description: 'Lists all tables in the connected MySQL database.',
33
+ inputSchema: {
34
+ type: 'object',
35
+ properties: {
36
+ database: {
37
+ type: 'string',
38
+ description: 'Optional: specific database name to list tables from',
39
+ },
40
+ },
41
+ },
42
+ },
43
+ {
44
+ name: 'read_table_schema',
45
+ description: 'Reads the schema of a specified table, including columns, types, keys, and indexes.',
46
+ inputSchema: {
47
+ type: 'object',
48
+ properties: {
49
+ table_name: {
50
+ type: 'string',
51
+ description: 'Name of the table to read schema from',
52
+ },
53
+ },
54
+ required: ['table_name'],
55
+ },
56
+ },
57
+ {
58
+ name: 'create_record',
59
+ description: 'Creates a new record in the specified table.',
60
+ inputSchema: {
61
+ type: 'object',
62
+ properties: {
63
+ table_name: {
64
+ type: 'string',
65
+ description: 'Name of the table to insert into',
66
+ },
67
+ data: {
68
+ type: 'object',
69
+ description: 'Object containing column names and values to insert',
70
+ },
71
+ },
72
+ required: ['table_name', 'data'],
73
+ },
74
+ },
75
+ {
76
+ name: 'read_records',
77
+ description: 'Reads records from the specified table with optional filtering, pagination, and sorting.',
78
+ inputSchema: {
79
+ type: 'object',
80
+ properties: {
81
+ table_name: {
82
+ type: 'string',
83
+ description: 'Name of the table to read from',
84
+ },
85
+ filters: {
86
+ type: 'array',
87
+ description: 'Array of filter conditions',
88
+ items: {
89
+ type: 'object',
90
+ properties: {
91
+ field: { type: 'string' },
92
+ operator: {
93
+ type: 'string',
94
+ enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like', 'in']
95
+ },
96
+ value: {
97
+ description: 'Value to compare against (can be string, number, boolean, or array for "in" operator)'
98
+ },
99
+ },
100
+ required: ['field', 'operator', 'value'],
101
+ },
102
+ },
103
+ pagination: {
104
+ type: 'object',
105
+ properties: {
106
+ page: { type: 'number', description: 'Page number (starting from 1)' },
107
+ limit: { type: 'number', description: 'Number of records per page' },
108
+ },
109
+ },
110
+ sorting: {
111
+ type: 'object',
112
+ properties: {
113
+ field: { type: 'string', description: 'Field name to sort by' },
114
+ direction: { type: 'string', enum: ['asc', 'desc'] },
115
+ },
116
+ },
117
+ },
118
+ required: ['table_name'],
119
+ },
120
+ },
121
+ {
122
+ name: 'update_record',
123
+ description: 'Updates existing records in the specified table based on conditions.',
124
+ inputSchema: {
125
+ type: 'object',
126
+ properties: {
127
+ table_name: {
128
+ type: 'string',
129
+ description: 'Name of the table to update',
130
+ },
131
+ data: {
132
+ type: 'object',
133
+ description: 'Object containing column names and new values',
134
+ },
135
+ conditions: {
136
+ type: 'array',
137
+ description: 'Array of conditions to identify which records to update',
138
+ items: {
139
+ type: 'object',
140
+ properties: {
141
+ field: { type: 'string' },
142
+ operator: {
143
+ type: 'string',
144
+ enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like', 'in']
145
+ },
146
+ value: {},
147
+ },
148
+ required: ['field', 'operator', 'value'],
149
+ },
150
+ },
151
+ },
152
+ required: ['table_name', 'data', 'conditions'],
153
+ },
154
+ },
155
+ {
156
+ name: 'delete_record',
157
+ description: 'Deletes records from the specified table based on conditions.',
158
+ inputSchema: {
159
+ type: 'object',
160
+ properties: {
161
+ table_name: {
162
+ type: 'string',
163
+ description: 'Name of the table to delete from',
164
+ },
165
+ conditions: {
166
+ type: 'array',
167
+ description: 'Array of conditions to identify which records to delete',
168
+ items: {
169
+ type: 'object',
170
+ properties: {
171
+ field: { type: 'string' },
172
+ operator: {
173
+ type: 'string',
174
+ enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like', 'in']
175
+ },
176
+ value: {},
177
+ },
178
+ required: ['field', 'operator', 'value'],
179
+ },
180
+ },
181
+ },
182
+ required: ['table_name', 'conditions'],
183
+ },
184
+ },
185
+ {
186
+ name: 'run_query',
187
+ description: 'Runs a read-only SQL SELECT query with optional parameters. Only SELECT statements are allowed.',
188
+ inputSchema: {
189
+ type: 'object',
190
+ properties: {
191
+ query: {
192
+ type: 'string',
193
+ description: 'SQL SELECT query to execute',
194
+ },
195
+ params: {
196
+ type: 'array',
197
+ description: 'Optional array of parameters for parameterized queries',
198
+ items: {},
199
+ },
200
+ },
201
+ required: ['query'],
202
+ },
203
+ },
204
+ {
205
+ name: 'execute_sql',
206
+ description: 'Executes a write SQL operation (INSERT, UPDATE, DELETE) with optional parameters. DDL operations require "ddl" permission.',
207
+ inputSchema: {
208
+ type: 'object',
209
+ properties: {
210
+ query: {
211
+ type: 'string',
212
+ description: 'SQL query to execute (INSERT, UPDATE, DELETE, or DDL if permitted)',
213
+ },
214
+ params: {
215
+ type: 'array',
216
+ description: 'Optional array of parameters for parameterized queries',
217
+ items: {},
218
+ },
219
+ },
220
+ required: ['query'],
221
+ },
222
+ },
223
+ {
224
+ name: 'create_table',
225
+ description: 'Creates a new table with the specified columns and indexes. Requires "ddl" permission.',
226
+ inputSchema: {
227
+ type: 'object',
228
+ properties: {
229
+ table_name: {
230
+ type: 'string',
231
+ description: 'Name of the table to create',
232
+ },
233
+ columns: {
234
+ type: 'array',
235
+ description: 'Array of column definitions',
236
+ items: {
237
+ type: 'object',
238
+ properties: {
239
+ name: { type: 'string', description: 'Column name' },
240
+ type: { type: 'string', description: 'MySQL data type (e.g., VARCHAR(255), INT, TEXT)' },
241
+ nullable: { type: 'boolean', description: 'Whether column can be NULL' },
242
+ primary_key: { type: 'boolean', description: 'Whether this is the primary key' },
243
+ auto_increment: { type: 'boolean', description: 'Whether column auto-increments' },
244
+ default: { type: 'string', description: 'Default value' },
245
+ },
246
+ required: ['name', 'type'],
247
+ },
248
+ },
249
+ indexes: {
250
+ type: 'array',
251
+ description: 'Optional indexes to create',
252
+ items: {
253
+ type: 'object',
254
+ properties: {
255
+ name: { type: 'string' },
256
+ columns: { type: 'array', items: { type: 'string' } },
257
+ unique: { type: 'boolean' },
258
+ },
259
+ },
260
+ },
261
+ },
262
+ required: ['table_name', 'columns'],
263
+ },
264
+ },
265
+ {
266
+ name: 'alter_table',
267
+ description: 'Alters an existing table structure (add/drop/modify columns, add/drop indexes). Requires "ddl" permission.',
268
+ inputSchema: {
269
+ type: 'object',
270
+ properties: {
271
+ table_name: {
272
+ type: 'string',
273
+ description: 'Name of the table to alter',
274
+ },
275
+ operations: {
276
+ type: 'array',
277
+ description: 'Array of alter operations to perform',
278
+ items: {
279
+ type: 'object',
280
+ properties: {
281
+ type: {
282
+ type: 'string',
283
+ enum: ['add_column', 'drop_column', 'modify_column', 'rename_column', 'add_index', 'drop_index'],
284
+ description: 'Type of alteration',
285
+ },
286
+ column_name: { type: 'string' },
287
+ new_column_name: { type: 'string' },
288
+ column_type: { type: 'string' },
289
+ nullable: { type: 'boolean' },
290
+ default: { type: 'string' },
291
+ index_name: { type: 'string' },
292
+ index_columns: { type: 'array', items: { type: 'string' } },
293
+ unique: { type: 'boolean' },
294
+ },
295
+ required: ['type'],
296
+ },
297
+ },
298
+ },
299
+ required: ['table_name', 'operations'],
300
+ },
301
+ },
302
+ {
303
+ name: 'drop_table',
304
+ description: 'Drops (deletes) a table and all its data. Requires "ddl" permission. WARNING: This is irreversible!',
305
+ inputSchema: {
306
+ type: 'object',
307
+ properties: {
308
+ table_name: {
309
+ type: 'string',
310
+ description: 'Name of the table to drop',
311
+ },
312
+ if_exists: {
313
+ type: 'boolean',
314
+ description: 'If true, will not error if table does not exist',
315
+ },
316
+ },
317
+ required: ['table_name'],
318
+ },
319
+ },
320
+ {
321
+ name: 'execute_ddl',
322
+ description: 'Executes raw DDL SQL (CREATE, ALTER, DROP, TRUNCATE, RENAME). Requires "ddl" permission.',
323
+ inputSchema: {
324
+ type: 'object',
325
+ properties: {
326
+ query: {
327
+ type: 'string',
328
+ description: 'DDL SQL query to execute',
329
+ },
330
+ },
331
+ required: ['query'],
332
+ },
333
+ },
334
+ {
335
+ name: 'describe_connection',
336
+ description: 'Returns information about the current database connection.',
337
+ inputSchema: {
338
+ type: 'object',
339
+ properties: {},
340
+ },
341
+ },
342
+ {
343
+ name: 'test_connection',
344
+ description: 'Tests the database connection and returns latency information.',
345
+ inputSchema: {
346
+ type: 'object',
347
+ properties: {},
348
+ },
349
+ },
350
+ {
351
+ name: 'get_table_relationships',
352
+ description: 'Returns foreign key relationships for a specified table.',
353
+ inputSchema: {
354
+ type: 'object',
355
+ properties: {
356
+ table_name: {
357
+ type: 'string',
358
+ description: 'Name of the table to get relationships for',
359
+ },
360
+ },
361
+ required: ['table_name'],
362
+ },
363
+ },
364
+ // Transaction Tools
365
+ {
366
+ name: 'begin_transaction',
367
+ description: 'Begins a new database transaction. Returns a transaction ID for subsequent operations.',
368
+ inputSchema: {
369
+ type: 'object',
370
+ properties: {
371
+ transactionId: {
372
+ type: 'string',
373
+ description: 'Optional custom transaction ID. If not provided, one will be generated.',
374
+ },
375
+ },
376
+ },
377
+ },
378
+ {
379
+ name: 'commit_transaction',
380
+ description: 'Commits a transaction and makes all changes permanent.',
381
+ inputSchema: {
382
+ type: 'object',
383
+ properties: {
384
+ transactionId: {
385
+ type: 'string',
386
+ description: 'The transaction ID to commit',
387
+ },
388
+ },
389
+ required: ['transactionId'],
390
+ },
391
+ },
392
+ {
393
+ name: 'rollback_transaction',
394
+ description: 'Rolls back a transaction and undoes all changes made within it.',
395
+ inputSchema: {
396
+ type: 'object',
397
+ properties: {
398
+ transactionId: {
399
+ type: 'string',
400
+ description: 'The transaction ID to rollback',
401
+ },
402
+ },
403
+ required: ['transactionId'],
404
+ },
405
+ },
406
+ {
407
+ name: 'get_transaction_status',
408
+ description: 'Returns the status of all active transactions.',
409
+ inputSchema: {
410
+ type: 'object',
411
+ properties: {},
412
+ },
413
+ },
414
+ {
415
+ name: 'execute_in_transaction',
416
+ description: 'Executes a SQL query within an active transaction.',
417
+ inputSchema: {
418
+ type: 'object',
419
+ properties: {
420
+ transactionId: {
421
+ type: 'string',
422
+ description: 'The transaction ID to execute the query within',
423
+ },
424
+ query: {
425
+ type: 'string',
426
+ description: 'SQL query to execute within the transaction',
427
+ },
428
+ params: {
429
+ type: 'array',
430
+ description: 'Optional array of parameters for parameterized queries',
431
+ items: {},
432
+ },
433
+ },
434
+ required: ['transactionId', 'query'],
435
+ },
436
+ },
437
+ // Stored Procedure Tools
438
+ {
439
+ name: 'list_stored_procedures',
440
+ description: 'Lists all stored procedures in the specified database.',
441
+ inputSchema: {
442
+ type: 'object',
443
+ properties: {
444
+ database: {
445
+ type: 'string',
446
+ description: 'Optional: specific database name to list procedures from',
447
+ },
448
+ },
449
+ },
450
+ },
451
+ {
452
+ name: 'get_stored_procedure_info',
453
+ description: 'Gets detailed information about a specific stored procedure including parameters and metadata.',
454
+ inputSchema: {
455
+ type: 'object',
456
+ properties: {
457
+ procedure_name: {
458
+ type: 'string',
459
+ description: 'Name of the stored procedure to get information for',
460
+ },
461
+ database: {
462
+ type: 'string',
463
+ description: 'Optional: specific database name',
464
+ },
465
+ },
466
+ required: ['procedure_name'],
467
+ },
468
+ },
469
+ {
470
+ name: 'execute_stored_procedure',
471
+ description: 'Executes a stored procedure with optional parameters.',
472
+ inputSchema: {
473
+ type: 'object',
474
+ properties: {
475
+ procedure_name: {
476
+ type: 'string',
477
+ description: 'Name of the stored procedure to execute',
478
+ },
479
+ parameters: {
480
+ type: 'array',
481
+ description: 'Optional array of parameters to pass to the stored procedure',
482
+ items: {},
483
+ },
484
+ database: {
485
+ type: 'string',
486
+ description: 'Optional: specific database name',
487
+ },
488
+ },
489
+ required: ['procedure_name'],
490
+ },
491
+ },
492
+ {
493
+ name: 'create_stored_procedure',
494
+ description: 'Creates a new stored procedure with the specified parameters and body.',
495
+ inputSchema: {
496
+ type: 'object',
497
+ properties: {
498
+ procedure_name: {
499
+ type: 'string',
500
+ description: 'Name of the stored procedure to create',
501
+ },
502
+ parameters: {
503
+ type: 'array',
504
+ description: 'Optional array of parameter definitions',
505
+ items: {
506
+ type: 'object',
507
+ properties: {
508
+ name: { type: 'string', description: 'Parameter name' },
509
+ mode: { type: 'string', enum: ['IN', 'OUT', 'INOUT'], description: 'Parameter mode' },
510
+ data_type: { type: 'string', description: 'MySQL data type (e.g., VARCHAR(255), INT)' },
511
+ },
512
+ required: ['name', 'mode', 'data_type'],
513
+ },
514
+ },
515
+ body: {
516
+ type: 'string',
517
+ description: 'SQL body of the stored procedure',
518
+ },
519
+ comment: {
520
+ type: 'string',
521
+ description: 'Optional comment for the stored procedure',
522
+ },
523
+ database: {
524
+ type: 'string',
525
+ description: 'Optional: specific database name',
526
+ },
527
+ },
528
+ required: ['procedure_name', 'body'],
529
+ },
530
+ },
531
+ {
532
+ name: 'drop_stored_procedure',
533
+ description: 'Drops (deletes) a stored procedure. WARNING: This is irreversible!',
534
+ inputSchema: {
535
+ type: 'object',
536
+ properties: {
537
+ procedure_name: {
538
+ type: 'string',
539
+ description: 'Name of the stored procedure to drop',
540
+ },
541
+ if_exists: {
542
+ type: 'boolean',
543
+ description: 'If true, will not error if procedure does not exist',
544
+ },
545
+ database: {
546
+ type: 'string',
547
+ description: 'Optional: specific database name',
548
+ },
549
+ },
550
+ required: ['procedure_name'],
551
+ },
552
+ },
553
+ {
554
+ name: 'show_create_procedure',
555
+ description: 'Shows the CREATE statement for a stored procedure.',
556
+ inputSchema: {
557
+ type: 'object',
558
+ properties: {
559
+ procedure_name: {
560
+ type: 'string',
561
+ description: 'Name of the stored procedure to show CREATE statement for',
562
+ },
563
+ database: {
564
+ type: 'string',
565
+ description: 'Optional: specific database name',
566
+ },
567
+ },
568
+ required: ['procedure_name'],
569
+ },
570
+ },
571
+ ];
572
+ // Create the MCP server
573
+ const server = new index_js_1.Server({
574
+ name: 'mysql-mcp-server',
575
+ version: '1.0.0',
576
+ }, {
577
+ capabilities: {
578
+ tools: {},
579
+ },
580
+ });
581
+ // Handle list tools request
582
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
583
+ return {
584
+ tools: TOOLS,
585
+ };
586
+ });
587
+ // Handle tool call requests
588
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
589
+ const { name, arguments: args } = request.params;
590
+ try {
591
+ let result;
592
+ switch (name) {
593
+ case 'list_databases':
594
+ result = await mysqlMCP.listDatabases();
595
+ break;
596
+ case 'list_tables':
597
+ result = await mysqlMCP.listTables(args);
598
+ break;
599
+ case 'read_table_schema':
600
+ result = await mysqlMCP.readTableSchema(args);
601
+ break;
602
+ case 'create_record':
603
+ result = await mysqlMCP.createRecord(args);
604
+ break;
605
+ case 'read_records':
606
+ result = await mysqlMCP.readRecords(args);
607
+ break;
608
+ case 'update_record':
609
+ result = await mysqlMCP.updateRecord(args);
610
+ break;
611
+ case 'delete_record':
612
+ result = await mysqlMCP.deleteRecord(args);
613
+ break;
614
+ case 'run_query':
615
+ result = await mysqlMCP.runQuery(args);
616
+ break;
617
+ case 'execute_sql':
618
+ result = await mysqlMCP.executeSql(args);
619
+ break;
620
+ case 'create_table':
621
+ result = await mysqlMCP.createTable(args);
622
+ break;
623
+ case 'alter_table':
624
+ result = await mysqlMCP.alterTable(args);
625
+ break;
626
+ case 'drop_table':
627
+ result = await mysqlMCP.dropTable(args);
628
+ break;
629
+ case 'execute_ddl':
630
+ result = await mysqlMCP.executeDdl(args);
631
+ break;
632
+ case 'describe_connection':
633
+ result = await mysqlMCP.describeConnection();
634
+ break;
635
+ case 'test_connection':
636
+ result = await mysqlMCP.testConnection();
637
+ break;
638
+ case 'get_table_relationships':
639
+ result = await mysqlMCP.getTableRelationships(args);
640
+ break;
641
+ // Transaction Tools
642
+ case 'begin_transaction':
643
+ result = await mysqlMCP.beginTransaction(args);
644
+ break;
645
+ case 'commit_transaction':
646
+ result = await mysqlMCP.commitTransaction(args);
647
+ break;
648
+ case 'rollback_transaction':
649
+ result = await mysqlMCP.rollbackTransaction(args);
650
+ break;
651
+ case 'get_transaction_status':
652
+ result = await mysqlMCP.getTransactionStatus();
653
+ break;
654
+ case 'execute_in_transaction':
655
+ result = await mysqlMCP.executeInTransaction(args);
656
+ break;
657
+ // Stored Procedure Tools
658
+ case 'list_stored_procedures':
659
+ result = await mysqlMCP.listStoredProcedures(args);
660
+ break;
661
+ case 'get_stored_procedure_info':
662
+ result = await mysqlMCP.getStoredProcedureInfo(args);
663
+ break;
664
+ case 'execute_stored_procedure':
665
+ result = await mysqlMCP.executeStoredProcedure(args);
666
+ break;
667
+ case 'create_stored_procedure':
668
+ result = await mysqlMCP.createStoredProcedure(args);
669
+ break;
670
+ case 'drop_stored_procedure':
671
+ result = await mysqlMCP.dropStoredProcedure(args);
672
+ break;
673
+ case 'show_create_procedure':
674
+ result = await mysqlMCP.showCreateProcedure(args);
675
+ break;
676
+ default:
677
+ throw new Error(`Unknown tool: ${name}`);
678
+ }
679
+ // Handle the result based on status
680
+ if (result.status === 'error') {
681
+ return {
682
+ content: [
683
+ {
684
+ type: 'text',
685
+ text: `Error: ${result.error}`,
686
+ },
687
+ ],
688
+ isError: true,
689
+ };
690
+ }
691
+ // Return successful result - handle different result types
692
+ let responseData;
693
+ if ('data' in result) {
694
+ // Standard result with data property
695
+ responseData = result.data;
696
+ }
697
+ else if ('transactionId' in result) {
698
+ // Transaction result
699
+ responseData = {
700
+ transactionId: result.transactionId
701
+ };
702
+ if ('message' in result && result.message) {
703
+ responseData.message = result.message;
704
+ }
705
+ if ('activeTransactions' in result && result.activeTransactions) {
706
+ responseData.activeTransactions = result.activeTransactions;
707
+ }
708
+ }
709
+ else if ('message' in result) {
710
+ // Simple message result
711
+ responseData = { message: result.message };
712
+ }
713
+ else {
714
+ // Fallback
715
+ responseData = result;
716
+ }
717
+ return {
718
+ content: [
719
+ {
720
+ type: 'text',
721
+ text: JSON.stringify(responseData, null, 2),
722
+ },
723
+ ],
724
+ };
725
+ }
726
+ catch (error) {
727
+ return {
728
+ content: [
729
+ {
730
+ type: 'text',
731
+ text: `Error executing tool: ${error.message}`,
732
+ },
733
+ ],
734
+ isError: true,
735
+ };
736
+ }
737
+ });
738
+ // Start the server
739
+ async function main() {
740
+ const transport = new stdio_js_1.StdioServerTransport();
741
+ await server.connect(transport);
742
+ // Log to stderr (not stdout, which is used for MCP protocol)
743
+ console.error('MySQL MCP Server running on stdio');
744
+ }
745
+ main().catch((error) => {
746
+ console.error('Fatal error in main():', error);
747
+ process.exit(1);
748
+ });