Skip to main content
Glama
melihbirim

PostgreSQL MCP Server

by melihbirim

execute_query

Run read-only SQL queries (SELECT, SHOW, DESCRIBE, EXPLAIN, WITH) on PostgreSQL databases, with optional row limits, to retrieve or analyze data securely.

Instructions

Execute a read-only SQL query (SELECT, SHOW, DESCRIBE, EXPLAIN, WITH statements only)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of rows to return (default: 100)
queryYesSQL query to execute (read-only operations only)

Implementation Reference

  • The handler function for the 'execute_query' tool. It processes the input query and limit, adds a LIMIT if necessary for SELECT queries, executes the query using the helper executeQuery, formats the results as a markdown table, and returns them. Includes error handling.
    async ({ query, limit }) => { try { const maxLimit = limit || 100; // Add LIMIT clause if not present and it's a SELECT query let finalQuery = query.trim(); const normalizedQuery = finalQuery.toLowerCase(); if (normalizedQuery.startsWith('select') && !normalizedQuery.includes('limit')) { finalQuery += ` LIMIT ${maxLimit}`; } const rows = await executeQuery(finalQuery); if (rows.length === 0) { return { content: [ { type: "text", text: "Query executed successfully. No rows returned.", }, ], }; } // Format results as a table const headers = Object.keys(rows[0]); let result = `Query Results (${rows.length} rows):\n\n`; // Add headers result += headers.join(' | ') + '\n'; result += headers.map(() => '---').join(' | ') + '\n'; // Add rows rows.forEach(row => { const values = headers.map(header => { const value = row[header]; return value === null ? 'NULL' : String(value); }); result += values.join(' | ') + '\n'; }); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; return { content: [ { type: "text", text: `Error executing query: ${errorMessage}`, }, ], }; } }
  • Zod schema defining the input parameters for the 'execute_query' tool: required 'query' string and optional 'limit' number.
    { query: z.string().describe("SQL query to execute (read-only operations only)"), limit: z.number().describe("Maximum number of rows to return (default: 100)").optional(), },
  • src/index.ts:294-363 (registration)
    Registration of the 'execute_query' tool using server.tool(), including name, description, input schema, and handler function.
    server.tool( "execute_query", "Execute a read-only SQL query (SELECT, SHOW, DESCRIBE, EXPLAIN, WITH statements only)", { query: z.string().describe("SQL query to execute (read-only operations only)"), limit: z.number().describe("Maximum number of rows to return (default: 100)").optional(), }, async ({ query, limit }) => { try { const maxLimit = limit || 100; // Add LIMIT clause if not present and it's a SELECT query let finalQuery = query.trim(); const normalizedQuery = finalQuery.toLowerCase(); if (normalizedQuery.startsWith('select') && !normalizedQuery.includes('limit')) { finalQuery += ` LIMIT ${maxLimit}`; } const rows = await executeQuery(finalQuery); if (rows.length === 0) { return { content: [ { type: "text", text: "Query executed successfully. No rows returned.", }, ], }; } // Format results as a table const headers = Object.keys(rows[0]); let result = `Query Results (${rows.length} rows):\n\n`; // Add headers result += headers.join(' | ') + '\n'; result += headers.map(() => '---').join(' | ') + '\n'; // Add rows rows.forEach(row => { const values = headers.map(header => { const value = row[header]; return value === null ? 'NULL' : String(value); }); result += values.join(' | ') + '\n'; }); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; return { content: [ { type: "text", text: `Error executing query: ${errorMessage}`, }, ], }; } } );
  • Helper function 'executeQuery' that performs the actual database query execution with read-only safety checks. Used by the 'execute_query' handler and other tools.
    async function executeQuery(query: string, params: any[] = []): Promise<any[]> { const client = await getDbConnection(); // Basic safety checks for read-only operations const normalizedQuery = query.trim().toLowerCase(); const readOnlyPrefixes = ['select', 'show', 'describe', 'explain', 'with']; const isReadOnly = readOnlyPrefixes.some(prefix => normalizedQuery.startsWith(prefix)); if (!isReadOnly) { throw new Error("Only read-only queries (SELECT, SHOW, DESCRIBE, EXPLAIN, WITH) are allowed for security."); } try { const result = await client.query(query, params); return result.rows; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; throw new Error(`Query execution failed: ${errorMessage}`); } }

Other Tools

Related Tools

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/melihbirim/pg-mcp'

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