@imrieul/mysql-mcp-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.
package/dist/index.js ADDED
@@ -0,0 +1,686 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+
6
+ // src/config.ts
7
+ function parseConnectionString(connStr) {
8
+ let url;
9
+ try {
10
+ url = new URL(connStr);
11
+ } catch {
12
+ throw new Error(`Invalid connection string: ${connStr}`);
13
+ }
14
+ if (url.protocol !== "mysql:") {
15
+ throw new Error(`Expected mysql:// protocol, got ${url.protocol}`);
16
+ }
17
+ const database = url.pathname.slice(1) || void 0;
18
+ return {
19
+ host: url.hostname,
20
+ port: url.port ? parseInt(url.port, 10) : 3306,
21
+ user: decodeURIComponent(url.username),
22
+ password: decodeURIComponent(url.password),
23
+ database
24
+ };
25
+ }
26
+ function parseEnvConfig(env) {
27
+ const host = env.MYSQL_HOST;
28
+ const user = env.MYSQL_USER;
29
+ const password = env.MYSQL_PASSWORD;
30
+ if (!host) throw new Error("Missing required environment variable: MYSQL_HOST");
31
+ if (!user) throw new Error("Missing required environment variable: MYSQL_USER");
32
+ if (!password) throw new Error("Missing required environment variable: MYSQL_PASSWORD");
33
+ return {
34
+ host,
35
+ port: env.MYSQL_PORT ? parseInt(env.MYSQL_PORT, 10) : 3306,
36
+ user,
37
+ password,
38
+ database: env.MYSQL_DATABASE || void 0
39
+ };
40
+ }
41
+ function parseArgs(argv) {
42
+ let connectionString;
43
+ let readonly = false;
44
+ for (const arg of argv) {
45
+ if (arg === "--readonly") {
46
+ readonly = true;
47
+ } else if (arg.startsWith("mysql://")) {
48
+ connectionString = arg;
49
+ }
50
+ }
51
+ return { connectionString, readonly };
52
+ }
53
+ function resolveConfig(argv, env = process.env) {
54
+ const args = parseArgs(argv);
55
+ const mysql2 = args.connectionString ? parseConnectionString(args.connectionString) : parseEnvConfig(env);
56
+ const readonly = args.readonly || env.MYSQL_READONLY === "true";
57
+ const maxRows = env.MYSQL_MAX_ROWS ? parseInt(env.MYSQL_MAX_ROWS, 10) : 100;
58
+ const queryTimeout = env.MYSQL_QUERY_TIMEOUT !== void 0 ? parseInt(env.MYSQL_QUERY_TIMEOUT, 10) : 3e4;
59
+ const ssl = env.MYSQL_SSL === "true";
60
+ return { mysql: mysql2, readonly, maxRows, queryTimeout, ssl };
61
+ }
62
+
63
+ // src/connection.ts
64
+ import mysql from "mysql2/promise";
65
+ function createConnectionManager(config, options) {
66
+ const poolOptions = {
67
+ host: config.host,
68
+ port: config.port,
69
+ user: config.user,
70
+ password: config.password,
71
+ database: config.database,
72
+ connectionLimit: 5,
73
+ waitForConnections: true
74
+ };
75
+ if (options?.ssl) {
76
+ poolOptions.ssl = { rejectUnauthorized: true };
77
+ }
78
+ const pool = mysql.createPool(poolOptions);
79
+ return {
80
+ getPool() {
81
+ return pool;
82
+ },
83
+ async close() {
84
+ await pool.end();
85
+ }
86
+ };
87
+ }
88
+
89
+ // src/query-runner.ts
90
+ var QueryTimeoutError = class extends Error {
91
+ constructor(timeoutMs) {
92
+ super(`Query timeout after ${timeoutMs}ms`);
93
+ this.name = "QueryTimeoutError";
94
+ }
95
+ };
96
+ function withTimeout(promise, timeoutMs) {
97
+ if (timeoutMs <= 0) return promise;
98
+ return Promise.race([
99
+ promise,
100
+ new Promise((_, reject) => setTimeout(() => reject(new QueryTimeoutError(timeoutMs)), timeoutMs))
101
+ ]);
102
+ }
103
+ function createQueryRunner(pool, options) {
104
+ const { readonly, queryTimeout } = options;
105
+ return {
106
+ async query(sql) {
107
+ if (!readonly) {
108
+ return withTimeout(pool.query(sql), queryTimeout);
109
+ }
110
+ const conn = await pool.getConnection();
111
+ try {
112
+ await conn.query("SET SESSION TRANSACTION READ ONLY");
113
+ await conn.beginTransaction();
114
+ const result = await withTimeout(conn.query(sql), queryTimeout);
115
+ await conn.rollback();
116
+ return result;
117
+ } catch (error) {
118
+ try {
119
+ await conn.rollback();
120
+ } catch {
121
+ }
122
+ throw error;
123
+ } finally {
124
+ conn.release();
125
+ }
126
+ },
127
+ async withConnection(fn) {
128
+ const conn = await pool.getConnection();
129
+ try {
130
+ if (readonly) {
131
+ await conn.query("SET SESSION TRANSACTION READ ONLY");
132
+ await conn.beginTransaction();
133
+ }
134
+ const queryFn = (sql) => withTimeout(conn.query(sql), queryTimeout);
135
+ const result = await fn(queryFn);
136
+ if (readonly) {
137
+ await conn.rollback();
138
+ }
139
+ return result;
140
+ } catch (error) {
141
+ if (readonly) {
142
+ try {
143
+ await conn.rollback();
144
+ } catch {
145
+ }
146
+ }
147
+ throw error;
148
+ } finally {
149
+ conn.release();
150
+ }
151
+ }
152
+ };
153
+ }
154
+
155
+ // src/server.ts
156
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
157
+
158
+ // src/tools/query.ts
159
+ import { z } from "zod";
160
+
161
+ // src/tools/error-hint.ts
162
+ var HINTS = [
163
+ [/Table '.*' doesn't exist/i, "Hint: use list_tables to check available tables."],
164
+ [/Unknown column/i, "Hint: use describe_table to check column names."],
165
+ [/Unknown database/i, "Hint: use list_databases to check available databases."],
166
+ [/Access denied/i, "Hint: check database user permissions."],
167
+ [/ECONNREFUSED|ETIMEDOUT|ENOTFOUND/i, "Hint: check MYSQL_HOST and MYSQL_PORT settings."],
168
+ [/ER_PARSE_ERROR/i, "Hint: check SQL syntax."],
169
+ [/Query timeout/i, "Hint: increase MYSQL_QUERY_TIMEOUT or optimize the query."]
170
+ ];
171
+ function formatError(error) {
172
+ const message = error instanceof Error ? error.message : String(error);
173
+ const hint = HINTS.find(([pattern]) => pattern.test(message));
174
+ return hint ? `Error: ${message}
175
+ ${hint[1]}` : `Error: ${message}`;
176
+ }
177
+
178
+ // src/tools/query.ts
179
+ var queryToolName = "query";
180
+ var queryToolConfig = {
181
+ title: "Query",
182
+ description: "Execute a read-only SQL query (SELECT, SHOW, DESCRIBE, EXPLAIN).",
183
+ inputSchema: {
184
+ sql: z.string().describe(
185
+ "The SQL query to execute. Only SELECT, SHOW, DESCRIBE, EXPLAIN, and WITH (CTE) statements are allowed."
186
+ )
187
+ }
188
+ };
189
+ var ALLOWED_PREFIXES = ["SELECT", "SHOW", "DESCRIBE", "EXPLAIN", "WITH"];
190
+ var LIMIT_PREFIXES = ["SELECT", "WITH"];
191
+ function needsLimit(normalized) {
192
+ if (!LIMIT_PREFIXES.some((p) => normalized.startsWith(p))) return false;
193
+ return !/\bLIMIT\b/i.test(normalized);
194
+ }
195
+ function createQueryHandler(runner, maxRows) {
196
+ return async ({ sql }) => {
197
+ const normalized = sql.trim().toUpperCase();
198
+ const isAllowed = ALLOWED_PREFIXES.some((prefix) => normalized.startsWith(prefix));
199
+ if (!isAllowed) {
200
+ return {
201
+ isError: true,
202
+ content: [
203
+ {
204
+ type: "text",
205
+ text: 'Error: Only SELECT, SHOW, DESCRIBE, EXPLAIN queries are allowed. Use the "execute" tool for data modification.'
206
+ }
207
+ ]
208
+ };
209
+ }
210
+ try {
211
+ const finalSql = needsLimit(normalized) ? `${sql.trim()} LIMIT ${maxRows}` : sql;
212
+ const [rows] = await runner.query(finalSql);
213
+ const arr = rows;
214
+ if (arr.length === 0) {
215
+ return { content: [{ type: "text", text: "(empty)" }] };
216
+ }
217
+ const cols = Object.keys(arr[0]);
218
+ const data = arr.map((r) => cols.map((c) => r[c]));
219
+ return {
220
+ content: [{ type: "text", text: JSON.stringify({ columns: cols, rows: data }) }]
221
+ };
222
+ } catch (error) {
223
+ return {
224
+ isError: true,
225
+ content: [{ type: "text", text: formatError(error) }]
226
+ };
227
+ }
228
+ };
229
+ }
230
+
231
+ // src/tools/execute.ts
232
+ import { z as z2 } from "zod";
233
+ var executeToolName = "execute";
234
+ var executeToolConfig = {
235
+ title: "Execute",
236
+ description: "Execute a data modification SQL statement (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, etc.).",
237
+ inputSchema: {
238
+ sql: z2.string().describe('The SQL statement to execute. SELECT statements are not allowed here; use the "query" tool instead.')
239
+ }
240
+ };
241
+ function createExecuteHandler(runner, isReadonly) {
242
+ return async ({ sql }) => {
243
+ if (isReadonly) {
244
+ return {
245
+ isError: true,
246
+ content: [
247
+ {
248
+ type: "text",
249
+ text: "Error: Server is in read-only mode. Data modification is not allowed."
250
+ }
251
+ ]
252
+ };
253
+ }
254
+ const normalized = sql.trim().toUpperCase();
255
+ if (normalized.startsWith("SELECT") || normalized.startsWith("WITH")) {
256
+ return {
257
+ isError: true,
258
+ content: [
259
+ {
260
+ type: "text",
261
+ text: 'Error: Use the "query" tool for SELECT statements.'
262
+ }
263
+ ]
264
+ };
265
+ }
266
+ try {
267
+ const [result] = await runner.query(sql);
268
+ const r = result;
269
+ return {
270
+ content: [
271
+ { type: "text", text: `affectedRows: ${r.affectedRows}, changedRows: ${r.changedRows ?? 0}` }
272
+ ]
273
+ };
274
+ } catch (error) {
275
+ return {
276
+ isError: true,
277
+ content: [{ type: "text", text: formatError(error) }]
278
+ };
279
+ }
280
+ };
281
+ }
282
+
283
+ // src/tools/list-databases.ts
284
+ var listDatabasesToolName = "list_databases";
285
+ var listDatabasesToolConfig = {
286
+ title: "List Databases",
287
+ description: "List all databases on the MySQL server."
288
+ };
289
+ function createListDatabasesHandler(runner) {
290
+ return async () => {
291
+ try {
292
+ const [rows] = await runner.query("SHOW DATABASES");
293
+ const databases = rows.map((r) => Object.values(r)[0]);
294
+ return {
295
+ content: [{ type: "text", text: JSON.stringify(databases) }]
296
+ };
297
+ } catch (error) {
298
+ return {
299
+ isError: true,
300
+ content: [{ type: "text", text: formatError(error) }]
301
+ };
302
+ }
303
+ };
304
+ }
305
+
306
+ // src/tools/list-tables.ts
307
+ import { z as z3 } from "zod";
308
+
309
+ // src/tools/resolve-database.ts
310
+ async function resolveDatabase(query, database) {
311
+ if (database) return database;
312
+ const [rows] = await query("SELECT DATABASE() AS db");
313
+ const db = rows[0]?.db;
314
+ return typeof db === "string" ? db : null;
315
+ }
316
+
317
+ // src/tools/sql-escape.ts
318
+ function escapeStringValue(value) {
319
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "''");
320
+ }
321
+ function quoteStringValue(value) {
322
+ return `'${escapeStringValue(value)}'`;
323
+ }
324
+
325
+ // src/tools/list-tables.ts
326
+ var listTablesToolName = "list_tables";
327
+ var listTablesToolConfig = {
328
+ title: "List Tables",
329
+ description: "List all tables in the specified database (or the current database if not specified).",
330
+ inputSchema: {
331
+ database: z3.string().optional().describe("Database name. Uses the current database if omitted.")
332
+ }
333
+ };
334
+ function createListTablesHandler(runner) {
335
+ return async ({ database }) => {
336
+ try {
337
+ return await runner.withConnection(async (query) => {
338
+ const db = await resolveDatabase(query, database);
339
+ if (!db) {
340
+ return {
341
+ isError: true,
342
+ content: [
343
+ {
344
+ type: "text",
345
+ text: "Error: No database selected. Specify a database name or set MYSQL_DATABASE."
346
+ }
347
+ ]
348
+ };
349
+ }
350
+ const sql = `SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = ${quoteStringValue(db)} ORDER BY TABLE_NAME`;
351
+ const [rows] = await query(sql);
352
+ const lines = rows.map((r) => {
353
+ const comment = r.TABLE_COMMENT;
354
+ return comment ? `${r.TABLE_NAME} -- ${comment}` : r.TABLE_NAME;
355
+ });
356
+ return {
357
+ content: [{ type: "text", text: lines.join("\n") }]
358
+ };
359
+ });
360
+ } catch (error) {
361
+ return {
362
+ isError: true,
363
+ content: [{ type: "text", text: formatError(error) }]
364
+ };
365
+ }
366
+ };
367
+ }
368
+
369
+ // src/tools/describe-table.ts
370
+ import { z as z4 } from "zod";
371
+
372
+ // src/tools/format-column.ts
373
+ function formatColumn(col) {
374
+ let result = `${col.name} ${col.type}`;
375
+ if (!col.nullable) result += " NOT NULL";
376
+ if (col.key === "PRI") result += " PK";
377
+ else if (col.key === "UNI") result += " UNIQUE";
378
+ else if (col.key === "MUL") result += " INDEX";
379
+ if (col.defaultValue != null) result += ` DEFAULT ${col.defaultValue}`;
380
+ if (col.extra) result += ` ${col.extra}`;
381
+ if (col.comment) result += ` -- ${col.comment}`;
382
+ return result;
383
+ }
384
+
385
+ // src/tools/describe-table.ts
386
+ var describeTableToolName = "describe_table";
387
+ var describeTableToolConfig = {
388
+ title: "Describe Table",
389
+ description: "Show the schema/structure of a table, including column names, types, constraints, and comments.",
390
+ inputSchema: {
391
+ table: z4.string().describe("Table name to describe."),
392
+ database: z4.string().optional().describe("Database name. Uses the current database if omitted.")
393
+ }
394
+ };
395
+ function createDescribeTableHandler(runner) {
396
+ return async ({ table, database }) => {
397
+ try {
398
+ return await runner.withConnection(async (query) => {
399
+ const db = await resolveDatabase(query, database);
400
+ if (!db) {
401
+ return {
402
+ isError: true,
403
+ content: [
404
+ {
405
+ type: "text",
406
+ text: "Error: No database selected. Specify a database name or set MYSQL_DATABASE."
407
+ }
408
+ ]
409
+ };
410
+ }
411
+ const sql = `SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, EXTRA, COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ${quoteStringValue(db)} AND TABLE_NAME = ${quoteStringValue(table)} ORDER BY ORDINAL_POSITION`;
412
+ const [rows] = await query(sql);
413
+ const typedRows = rows;
414
+ if (typedRows.length === 0) {
415
+ return {
416
+ isError: true,
417
+ content: [
418
+ {
419
+ type: "text",
420
+ text: `Error: Table '${table}' doesn't exist in database '${db}'.
421
+ Hint: use list_tables to check available tables.`
422
+ }
423
+ ]
424
+ };
425
+ }
426
+ const columns = typedRows.map(
427
+ (r) => formatColumn({
428
+ name: String(r.COLUMN_NAME),
429
+ type: String(r.COLUMN_TYPE),
430
+ nullable: r.IS_NULLABLE === "YES",
431
+ key: String(r.COLUMN_KEY ?? ""),
432
+ defaultValue: r.COLUMN_DEFAULT,
433
+ extra: String(r.EXTRA ?? ""),
434
+ comment: String(r.COLUMN_COMMENT ?? "")
435
+ })
436
+ );
437
+ return {
438
+ content: [{ type: "text", text: `${table}:
439
+ ${columns.join("\n")}` }]
440
+ };
441
+ });
442
+ } catch (error) {
443
+ return {
444
+ isError: true,
445
+ content: [{ type: "text", text: formatError(error) }]
446
+ };
447
+ }
448
+ };
449
+ }
450
+
451
+ // src/tools/describe-all-tables.ts
452
+ import { z as z5 } from "zod";
453
+ var describeAllTablesToolName = "describe_all_tables";
454
+ var describeAllTablesToolConfig = {
455
+ title: "Describe All Tables",
456
+ description: "Show the schema of all tables at once. Much more efficient than calling describe_table for each table individually.",
457
+ inputSchema: {
458
+ database: z5.string().optional().describe("Database name. Uses the current database if omitted.")
459
+ }
460
+ };
461
+ function createDescribeAllTablesHandler(runner) {
462
+ return async ({ database }) => {
463
+ try {
464
+ return await runner.withConnection(async (query) => {
465
+ const db = await resolveDatabase(query, database);
466
+ if (!db) {
467
+ return {
468
+ isError: true,
469
+ content: [
470
+ {
471
+ type: "text",
472
+ text: "Error: No database selected. Specify a database name or set MYSQL_DATABASE."
473
+ }
474
+ ]
475
+ };
476
+ }
477
+ const sql = `SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, EXTRA, COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ${quoteStringValue(db)} ORDER BY TABLE_NAME, ORDINAL_POSITION`;
478
+ const [rows] = await query(sql);
479
+ const typedRows = rows;
480
+ if (typedRows.length === 0) {
481
+ return { content: [{ type: "text", text: "(no tables)" }] };
482
+ }
483
+ const tableMap = /* @__PURE__ */ new Map();
484
+ for (const r of typedRows) {
485
+ const tableName = String(r.TABLE_NAME);
486
+ const line = formatColumn({
487
+ name: String(r.COLUMN_NAME),
488
+ type: String(r.COLUMN_TYPE),
489
+ nullable: r.IS_NULLABLE === "YES",
490
+ key: String(r.COLUMN_KEY ?? ""),
491
+ defaultValue: r.COLUMN_DEFAULT,
492
+ extra: String(r.EXTRA ?? ""),
493
+ comment: String(r.COLUMN_COMMENT ?? "")
494
+ });
495
+ if (!tableMap.has(tableName)) {
496
+ tableMap.set(tableName, []);
497
+ }
498
+ tableMap.get(tableName).push(line);
499
+ }
500
+ const parts = Array.from(tableMap.entries()).map(([table, lines]) => `${table}:
501
+ ${lines.join("\n")}`);
502
+ return {
503
+ content: [{ type: "text", text: parts.join("\n\n") }]
504
+ };
505
+ });
506
+ } catch (error) {
507
+ return {
508
+ isError: true,
509
+ content: [{ type: "text", text: formatError(error) }]
510
+ };
511
+ }
512
+ };
513
+ }
514
+
515
+ // src/tools/add-comment.ts
516
+ import { z as z6 } from "zod";
517
+ var addCommentToolName = "add_comment";
518
+ var addCommentToolConfig = {
519
+ title: "Add Comment",
520
+ description: "Safely add a comment to a table or column. This tool only modifies comments \u2014 it cannot alter table structure, column types, or data.",
521
+ inputSchema: {
522
+ table: z6.string().describe("Table name."),
523
+ column: z6.string().optional().describe("Column name. If omitted, sets a table-level comment."),
524
+ comment: z6.string().describe("Comment text to set."),
525
+ database: z6.string().optional().describe("Database name. Uses the current database if omitted.")
526
+ }
527
+ };
528
+ function createAddCommentHandler(runner, isReadonly) {
529
+ return async ({
530
+ table,
531
+ column,
532
+ comment,
533
+ database
534
+ }) => {
535
+ if (isReadonly) {
536
+ return {
537
+ isError: true,
538
+ content: [
539
+ {
540
+ type: "text",
541
+ text: "Error: Server is in read-only mode. Modifying comments is not allowed."
542
+ }
543
+ ]
544
+ };
545
+ }
546
+ try {
547
+ return await runner.withConnection(async (query) => {
548
+ const db = await resolveDatabase(query, database);
549
+ if (!db) {
550
+ return {
551
+ isError: true,
552
+ content: [
553
+ {
554
+ type: "text",
555
+ text: "Error: No database selected. Specify a database name or set MYSQL_DATABASE."
556
+ }
557
+ ]
558
+ };
559
+ }
560
+ const fullName = `\`${db}\`.\`${table}\``;
561
+ const escapedComment = escapeStringValue(comment);
562
+ if (!column) {
563
+ const sql = `ALTER TABLE ${fullName} COMMENT = '${escapedComment}'`;
564
+ await query(sql);
565
+ return {
566
+ content: [{ type: "text", text: `Table comment updated: ${table}` }]
567
+ };
568
+ }
569
+ const colSql = `SELECT COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, EXTRA FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ${quoteStringValue(db)} AND TABLE_NAME = ${quoteStringValue(table)} AND COLUMN_NAME = ${quoteStringValue(column)}`;
570
+ const [rows] = await query(colSql);
571
+ const colInfo = rows[0];
572
+ if (!colInfo) {
573
+ return {
574
+ isError: true,
575
+ content: [
576
+ {
577
+ type: "text",
578
+ text: `Error: Column '${column}' doesn't exist in table '${table}'.
579
+ Hint: use describe_table to check available columns.`
580
+ }
581
+ ]
582
+ };
583
+ }
584
+ let modifySql = `ALTER TABLE ${fullName} MODIFY COLUMN \`${column}\` ${colInfo.COLUMN_TYPE}`;
585
+ if (colInfo.IS_NULLABLE === "NO") modifySql += " NOT NULL";
586
+ if (colInfo.COLUMN_DEFAULT != null)
587
+ modifySql += ` DEFAULT '${escapeStringValue(String(colInfo.COLUMN_DEFAULT))}'`;
588
+ if (colInfo.EXTRA) modifySql += ` ${colInfo.EXTRA}`;
589
+ modifySql += ` COMMENT '${escapedComment}'`;
590
+ await query(modifySql);
591
+ return {
592
+ content: [{ type: "text", text: `Column comment updated: ${table}.${column}` }]
593
+ };
594
+ });
595
+ } catch (error) {
596
+ return {
597
+ isError: true,
598
+ content: [{ type: "text", text: formatError(error) }]
599
+ };
600
+ }
601
+ };
602
+ }
603
+
604
+ // src/tools/index.ts
605
+ function registerAllTools(server, runner, readonly, maxRows) {
606
+ server.tool(
607
+ queryToolName,
608
+ queryToolConfig.description,
609
+ queryToolConfig.inputSchema,
610
+ createQueryHandler(runner, maxRows)
611
+ );
612
+ server.tool(
613
+ executeToolName,
614
+ executeToolConfig.description,
615
+ executeToolConfig.inputSchema,
616
+ createExecuteHandler(runner, readonly)
617
+ );
618
+ server.tool(listDatabasesToolName, listDatabasesToolConfig.description, createListDatabasesHandler(runner));
619
+ server.tool(
620
+ listTablesToolName,
621
+ listTablesToolConfig.description,
622
+ listTablesToolConfig.inputSchema,
623
+ createListTablesHandler(runner)
624
+ );
625
+ server.tool(
626
+ describeTableToolName,
627
+ describeTableToolConfig.description,
628
+ describeTableToolConfig.inputSchema,
629
+ createDescribeTableHandler(runner)
630
+ );
631
+ server.tool(
632
+ describeAllTablesToolName,
633
+ describeAllTablesToolConfig.description,
634
+ describeAllTablesToolConfig.inputSchema,
635
+ createDescribeAllTablesHandler(runner)
636
+ );
637
+ server.tool(
638
+ addCommentToolName,
639
+ addCommentToolConfig.description,
640
+ addCommentToolConfig.inputSchema,
641
+ createAddCommentHandler(runner, readonly)
642
+ );
643
+ }
644
+
645
+ // src/server.ts
646
+ function createMcpServer(runner, readonly, maxRows) {
647
+ const server = new McpServer({
648
+ name: "@imrieul/mysql-mcp-server",
649
+ version: "0.1.0"
650
+ });
651
+ registerAllTools(server, runner, readonly, maxRows);
652
+ return server;
653
+ }
654
+
655
+ // src/index.ts
656
+ async function main() {
657
+ const config = resolveConfig(process.argv.slice(2));
658
+ const connectionManager = createConnectionManager(config.mysql, { ssl: config.ssl });
659
+ const runner = createQueryRunner(connectionManager.getPool(), {
660
+ readonly: config.readonly,
661
+ queryTimeout: config.queryTimeout
662
+ });
663
+ const server = createMcpServer(runner, config.readonly, config.maxRows);
664
+ const transport = new StdioServerTransport();
665
+ process.on("SIGINT", async () => {
666
+ await server.close();
667
+ await connectionManager.close();
668
+ process.exit(0);
669
+ });
670
+ process.on("SIGTERM", async () => {
671
+ await server.close();
672
+ await connectionManager.close();
673
+ process.exit(0);
674
+ });
675
+ console.error("MySQL MCP Server starting...");
676
+ console.error(`Readonly mode: ${config.readonly}`);
677
+ console.error(`Query timeout: ${config.queryTimeout}ms`);
678
+ console.error(`SSL: ${config.ssl}`);
679
+ await server.connect(transport);
680
+ console.error("MySQL MCP Server connected via stdio");
681
+ }
682
+ main().catch((error) => {
683
+ console.error("Fatal error:", error);
684
+ process.exit(1);
685
+ });
686
+ //# sourceMappingURL=index.js.map