UNPKG

@rolandohuber/mysql-mcp-server

Version:

A comprehensive MCP server for MySQL database operations with 16 tools, multi-transport support, and intelligent test data generation

180 lines 6.5 kB
import mysql from 'mysql2/promise'; export class MysqlService { constructor(config) { this.connection = null; this.config = config; } async connect() { if (this.connection) { return; } try { const connectionConfig = { host: this.config.host, port: this.config.port, user: this.config.user, password: this.config.password, charset: 'utf8mb4', timezone: '+00:00', }; if (this.config.database) { connectionConfig.database = this.config.database; } this.connection = await mysql.createConnection(connectionConfig); } catch (error) { throw new Error(`Failed to connect to MySQL: ${error}`); } } async disconnect() { if (this.connection) { await this.connection.end(); this.connection = null; } } async query(sql, params = []) { await this.connect(); if (!this.connection) { throw new Error('No database connection available'); } try { const [rows, fields] = await this.connection.execute(sql, params); if (Array.isArray(rows)) { return { rows: rows, fields: fields, }; } else { const result = rows; return { rows: [], fields: [], affectedRows: result.affectedRows, insertId: result.insertId, }; } } catch (error) { throw new Error(`Query execution failed: ${error}`); } } async ping() { try { await this.connect(); if (!this.connection) return false; await this.connection.ping(); return true; } catch (error) { return false; } } async getVersion() { const result = await this.query('SELECT VERSION() as version'); return result.rows[0]?.version || 'Unknown'; } async listDatabases() { const result = await this.query('SHOW DATABASES'); return result.rows.map((row) => row.Database); } async listTables() { const result = await this.query('SHOW TABLES'); const key = Object.keys(result.rows[0] || {})[0]; return result.rows.map((row) => row[key]); } async describeTable(tableName) { // First check if table exists const tableExists = await this.query(` SELECT COUNT(*) as count FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? `, [this.config.database, tableName]); if (tableExists.rows[0].count === 0) { throw new Error(`Table '${tableName}' does not exist`); } const result = await this.query(` SELECT COLUMN_NAME as column_name, DATA_TYPE as data_type, IS_NULLABLE as is_nullable, COLUMN_KEY as column_key, COLUMN_DEFAULT as column_default, EXTRA as extra, CHARACTER_MAXIMUM_LENGTH as character_maximum_length, NUMERIC_PRECISION as numeric_precision, NUMERIC_SCALE as numeric_scale FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION `, [this.config.database, tableName]); return result.rows; } async getTableRelations(tableName) { // Outgoing relations (this table's foreign keys) const outgoingResult = await this.query(` SELECT COLUMN_NAME as column_name, REFERENCED_TABLE_NAME as referenced_table_name, REFERENCED_COLUMN_NAME as referenced_column_name, CONSTRAINT_NAME as constraint_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND REFERENCED_TABLE_NAME IS NOT NULL `, [this.config.database, tableName]); // Incoming relations (other tables referencing this table) const incomingResult = await this.query(` SELECT TABLE_NAME as table_name, COLUMN_NAME as column_name, REFERENCED_COLUMN_NAME as referenced_column_name, CONSTRAINT_NAME as constraint_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = ? AND REFERENCED_TABLE_NAME = ? `, [this.config.database, tableName]); return { outgoing: outgoingResult.rows, incoming: incomingResult.rows, }; } async getTableIndexes(tableName) { const result = await this.query(`SHOW INDEX FROM \`${tableName}\``); return result.rows; } async insert(tableName, rows) { if (rows.length === 0) { return { affectedRows: 0, insertId: 0 }; } const columns = Object.keys(rows[0]); const placeholders = columns.map(() => '?').join(', '); const values = rows.flatMap(row => columns.map(col => row[col])); const sql = `INSERT INTO \`${tableName}\` (\`${columns.join('`, `')}\`) VALUES ${rows.map(() => `(${placeholders})`).join(', ')}`; const result = await this.query(sql, values); return { affectedRows: result.affectedRows || 0, insertId: result.insertId || 0, }; } async update(tableName, data, whereClause) { const columns = Object.keys(data); const setClause = columns.map(col => `\`${col}\` = ?`).join(', '); const values = columns.map(col => data[col]); const sql = `UPDATE \`${tableName}\` SET ${setClause} WHERE ${whereClause}`; const result = await this.query(sql, values); return { affectedRows: result.affectedRows || 0 }; } async delete(tableName, whereClause) { const sql = `DELETE FROM \`${tableName}\` WHERE ${whereClause}`; const result = await this.query(sql); return { affectedRows: result.affectedRows || 0 }; } async explain(query) { const result = await this.query(`EXPLAIN ${query}`); return result.rows; } async sampleData(tableName, count) { const result = await this.query(`SELECT * FROM \`${tableName}\` LIMIT ${count}`); return result.rows; } } //# sourceMappingURL=MysqlService.js.map