#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import mariadb from 'mariadb';
// Configuration de la connexion MariaDB
const dbConfig = {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '3306'),
user: process.env.DB_USER || 'cotranet',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || '',
connectionLimit: 5
};
// Créer un pool de connexions
const pool = mariadb.createPool(dbConfig);
// Créer le serveur MCP
const server = new Server(
{
name: 'mariadb-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Liste des outils disponibles
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'query_database',
description: 'Execute a SQL query on the MariaDB database. Returns the results as JSON.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The SQL query to execute (SELECT, SHOW, DESCRIBE, etc.)',
},
},
required: ['query'],
},
},
{
name: 'list_databases',
description: 'List all databases available on the MariaDB server',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'list_tables',
description: 'List all tables in the current database',
inputSchema: {
type: 'object',
properties: {
database: {
type: 'string',
description: 'Database name (optional, uses the default if not specified)',
},
},
},
},
{
name: 'describe_table',
description: 'Get the structure/schema of a specific table',
inputSchema: {
type: 'object',
properties: {
table: {
type: 'string',
description: 'Name of the table to describe',
},
database: {
type: 'string',
description: 'Database name (optional)',
},
},
required: ['table'],
},
},
],
};
});
// Gestionnaire d'exécution des outils
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
let conn;
try {
conn = await pool.getConnection();
switch (name) {
case 'query_database': {
const query = args.query;
// Sécurité : bloquer les requêtes dangereuses
const dangerousKeywords = ['DROP', 'DELETE', 'TRUNCATE', 'ALTER', 'CREATE', 'INSERT', 'UPDATE'];
const upperQuery = query.toUpperCase().trim();
for (const keyword of dangerousKeywords) {
if (upperQuery.startsWith(keyword)) {
return {
content: [
{
type: 'text',
text: `Error: ${keyword} queries are not allowed for safety reasons. Only SELECT and SHOW queries are permitted.`,
},
],
};
}
}
const results = await conn.query(query);
// Format compact : une ligne par résultat avec séparateur |
if (results.length === 0) {
return { content: [{ type: 'text', text: '(empty)' }] };
}
const keys = Object.keys(results[0]);
const header = keys.join(' | ');
const rows = results.map(r => keys.map(k => r[k] ?? '').join(' | '));
return {
content: [
{
type: 'text',
text: `${header}\n${rows.join('\n')}`,
},
],
};
}
case 'list_databases': {
const results = await conn.query('SHOW DATABASES');
const dbs = results.map(r => r.Database).join('\n');
return {
content: [
{
type: 'text',
text: dbs,
},
],
};
}
case 'list_tables': {
const dbName = args.database || dbConfig.database;
const query = dbName ? `SHOW TABLES FROM ${dbName}` : 'SHOW TABLES';
const results = await conn.query(query);
const tables = results.map(r => Object.values(r)[0]).join('\n');
return {
content: [
{
type: 'text',
text: tables,
},
],
};
}
case 'describe_table': {
const table = args.table;
const dbName = args.database;
const fullTableName = dbName ? `${dbName}.${table}` : table;
const results = await conn.query(`DESCRIBE ${fullTableName}`);
// Format: Field | Type | Null | Key | Default | Extra
const lines = results.map(r =>
`${r.Field} | ${r.Type} | ${r.Null} | ${r.Key || '-'} | ${r.Default || '-'} | ${r.Extra || '-'}`
);
return {
content: [
{
type: 'text',
text: lines.join('\n'),
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error.message}`,
},
],
isError: true,
};
} finally {
if (conn) conn.release();
}
});
// Démarrer le serveur
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MariaDB MCP Server running on stdio');
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});