@xingyuchen/mysql-mcp-server 3.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/README.md +562 -0
- package/README_ENHANCED.md +276 -0
- package/SMITHERY_DEPLOY.md +391 -0
- package/dist/connection-manager.js +159 -0
- package/dist/database.js +400 -0
- package/dist/index.js +632 -0
- package/dist/log-viewer.js +249 -0
- package/dist/logger.js +122 -0
- package/dist/transaction-manager.js +247 -0
- package/package.json +75 -0
- package/smithery.yaml +18 -0
package/dist/index.js
ADDED
@@ -0,0 +1,632 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from "@modelcontextprotocol/sdk/types.js";
|
5
|
+
import { ConnectionManager } from "./connection-manager.js";
|
6
|
+
import { logger } from "./logger.js";
|
7
|
+
const server = new Server({
|
8
|
+
name: "mysql-mcp-server",
|
9
|
+
version: "0.1.0",
|
10
|
+
}, {
|
11
|
+
capabilities: {
|
12
|
+
tools: {},
|
13
|
+
},
|
14
|
+
});
|
15
|
+
// 全局连接管理器实例
|
16
|
+
const connectionManager = new ConnectionManager();
|
17
|
+
// 辅助函数:获取数据库管理器
|
18
|
+
function getTargetManager(connection_id) {
|
19
|
+
const targetManager = connection_id
|
20
|
+
? connectionManager.getConnection(connection_id)
|
21
|
+
: connectionManager.getActiveConnection();
|
22
|
+
if (!targetManager || !targetManager.isConnected()) {
|
23
|
+
const errorMsg = connection_id
|
24
|
+
? `❌ 连接 '${connection_id}' 不存在或未连接`
|
25
|
+
: "❌ 没有活跃的数据库连接,请先使用 connect_database 工具连接到数据库";
|
26
|
+
throw new McpError(ErrorCode.InvalidRequest, errorMsg);
|
27
|
+
}
|
28
|
+
return targetManager;
|
29
|
+
}
|
30
|
+
// 列出可用工具
|
31
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
32
|
+
return {
|
33
|
+
tools: [
|
34
|
+
{
|
35
|
+
name: "connect_database",
|
36
|
+
description: "连接到MySQL数据库",
|
37
|
+
inputSchema: {
|
38
|
+
type: "object",
|
39
|
+
properties: {
|
40
|
+
host: {
|
41
|
+
type: "string",
|
42
|
+
description: "数据库主机地址(例如:localhost 或 127.0.0.1)",
|
43
|
+
},
|
44
|
+
port: {
|
45
|
+
type: "number",
|
46
|
+
description: "数据库端口号(默认:3306)",
|
47
|
+
default: 3306,
|
48
|
+
},
|
49
|
+
user: {
|
50
|
+
type: "string",
|
51
|
+
description: "数据库用户名",
|
52
|
+
},
|
53
|
+
password: {
|
54
|
+
type: "string",
|
55
|
+
description: "数据库密码",
|
56
|
+
},
|
57
|
+
database: {
|
58
|
+
type: "string",
|
59
|
+
description: "要连接的数据库名称",
|
60
|
+
},
|
61
|
+
connection_id: {
|
62
|
+
type: "string",
|
63
|
+
description: "连接标识符(可选,用于管理多个数据库连接)",
|
64
|
+
},
|
65
|
+
},
|
66
|
+
required: ["host", "user", "password", "database"],
|
67
|
+
},
|
68
|
+
},
|
69
|
+
{
|
70
|
+
name: "execute_query",
|
71
|
+
description: "执行SQL查询语句(支持增删改查所有操作)",
|
72
|
+
inputSchema: {
|
73
|
+
type: "object",
|
74
|
+
properties: {
|
75
|
+
query: {
|
76
|
+
type: "string",
|
77
|
+
description: "要执行的SQL查询语句",
|
78
|
+
},
|
79
|
+
params: {
|
80
|
+
type: "array",
|
81
|
+
description: "SQL参数(可选,用于参数化查询)",
|
82
|
+
items: {
|
83
|
+
type: "string"
|
84
|
+
}
|
85
|
+
},
|
86
|
+
connection_id: {
|
87
|
+
type: "string",
|
88
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
89
|
+
},
|
90
|
+
},
|
91
|
+
required: ["query"],
|
92
|
+
},
|
93
|
+
},
|
94
|
+
{
|
95
|
+
name: "begin_transaction",
|
96
|
+
description: "开始数据库事务",
|
97
|
+
inputSchema: {
|
98
|
+
type: "object",
|
99
|
+
properties: {
|
100
|
+
connection_id: {
|
101
|
+
type: "string",
|
102
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
103
|
+
},
|
104
|
+
},
|
105
|
+
},
|
106
|
+
},
|
107
|
+
{
|
108
|
+
name: "commit_transaction",
|
109
|
+
description: "提交数据库事务",
|
110
|
+
inputSchema: {
|
111
|
+
type: "object",
|
112
|
+
properties: {
|
113
|
+
connection_id: {
|
114
|
+
type: "string",
|
115
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
116
|
+
},
|
117
|
+
},
|
118
|
+
},
|
119
|
+
},
|
120
|
+
{
|
121
|
+
name: "rollback_transaction",
|
122
|
+
description: "回滚数据库事务",
|
123
|
+
inputSchema: {
|
124
|
+
type: "object",
|
125
|
+
properties: {
|
126
|
+
connection_id: {
|
127
|
+
type: "string",
|
128
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
129
|
+
},
|
130
|
+
},
|
131
|
+
},
|
132
|
+
},
|
133
|
+
{
|
134
|
+
name: "show_transaction_history",
|
135
|
+
description: "显示当前事务的操作历史",
|
136
|
+
inputSchema: {
|
137
|
+
type: "object",
|
138
|
+
properties: {
|
139
|
+
connection_id: {
|
140
|
+
type: "string",
|
141
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
142
|
+
},
|
143
|
+
},
|
144
|
+
},
|
145
|
+
},
|
146
|
+
{
|
147
|
+
name: "rollback_to_step",
|
148
|
+
description: "回滚到指定的操作步骤",
|
149
|
+
inputSchema: {
|
150
|
+
type: "object",
|
151
|
+
properties: {
|
152
|
+
step_number: {
|
153
|
+
type: "number",
|
154
|
+
description: "要回滚到的步骤号(从操作历史中选择)",
|
155
|
+
},
|
156
|
+
connection_id: {
|
157
|
+
type: "string",
|
158
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
159
|
+
},
|
160
|
+
},
|
161
|
+
required: ["step_number"],
|
162
|
+
},
|
163
|
+
},
|
164
|
+
{
|
165
|
+
name: "full_rollback",
|
166
|
+
description: "完全回滚当前事务的所有操作",
|
167
|
+
inputSchema: {
|
168
|
+
type: "object",
|
169
|
+
properties: {
|
170
|
+
connection_id: {
|
171
|
+
type: "string",
|
172
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
173
|
+
},
|
174
|
+
},
|
175
|
+
},
|
176
|
+
},
|
177
|
+
{
|
178
|
+
name: "show_tables",
|
179
|
+
description: "显示数据库中的所有表及其结构信息",
|
180
|
+
inputSchema: {
|
181
|
+
type: "object",
|
182
|
+
properties: {
|
183
|
+
connection_id: {
|
184
|
+
type: "string",
|
185
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
186
|
+
},
|
187
|
+
},
|
188
|
+
},
|
189
|
+
},
|
190
|
+
{
|
191
|
+
name: "describe_table",
|
192
|
+
description: "显示指定表的详细结构信息和样本数据",
|
193
|
+
inputSchema: {
|
194
|
+
type: "object",
|
195
|
+
properties: {
|
196
|
+
table_name: {
|
197
|
+
type: "string",
|
198
|
+
description: "要查看结构的表名",
|
199
|
+
},
|
200
|
+
connection_id: {
|
201
|
+
type: "string",
|
202
|
+
description: "连接标识符(可选,不指定则使用当前活跃连接)",
|
203
|
+
},
|
204
|
+
},
|
205
|
+
required: ["table_name"],
|
206
|
+
},
|
207
|
+
},
|
208
|
+
{
|
209
|
+
name: "disconnect_database",
|
210
|
+
description: "断开数据库连接",
|
211
|
+
inputSchema: {
|
212
|
+
type: "object",
|
213
|
+
properties: {
|
214
|
+
connection_id: {
|
215
|
+
type: "string",
|
216
|
+
description: "要断开的连接标识符(可选,不指定则断开当前活跃连接)",
|
217
|
+
},
|
218
|
+
},
|
219
|
+
},
|
220
|
+
},
|
221
|
+
{
|
222
|
+
name: "list_connections",
|
223
|
+
description: "列出所有数据库连接",
|
224
|
+
inputSchema: {
|
225
|
+
type: "object",
|
226
|
+
properties: {},
|
227
|
+
},
|
228
|
+
},
|
229
|
+
{
|
230
|
+
name: "switch_active_connection",
|
231
|
+
description: "切换当前活跃的数据库连接",
|
232
|
+
inputSchema: {
|
233
|
+
type: "object",
|
234
|
+
properties: {
|
235
|
+
connection_id: {
|
236
|
+
type: "string",
|
237
|
+
description: "要切换到的连接标识符",
|
238
|
+
},
|
239
|
+
},
|
240
|
+
required: ["connection_id"],
|
241
|
+
},
|
242
|
+
},
|
243
|
+
{
|
244
|
+
name: "remove_connection",
|
245
|
+
description: "移除指定的数据库连接",
|
246
|
+
inputSchema: {
|
247
|
+
type: "object",
|
248
|
+
properties: {
|
249
|
+
connection_id: {
|
250
|
+
type: "string",
|
251
|
+
description: "要移除的连接标识符",
|
252
|
+
},
|
253
|
+
},
|
254
|
+
required: ["connection_id"],
|
255
|
+
},
|
256
|
+
},
|
257
|
+
],
|
258
|
+
};
|
259
|
+
});
|
260
|
+
// 处理工具调用
|
261
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
262
|
+
const { name, arguments: args } = request.params;
|
263
|
+
// 记录工具调用
|
264
|
+
logger.info(`工具调用开始`, { tool: name, args });
|
265
|
+
try {
|
266
|
+
switch (name) {
|
267
|
+
case "connect_database": {
|
268
|
+
const { host, port = 3306, user, password, database, connection_id } = args;
|
269
|
+
// 生成连接ID(如果未提供)
|
270
|
+
const connId = connection_id || `${host}_${database}_${Date.now()}`;
|
271
|
+
// 添加新连接
|
272
|
+
await connectionManager.addConnection(connId, { host, port, user, password, database });
|
273
|
+
const totalConnections = connectionManager.getConnectionCount();
|
274
|
+
const isActive = connectionManager.getActiveConnectionId() === connId;
|
275
|
+
return {
|
276
|
+
content: [
|
277
|
+
{
|
278
|
+
type: "text",
|
279
|
+
text: `✅ 成功连接到MySQL数据库!\n📍 连接ID: ${connId}\n📍 主机: ${host}:${port}\n🗄️ 数据库: ${database}\n👤 用户: ${user}\n🎯 活跃连接: ${isActive ? '是' : '否'}\n📊 总连接数: ${totalConnections}`,
|
280
|
+
},
|
281
|
+
],
|
282
|
+
};
|
283
|
+
}
|
284
|
+
case "execute_query": {
|
285
|
+
const { query, params = [], connection_id } = args;
|
286
|
+
// 获取目标数据库管理器
|
287
|
+
const targetManager = connection_id
|
288
|
+
? connectionManager.getConnection(connection_id)
|
289
|
+
: connectionManager.getActiveConnection();
|
290
|
+
if (!targetManager || !targetManager.isConnected()) {
|
291
|
+
const errorMsg = connection_id
|
292
|
+
? `❌ 连接 '${connection_id}' 不存在或未连接`
|
293
|
+
: "❌ 没有活跃的数据库连接,请先使用 connect_database 工具连接到数据库";
|
294
|
+
throw new McpError(ErrorCode.InvalidRequest, errorMsg);
|
295
|
+
}
|
296
|
+
const result = await targetManager.executeQuery(query, params);
|
297
|
+
const activeConnId = connectionManager.getActiveConnectionId();
|
298
|
+
const usedConnId = connection_id || activeConnId;
|
299
|
+
return {
|
300
|
+
content: [
|
301
|
+
{
|
302
|
+
type: "text",
|
303
|
+
text: `✅ SQL执行成功!\n🔗 使用连接: ${usedConnId}\n📊 操作类型: ${result.type}\n⏱️ 执行时间: ${result.duration}ms\n\n📋 结果:\n${JSON.stringify(result, null, 2)}`,
|
304
|
+
},
|
305
|
+
],
|
306
|
+
};
|
307
|
+
}
|
308
|
+
case "begin_transaction": {
|
309
|
+
const { connection_id } = args;
|
310
|
+
const targetManager = getTargetManager(connection_id);
|
311
|
+
await targetManager.beginTransaction();
|
312
|
+
return {
|
313
|
+
content: [
|
314
|
+
{
|
315
|
+
type: "text",
|
316
|
+
text: `✅ 事务已开始!\n🔗 连接: ${connection_id || connectionManager.getActiveConnectionId()}\n\n⚠️ 请记得在操作完成后提交或回滚事务`,
|
317
|
+
},
|
318
|
+
],
|
319
|
+
};
|
320
|
+
}
|
321
|
+
case "commit_transaction": {
|
322
|
+
const { connection_id } = args;
|
323
|
+
const targetManager = getTargetManager(connection_id);
|
324
|
+
const transactionManager = targetManager.getTransactionManager();
|
325
|
+
const result = await transactionManager.commitTransaction(async () => {
|
326
|
+
return await targetManager.commitTransaction();
|
327
|
+
});
|
328
|
+
return {
|
329
|
+
content: [
|
330
|
+
{
|
331
|
+
type: "text",
|
332
|
+
text: result,
|
333
|
+
},
|
334
|
+
],
|
335
|
+
};
|
336
|
+
}
|
337
|
+
case "rollback_transaction": {
|
338
|
+
const { connection_id } = args;
|
339
|
+
const targetManager = getTargetManager(connection_id);
|
340
|
+
const transactionManager = targetManager.getTransactionManager();
|
341
|
+
const result = await transactionManager.fullRollback(async (query, params) => {
|
342
|
+
return await targetManager.executeQuery(query, params || []);
|
343
|
+
});
|
344
|
+
return {
|
345
|
+
content: [
|
346
|
+
{
|
347
|
+
type: "text",
|
348
|
+
text: result,
|
349
|
+
},
|
350
|
+
],
|
351
|
+
};
|
352
|
+
}
|
353
|
+
case "show_transaction_history": {
|
354
|
+
const { connection_id } = args;
|
355
|
+
const targetManager = getTargetManager(connection_id);
|
356
|
+
const transactionManager = targetManager.getTransactionManager();
|
357
|
+
const historyText = transactionManager.getRollbackOptions();
|
358
|
+
return {
|
359
|
+
content: [
|
360
|
+
{
|
361
|
+
type: "text",
|
362
|
+
text: `📋 事务操作历史\n🔗 连接: ${connection_id || connectionManager.getActiveConnectionId()}\n\n${historyText}`,
|
363
|
+
},
|
364
|
+
],
|
365
|
+
};
|
366
|
+
}
|
367
|
+
case "rollback_to_step": {
|
368
|
+
const { step_number, connection_id } = args;
|
369
|
+
const targetManager = getTargetManager(connection_id);
|
370
|
+
const transactionManager = targetManager.getTransactionManager();
|
371
|
+
const result = await transactionManager.rollbackToStep(step_number, async (query, params) => {
|
372
|
+
return await targetManager.executeQuery(query, params || []);
|
373
|
+
});
|
374
|
+
return {
|
375
|
+
content: [
|
376
|
+
{
|
377
|
+
type: "text",
|
378
|
+
text: result,
|
379
|
+
},
|
380
|
+
],
|
381
|
+
};
|
382
|
+
}
|
383
|
+
case "full_rollback": {
|
384
|
+
const { connection_id } = args;
|
385
|
+
const targetManager = getTargetManager(connection_id);
|
386
|
+
const transactionManager = targetManager.getTransactionManager();
|
387
|
+
const result = await transactionManager.fullRollback(async (query, params) => {
|
388
|
+
return await targetManager.executeQuery(query, params || []);
|
389
|
+
});
|
390
|
+
return {
|
391
|
+
content: [
|
392
|
+
{
|
393
|
+
type: "text",
|
394
|
+
text: result,
|
395
|
+
},
|
396
|
+
],
|
397
|
+
};
|
398
|
+
}
|
399
|
+
case "show_tables": {
|
400
|
+
const { connection_id } = args;
|
401
|
+
const targetManager = getTargetManager(connection_id);
|
402
|
+
const tables = await targetManager.showTables();
|
403
|
+
let result = `📋 数据库概览\n🔗 连接: ${connection_id || connectionManager.getActiveConnectionId()}\n\n`;
|
404
|
+
if (tables.length === 0) {
|
405
|
+
result += "🔍 数据库中没有找到任何表";
|
406
|
+
}
|
407
|
+
else {
|
408
|
+
result += `📊 总共找到 ${tables.length} 个表:\n\n`;
|
409
|
+
for (const table of tables) {
|
410
|
+
const tableName = Object.values(table)[0];
|
411
|
+
try {
|
412
|
+
// 获取表的行数
|
413
|
+
const countResult = await targetManager.executeQuery(`SELECT COUNT(*) as count FROM \`${tableName}\``);
|
414
|
+
const rowCount = countResult.data[0]?.count || 0;
|
415
|
+
// 获取表结构(只显示列名和类型)
|
416
|
+
const structure = await targetManager.describeTable(tableName);
|
417
|
+
const columnInfo = structure.map((col) => `${col.Field}(${col.Type})`).slice(0, 5).join(', ');
|
418
|
+
const moreColumns = structure.length > 5 ? `... +${structure.length - 5}列` : '';
|
419
|
+
result += `🗂️ **${tableName}**\n`;
|
420
|
+
result += ` 📊 行数: ${rowCount}\n`;
|
421
|
+
result += ` 🏗️ 列: ${columnInfo}${moreColumns}\n\n`;
|
422
|
+
}
|
423
|
+
catch (error) {
|
424
|
+
result += `🗂️ **${tableName}**\n`;
|
425
|
+
result += ` ⚠️ 无法获取详细信息\n\n`;
|
426
|
+
}
|
427
|
+
}
|
428
|
+
result += `💡 提示: 使用 describe_table 工具查看具体表的详细结构和样本数据`;
|
429
|
+
}
|
430
|
+
return {
|
431
|
+
content: [
|
432
|
+
{
|
433
|
+
type: "text",
|
434
|
+
text: result,
|
435
|
+
},
|
436
|
+
],
|
437
|
+
};
|
438
|
+
}
|
439
|
+
case "describe_table": {
|
440
|
+
const { table_name, connection_id } = args;
|
441
|
+
const targetManager = getTargetManager(connection_id);
|
442
|
+
// 获取表结构
|
443
|
+
const structure = await targetManager.describeTable(table_name);
|
444
|
+
// 获取表的行数
|
445
|
+
const countResult = await targetManager.executeQuery(`SELECT COUNT(*) as count FROM \`${table_name}\``);
|
446
|
+
const totalRows = countResult.data[0]?.count || 0;
|
447
|
+
// 获取样本数据(最多5行)
|
448
|
+
let sampleData = [];
|
449
|
+
if (totalRows > 0) {
|
450
|
+
const sampleResult = await targetManager.executeQuery(`SELECT * FROM \`${table_name}\` LIMIT 5`);
|
451
|
+
sampleData = sampleResult.data;
|
452
|
+
}
|
453
|
+
// 格式化表结构
|
454
|
+
const structureText = structure
|
455
|
+
.map((col) => `${col.Field.padEnd(20)} | ${col.Type.padEnd(15)} | ${col.Null.padEnd(8)} | ${col.Key.padEnd(8)} | ${(col.Default || 'NULL').toString().padEnd(10)} | ${col.Extra || ''}`)
|
456
|
+
.join("\n");
|
457
|
+
let result = `🔍 表 "${table_name}" 的详细信息\n\n`;
|
458
|
+
result += `📊 基本信息:\n`;
|
459
|
+
result += ` 总行数: ${totalRows}\n`;
|
460
|
+
result += ` 总列数: ${structure.length}\n\n`;
|
461
|
+
result += `🏗️ 表结构:\n`;
|
462
|
+
result += `${"=".repeat(80)}\n`;
|
463
|
+
result += `字段名 | 类型 | 可为空 | 键 | 默认值 | 额外信息\n`;
|
464
|
+
result += `${"=".repeat(80)}\n`;
|
465
|
+
result += `${structureText}\n\n`;
|
466
|
+
if (sampleData.length > 0) {
|
467
|
+
result += `📄 样本数据 (前${sampleData.length}行):\n`;
|
468
|
+
result += `${"=".repeat(80)}\n`;
|
469
|
+
result += JSON.stringify(sampleData, null, 2);
|
470
|
+
}
|
471
|
+
else {
|
472
|
+
result += `📄 样本数据:\n`;
|
473
|
+
result += ` 表中暂无数据`;
|
474
|
+
}
|
475
|
+
result += `\n\n💡 提示: 使用 execute_query 工具可以执行更复杂的查询操作`;
|
476
|
+
return {
|
477
|
+
content: [
|
478
|
+
{
|
479
|
+
type: "text",
|
480
|
+
text: result,
|
481
|
+
},
|
482
|
+
],
|
483
|
+
};
|
484
|
+
}
|
485
|
+
case "disconnect_database": {
|
486
|
+
const { connection_id } = args;
|
487
|
+
const targetManager = connection_id
|
488
|
+
? connectionManager.getConnection(connection_id)
|
489
|
+
: connectionManager.getActiveConnection();
|
490
|
+
if (connection_id) {
|
491
|
+
// 移除指定连接
|
492
|
+
await connectionManager.removeConnection(connection_id);
|
493
|
+
}
|
494
|
+
else if (connectionManager.hasActiveConnection()) {
|
495
|
+
// 移除活跃连接
|
496
|
+
const activeId = connectionManager.getActiveConnectionId();
|
497
|
+
if (activeId) {
|
498
|
+
await connectionManager.removeConnection(activeId);
|
499
|
+
}
|
500
|
+
}
|
501
|
+
return {
|
502
|
+
content: [
|
503
|
+
{
|
504
|
+
type: "text",
|
505
|
+
text: "✅ 数据库连接已断开",
|
506
|
+
},
|
507
|
+
],
|
508
|
+
};
|
509
|
+
}
|
510
|
+
case "list_connections": {
|
511
|
+
const connections = connectionManager.listConnections();
|
512
|
+
if (connections.length === 0) {
|
513
|
+
return {
|
514
|
+
content: [
|
515
|
+
{
|
516
|
+
type: "text",
|
517
|
+
text: `📋 数据库连接列表\n\n🔍 当前没有任何数据库连接`,
|
518
|
+
},
|
519
|
+
],
|
520
|
+
};
|
521
|
+
}
|
522
|
+
let result = `📋 数据库连接列表\n\n📊 总连接数: ${connections.length}\n\n`;
|
523
|
+
connections.forEach((conn, index) => {
|
524
|
+
result += `${index + 1}. 🔗 **${conn.id}**${conn.isActive ? ' 🎯(活跃)' : ''}\n`;
|
525
|
+
result += ` 📍 主机: ${conn.host}:${conn.port}\n`;
|
526
|
+
result += ` 🗄️ 数据库: ${conn.database}\n`;
|
527
|
+
result += ` 👤 用户: ${conn.user}\n`;
|
528
|
+
result += ` ⏰ 连接时间: ${new Date(conn.connectedAt).toLocaleString()}\n\n`;
|
529
|
+
});
|
530
|
+
return {
|
531
|
+
content: [
|
532
|
+
{
|
533
|
+
type: "text",
|
534
|
+
text: result,
|
535
|
+
},
|
536
|
+
],
|
537
|
+
};
|
538
|
+
}
|
539
|
+
case "switch_active_connection": {
|
540
|
+
const { connection_id } = args;
|
541
|
+
await connectionManager.switchActiveConnection(connection_id);
|
542
|
+
const connection = connectionManager.listConnections().find(c => c.id === connection_id);
|
543
|
+
return {
|
544
|
+
content: [
|
545
|
+
{
|
546
|
+
type: "text",
|
547
|
+
text: `✅ 已切换活跃连接到: ${connection_id}\n📍 数据库: ${connection?.database}\n📊 当前总连接数: ${connectionManager.getConnectionCount()}`,
|
548
|
+
},
|
549
|
+
],
|
550
|
+
};
|
551
|
+
}
|
552
|
+
case "remove_connection": {
|
553
|
+
const { connection_id } = args;
|
554
|
+
await connectionManager.removeConnection(connection_id);
|
555
|
+
return {
|
556
|
+
content: [
|
557
|
+
{
|
558
|
+
type: "text",
|
559
|
+
text: `✅ 已移除连接: ${connection_id}\n📊 剩余连接数: ${connectionManager.getConnectionCount()}`,
|
560
|
+
},
|
561
|
+
],
|
562
|
+
};
|
563
|
+
}
|
564
|
+
default:
|
565
|
+
throw new McpError(ErrorCode.MethodNotFound, `未知的工具: ${name}`);
|
566
|
+
}
|
567
|
+
}
|
568
|
+
catch (error) {
|
569
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
570
|
+
// 记录错误
|
571
|
+
logger.error(`工具调用失败`, {
|
572
|
+
tool: name,
|
573
|
+
args,
|
574
|
+
error: err.message,
|
575
|
+
stack: err.stack
|
576
|
+
});
|
577
|
+
throw new McpError(ErrorCode.InternalError, `❌ 工具执行失败: ${err.message}`);
|
578
|
+
}
|
579
|
+
finally {
|
580
|
+
// 记录工具调用结束
|
581
|
+
logger.info(`工具调用结束`, { tool: name });
|
582
|
+
}
|
583
|
+
});
|
584
|
+
// 启动服务器
|
585
|
+
async function main() {
|
586
|
+
try {
|
587
|
+
// 记录服务器启动
|
588
|
+
logger.info("MySQL MCP Server 正在启动...");
|
589
|
+
const transport = new StdioServerTransport();
|
590
|
+
await server.connect(transport);
|
591
|
+
logger.info("MySQL MCP Server 已启动并等待连接", {
|
592
|
+
version: "2.0.0",
|
593
|
+
capabilities: ["connect_database", "execute_query", "show_tables", "describe_table", "begin_transaction", "commit_transaction", "rollback_transaction", "show_transaction_history", "rollback_to_step", "full_rollback", "disconnect_database"]
|
594
|
+
});
|
595
|
+
console.error("MySQL MCP Server 已启动并等待连接...");
|
596
|
+
}
|
597
|
+
catch (error) {
|
598
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
599
|
+
logger.error("服务器启动失败", { error: err.message, stack: err.stack });
|
600
|
+
throw err;
|
601
|
+
}
|
602
|
+
}
|
603
|
+
// 优雅关闭处理
|
604
|
+
process.on("SIGINT", async () => {
|
605
|
+
logger.info("接收到SIGINT信号,正在关闭服务器...");
|
606
|
+
// 断开所有连接
|
607
|
+
connectionManager.disconnectAll();
|
608
|
+
logger.info("服务器已关闭");
|
609
|
+
process.exit(0);
|
610
|
+
});
|
611
|
+
process.on("SIGTERM", async () => {
|
612
|
+
logger.info("接收到SIGTERM信号,正在关闭服务器...");
|
613
|
+
// 断开所有连接
|
614
|
+
connectionManager.disconnectAll();
|
615
|
+
logger.info("服务器已关闭");
|
616
|
+
process.exit(0);
|
617
|
+
});
|
618
|
+
// 处理未捕获的异常
|
619
|
+
process.on("uncaughtException", (error) => {
|
620
|
+
logger.error("未捕获的异常", { error: error.message, stack: error.stack });
|
621
|
+
process.exit(1);
|
622
|
+
});
|
623
|
+
process.on("unhandledRejection", (reason, promise) => {
|
624
|
+
logger.error("未处理的Promise拒绝", { reason, promise });
|
625
|
+
process.exit(1);
|
626
|
+
});
|
627
|
+
main().catch((error) => {
|
628
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
629
|
+
logger.error("启动服务器时发生错误", { error: err.message, stack: err.stack });
|
630
|
+
console.error("启动服务器时发生错误:", err.message);
|
631
|
+
process.exit(1);
|
632
|
+
});
|