Skip to main content
Glama

MySQL ReadOnly MCP Server

by zhaojw-php
index.ts5.57 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { MySQLConnection } from './mysql-connection.js'; import { config, validateConfig } from './config.js'; import { isReadOnlyQuery, addAutoLimit } from './utils/sql-validator.js'; const server = new Server( { name: 'mcp-mysql-readonly', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // MySQL connection instance let mysqlConnection: MySQLConnection; // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { const tools: Tool[] = [ { name: 'mysql_query', description: 'Execute a read-only SQL query on MySQL database', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'SQL query to execute (SELECT statements only)', }, }, required: ['query'], }, }, { name: 'mysql_list_tables', description: 'List all tables in the database', inputSchema: { type: 'object', properties: { database: { type: 'string', description: 'Database name (optional, defaults to configured database)', }, }, }, }, { name: 'mysql_describe_table', description: 'Get table structure and column information', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Table name to describe', }, database: { type: 'string', description: 'Database name (optional, defaults to configured database)', }, }, required: ['table'], }, }, { name: 'mysql_list_databases', description: 'List all available databases', inputSchema: { type: 'object', properties: {}, }, }, ]; return { tools }; }); // Handle tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { // Initialize MySQL connection if not already done if (!mysqlConnection) { mysqlConnection = new MySQLConnection(config); await mysqlConnection.connect(); } switch (name) { case 'mysql_query': { const { query } = args as { query: string }; // Validate that it's a read-only query if (!isReadOnlyQuery(query)) { throw new Error('Only SELECT statements are allowed for security reasons'); } // Apply automatic LIMIT to prevent large result sets const safeQuery = addAutoLimit(query); // Log if LIMIT was added for transparency if (safeQuery !== query.trim()) { console.error(`Added automatic LIMIT to query for safety: ${query}`); } const result = await mysqlConnection.executeQuery(safeQuery); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'mysql_list_tables': { const { database } = args as { database?: string }; const result = await mysqlConnection.listTables(database); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'mysql_describe_table': { const { table, database } = args as { table: string; database?: string }; const result = await mysqlConnection.describeTable(table, database); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'mysql_list_databases': { const result = await mysqlConnection.listDatabases(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [ { type: 'text', text: `Error: ${errorMessage}`, }, ], isError: true, }; } }); // Clean shutdown process.on('SIGINT', async () => { if (mysqlConnection) { await mysqlConnection.disconnect(); } process.exit(0); }); // Start the server async function main() { // Validate configuration before starting the server try { validateConfig(); console.error('Configuration validation passed'); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown configuration error'; console.error(`Configuration validation failed: ${errorMessage}`); console.error('Please check your environment variables and restart the server'); process.exit(1); } const transport = new StdioServerTransport(); await server.connect(transport); console.error('MySQL MCP ReadOnly server running on stdio'); } main().catch((error) => { console.error('Server error:', error); process.exit(1); });

Implementation Reference

Latest Blog Posts

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/zhaojw-php/mysql-readonly-mcp'

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