HaniBlindBox/server/scripts/mysql-mcp-server/index.js
2026-01-03 23:56:07 +08:00

126 lines
3.7 KiB
JavaScript

#!/usr/bin/env node
/**
* MySQL MCP Server - Node.js 实现
* 提供 execute_sql 工具用于执行 SQL 语句
*/
const mysql = require('mysql2/promise');
const readline = require('readline');
const config = {
host: process.env.MYSQL_HOST || 'localhost',
port: parseInt(process.env.MYSQL_PORT) || 3306,
user: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWORD || '',
database: process.env.MYSQL_DATABASE || 'test'
};
let pool = null;
async function getPool() {
if (!pool) {
pool = mysql.createPool(config);
}
return pool;
}
async function executeSQL(query) {
const p = await getPool();
const [rows, fields] = await p.query(query);
return { rows, fields };
}
function sendResponse(id, result) {
const response = { jsonrpc: '2.0', id, result };
process.stdout.write(JSON.stringify(response) + '\n');
}
function sendError(id, code, message) {
const response = { jsonrpc: '2.0', id, error: { code, message } };
process.stdout.write(JSON.stringify(response) + '\n');
}
async function handleRequest(request) {
const { id, method, params } = request;
try {
switch (method) {
case 'initialize':
sendResponse(id, {
protocolVersion: '2024-11-05',
capabilities: { tools: {} },
serverInfo: { name: 'mysql-node-mcp', version: '1.0.0' }
});
break;
case 'tools/list':
sendResponse(id, {
tools: [{
name: 'execute_sql',
description: 'Execute SQL query on MySQL database',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'SQL query to execute' }
},
required: ['query']
}
}]
});
break;
case 'tools/call':
if (params.name === 'execute_sql') {
const query = params.arguments.query;
const result = await executeSQL(query);
let content;
if (Array.isArray(result.rows) && result.rows.length > 0) {
content = JSON.stringify(result.rows, null, 2);
} else if (result.rows && result.rows.affectedRows !== undefined) {
content = `Rows affected: ${result.rows.affectedRows}`;
} else {
content = 'Query executed successfully';
}
sendResponse(id, {
content: [{ type: 'text', text: content }]
});
} else {
sendError(id, -32601, `Unknown tool: ${params.name}`);
}
break;
case 'notifications/initialized':
case 'notifications/cancelled':
// 忽略通知
break;
default:
if (id !== undefined) {
sendError(id, -32601, `Method not found: ${method}`);
}
}
} catch (err) {
if (id !== undefined) {
sendError(id, -32000, err.message);
}
}
}
const rl = readline.createInterface({ input: process.stdin });
rl.on('line', async (line) => {
try {
const request = JSON.parse(line);
await handleRequest(request);
} catch (err) {
// 忽略解析错误
}
});
process.on('SIGINT', async () => {
if (pool) await pool.end();
process.exit(0);
});