mysql-mcp-universal
Version:
🚀 Universal MySQL MCP Server - Connect GitHub Copilot to any MySQL database instantly
269 lines (243 loc) • 9.08 kB
JavaScript
// Servidor MCP Universal MySQL - Versión simplificada para testing
const dotenv = require("dotenv");
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
const {
CallToolRequestSchema,
ListToolsRequestSchema,
} = require("@modelcontextprotocol/sdk/types.js");
const mysql = require("mysql2/promise");
// Cargar variables de entorno
dotenv.config();
// Servidor MCP Universal MySQL
const server = new Server({
name: "MySQL-Universal-Connect",
version: "2.0.0"
}, {
capabilities: {
tools: {}
}
});
console.log("Servidor MCP MySQL Universal iniciado - JavaScript");
// Configuración por defecto
const defaultConfig = {
host: process.env.MYSQL_HOST || '127.0.0.1',
user: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWORD || '',
port: parseInt(process.env.MYSQL_PORT || '3306'),
connectTimeout: 10000,
acquireTimeout: 10000,
timeout: 10000
};
// Caché de conexiones
const connectionCache = new Map();
// Función para crear clave única para el caché de conexiones
function createConnectionKey(host, port, user, database) {
return `${host}:${port}:${user}:${database || 'no-db'}`;
}
// Función para conectar a MySQL con parámetros dinámicos
async function connectToDatabase(params = {}) {
const config = {
host: params.host || defaultConfig.host,
port: params.port || defaultConfig.port,
user: params.user || defaultConfig.user,
password: params.password || defaultConfig.password,
database: params.database,
connectTimeout: defaultConfig.connectTimeout,
acquireTimeout: defaultConfig.acquireTimeout,
timeout: defaultConfig.timeout
};
const connectionKey = createConnectionKey(config.host, config.port, config.user, config.database);
// Intentar reutilizar conexión existente
if (connectionCache.has(connectionKey)) {
const existingConnection = connectionCache.get(connectionKey);
try {
// Verificar si la conexión sigue activa
await existingConnection.ping();
return existingConnection;
} catch (error) {
// Si la conexión falló, eliminarla del caché
connectionCache.delete(connectionKey);
}
}
// Crear nueva conexión
const connection = await mysql.createConnection(config);
connectionCache.set(connectionKey, connection);
return connection;
}
// Definir las herramientas disponibles
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "show_databases",
description: "Muestra todas las bases de datos disponibles en el servidor MySQL",
inputSchema: {
type: "object",
properties: {
host: { type: "string", description: "Dirección del servidor MySQL (por defecto: 127.0.0.1)" },
port: { type: "number", description: "Puerto del servidor MySQL (por defecto: 3306)" },
user: { type: "string", description: "Usuario de MySQL (por defecto: root)" },
password: { type: "string", description: "Contraseña de MySQL" }
}
}
},
{
name: "show_tables",
description: "Muestra todas las tablas de una base de datos específica",
inputSchema: {
type: "object",
properties: {
database: { type: "string", description: "Nombre de la base de datos" },
host: { type: "string", description: "Dirección del servidor MySQL (por defecto: 127.0.0.1)" },
port: { type: "number", description: "Puerto del servidor MySQL (por defecto: 3306)" },
user: { type: "string", description: "Usuario de MySQL (por defecto: root)" },
password: { type: "string", description: "Contraseña de MySQL" }
},
required: ["database"]
}
},
{
name: "describe_table",
description: "Muestra la estructura de una tabla específica",
inputSchema: {
type: "object",
properties: {
table_name: { type: "string", description: "Nombre de la tabla a describir" },
database: { type: "string", description: "Nombre de la base de datos" },
host: { type: "string", description: "Dirección del servidor MySQL (por defecto: 127.0.0.1)" },
port: { type: "number", description: "Puerto del servidor MySQL (por defecto: 3306)" },
user: { type: "string", description: "Usuario de MySQL (por defecto: root)" },
password: { type: "string", description: "Contraseña de MySQL" }
},
required: ["table_name", "database"]
}
},
{
name: "execute_query",
description: "Ejecuta una consulta SQL en una base de datos específica",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "La consulta SQL a ejecutar" },
database: { type: "string", description: "Nombre de la base de datos" },
host: { type: "string", description: "Dirección del servidor MySQL (por defecto: 127.0.0.1)" },
port: { type: "number", description: "Puerto del servidor MySQL (por defecto: 3306)" },
user: { type: "string", description: "Usuario de MySQL (por defecto: root)" },
password: { type: "string", description: "Contraseña de MySQL" }
},
required: ["query", "database"]
}
}
]
}));
// Manejar las llamadas a las herramientas
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "show_databases": {
const db = await connectToDatabase({
host: args?.host,
port: args?.port,
user: args?.user,
password: args?.password
});
const [databases] = await db.execute("SHOW DATABASES");
return {
content: [
{
type: "text",
text: `Bases de datos disponibles en ${args?.host || defaultConfig.host}:${args?.port || defaultConfig.port}:\n${JSON.stringify(databases, null, 2)}`
}
]
};
}
case "show_tables": {
const db = await connectToDatabase({
host: args?.host,
port: args?.port,
user: args?.user,
password: args?.password,
database: args?.database
});
const [tables] = await db.execute("SHOW TABLES");
return {
content: [
{
type: "text",
text: `Tablas en la base de datos '${args?.database}':\n${JSON.stringify(tables, null, 2)}`
}
]
};
}
case "describe_table": {
const db = await connectToDatabase({
host: args?.host,
port: args?.port,
user: args?.user,
password: args?.password,
database: args?.database
});
const [columns] = await db.execute(`DESCRIBE \`${args?.table_name}\``);
return {
content: [
{
type: "text",
text: `Estructura de la tabla '${args?.table_name}' en la base de datos '${args?.database}':\n${JSON.stringify(columns, null, 2)}`
}
]
};
}
case "execute_query": {
const db = await connectToDatabase({
host: args?.host,
port: args?.port,
user: args?.user,
password: args?.password,
database: args?.database
});
const queryResult = await db.execute(args?.query);
return {
content: [
{
type: "text",
text: `Consulta ejecutada en '${args?.database}':\nQuery: ${args?.query}\n\nResultado:\n${JSON.stringify(queryResult[0], null, 2)}`
}
]
};
}
default:
throw new Error(`Herramienta desconocida: ${name}`);
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error.message}\nStack: ${error.stack}`
}
],
isError: true
};
}
});
// Manejar cierre graceful del servidor
process.on('SIGINT', async () => {
console.log('Cerrando conexiones de base de datos...');
for (const [key, connection] of connectionCache) {
try {
await connection.end();
} catch (error) {
console.error(`Error cerrando conexión ${key}:`, error);
}
}
connectionCache.clear();
process.exit(0);
});
// Iniciar el servidor
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("Servidor MCP MySQL Universal iniciado - Puede conectarse a cualquier base de datos MySQL");
}
main().catch(console.error);