Skip to main content
Glama

MySQL Custom MCP Server

index.ts7.06 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js"; import mysql from "mysql2/promise"; const DB_CONFIG = { host: process.env.MYSQL_HOST || "localhost", port: parseInt(process.env.MYSQL_PORT || "3306"), database: process.env.MYSQL_DB || "mysql", user: process.env.MYSQL_USER || "root", password: process.env.MYSQL_PASSWORD || "", }; // Hàm parse connection string thành config object function parseConnectionString(connectionString: string): any { const config: any = {}; const pairs = connectionString.split(";"); for (const pair of pairs) { const [key, value] = pair.split("="); if (key && value) { if (key.toLowerCase() === "port") { config[key.toLowerCase()] = parseInt(value); } else { switch (key.toLowerCase()) { case "username": case "uid": config["user"] = value; break; case "server": case "host": case "datasource": config["host"] = value; break; case "password": case "pwd": config["password"] = value; break; case "database": case "db": case "initial catalog": config["database"] = value; break; case "pooling": // Ignore pooling parameter as we always use pooling break; default: config[key.toLowerCase()] = value; } } } } return config; } class MySQLServer { private server: Server; private defaultPool: mysql.Pool; constructor() { this.server = new Server( { name: "mysql-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); this.defaultPool = mysql.createPool(DB_CONFIG); this.setupToolHandlers(); this.server.onerror = (error) => console.error("[MCP Error]", error); process.on("SIGINT", async () => { await this.defaultPool.end(); await this.server.close(); process.exit(0); }); } private async executeQueryWithConfig( query: string, params: any[] | undefined, connectionString?: string ) { let pool = this.defaultPool; let temporaryPool = false; if (connectionString) { const config = parseConnectionString(connectionString); // Tạo pool mới với config từ connection string pool = mysql.createPool(config); temporaryPool = true; } try { const [rows] = await pool.query(query, params || []); return rows; } finally { // Đóng pool tạm thời sau khi query xong if (temporaryPool) { await pool.end(); } } } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "execute_query", description: "Thực thi câu query SQL", inputSchema: { type: "object", properties: { query: { type: "string", description: "Câu lệnh SQL cần thực thi", }, params: { type: "array", description: "Các tham số cho câu query (optional)", items: { type: "any", }, }, connectionString: { type: "string", description: "Connection string tùy chỉnh (optional)", }, }, required: ["query"], }, }, { name: "list_tables", description: "Lấy danh sách các bảng trong database", inputSchema: { type: "object", properties: { database: { type: "string", description: "Tên database (mặc định lấy từ connection)", }, connectionString: { type: "string", description: "Connection string tùy chỉnh (optional)", }, }, }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case "execute_query": { const { query, params, connectionString } = request.params .arguments as { query: string; params?: any[]; connectionString?: string; }; const result = await this.executeQueryWithConfig( query, params, connectionString ); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "list_tables": { const { database, connectionString } = request.params.arguments as { database?: string; connectionString?: string; }; let dbName = database; if (!dbName) { // Nếu không có database được chỉ định, sử dụng database từ connection if (connectionString) { const config = parseConnectionString(connectionString); dbName = config.database || DB_CONFIG.database; } else { dbName = DB_CONFIG.database; } } const query = ` SELECT table_name FROM information_schema.tables WHERE table_schema = ? ORDER BY table_name `; const result = await this.executeQueryWithConfig( query, [dbName], connectionString ); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } } catch (error) { console.error(error); return { content: [ { type: "text", text: `Database error: ${(error as Error).message}`, }, ], isError: true, }; } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("MySQL MCP server running on stdio"); } } const server = new MySQLServer(); server.run().catch(console.error);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tonguyenducmanh/mcp-server-mysql'

If you have feedback or need assistance with the MCP directory API, please join our Discord server