Skip to main content
Glama

execute_query

Execute read-only SQL queries (SELECT, SHOW, DESCRIBE, EXPLAIN) to retrieve data from MySQL databases for analysis and reporting.

Instructions

Execute a read-only SQL query

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSQL query (only SELECT, SHOW, DESCRIBE, and EXPLAIN statements are allowed)
databaseNoDatabase name (optional, uses default if not specified)

Implementation Reference

  • The handler logic for the 'execute_query' tool within the CallToolRequestSchema request handler. Extracts query and database parameters, validates the query is read-only, executes it using the executeQuery helper, and returns JSON-formatted results.
    case "execute_query": { console.error('[Tool] Executing execute_query'); const query = request.params.arguments?.query as string; const database = request.params.arguments?.database as string | undefined; if (!query) { throw new McpError(ErrorCode.InvalidParams, "Query is required"); } // Validate that the query is read-only validateQuery(query); const { rows } = await executeQuery( pool, query, [], database ); return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }; }
  • src/index.ts:107-123 (registration)
    Registration of the 'execute_query' tool in the ListToolsRequestSchema handler, including name, description, and input schema definition.
    { name: "execute_query", description: "Execute a read-only SQL query", inputSchema: { type: "object", properties: { query: { type: "string", description: "SQL query (only SELECT, SHOW, DESCRIBE, and EXPLAIN statements are allowed)" }, database: { type: "string", description: "Database name (optional, uses default if not specified)" } }, required: ["query"] }
  • Core helper function executeQuery that performs the actual database query execution using a MySQL connection pool, with timeout, row limiting, and database switching.
    export async function executeQuery( pool: mysql.Pool, sql: string, params: any[] = [], database?: string ): Promise<{ rows: any; fields: mysql.FieldPacket[] }> { console.error(`[Query] Executing: ${sql}`); let connection: mysql.PoolConnection | null = null; try { // Get connection from pool connection = await pool.getConnection(); // Use specific database if provided if (database) { console.error(`[Query] Using database: ${database}`); await connection.query(`USE \`${database}\``); } // Execute query with timeout const [rows, fields] = await Promise.race([ connection.query(sql, params), new Promise<never>((_, reject) => { setTimeout(() => reject(new Error('Query timeout')), DEFAULT_TIMEOUT); }), ]); // Apply row limit if result is an array const limitedRows = Array.isArray(rows) && rows.length > DEFAULT_ROW_LIMIT ? rows.slice(0, DEFAULT_ROW_LIMIT) : rows; // Log result summary console.error(`[Query] Success: ${Array.isArray(rows) ? rows.length : 1} rows returned`); return { rows: limitedRows, fields }; } catch (error) { console.error('[Error] Query execution failed:', error); throw error; } finally { // Release connection back to pool if (connection) { connection.release(); } } }
  • Input validation schema enforcement for execute_query via validateQuery and supporting isReadOnlyQuery functions, ensuring only read-only SQL queries (SELECT, SHOW, etc.) are permitted.
    export function isReadOnlyQuery(query: string): boolean { // Normalize query by removing comments and extra whitespace const normalizedQuery = query .replace(/--.*$/gm, '') // Remove single-line comments .replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments .replace(/\s+/g, ' ') // Normalize whitespace .trim() .toUpperCase(); // Check if query starts with an allowed command const startsWithAllowed = ALLOWED_COMMANDS.some(cmd => normalizedQuery.startsWith(cmd + ' ') || normalizedQuery === cmd ); // Check if query contains any disallowed commands const containsDisallowed = DISALLOWED_COMMANDS.some(cmd => { const regex = new RegExp(`(^|\\s)${cmd}(\\s|$)`); return regex.test(normalizedQuery); }); // Check for multiple statements (;) const hasMultipleStatements = normalizedQuery.includes(';') && !normalizedQuery.endsWith(';'); // Query is read-only if it starts with an allowed command, // doesn't contain any disallowed commands, and doesn't have multiple statements return startsWithAllowed && !containsDisallowed && !hasMultipleStatements; } /** * Validates if a SQL query is safe to execute * @param query SQL query to validate * @throws Error if the query is not safe */ export function validateQuery(query: string): void { console.error('[Validator] Validating query:', query); if (!query || typeof query !== 'string') { throw new Error('Query must be a non-empty string'); } if (!isReadOnlyQuery(query)) { console.error('[Validator] Query rejected: not read-only'); throw new Error('Only read-only queries are allowed (SELECT, SHOW, DESCRIBE, EXPLAIN)'); } console.error('[Validator] Query validated as read-only'); }

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/Himanshu-Agg12/mcp-mysql-lens'

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