@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
JavaScript
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