126 lines
3.7 KiB
JavaScript
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);
|
|
});
|