Skip to main content
Glama
HenkDz

PostgreSQL MCP Server

pg_manage_query

Analyze PostgreSQL query performance by generating EXPLAIN plans, identifying slow queries, retrieving statistics, and resetting monitoring data.

Instructions

Manage PostgreSQL query analysis and performance - operation="explain" for EXPLAIN plans, operation="get_slow_queries" for slow query analysis, operation="get_stats" for query statistics, operation="reset_stats" for clearing statistics

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
operationYesOperation: explain (EXPLAIN/EXPLAIN ANALYZE query), get_slow_queries (find slow queries from pg_stat_statements), get_stats (query statistics with cache hit ratios), reset_stats (reset pg_stat_statements)
connectionStringNo
queryNoSQL query to explain (required for explain operation)
analyzeNoUse EXPLAIN ANALYZE - actually executes the query (for explain operation)
buffersNoInclude buffer usage information (for explain operation)
verboseNoInclude verbose output (for explain operation)
costsNoInclude cost estimates (for explain operation)
formatNoOutput format (for explain operation)json
limitNoNumber of slow queries to return (for get_slow_queries operation)
minDurationNoMinimum average duration in milliseconds (for get_slow_queries operation)
orderByNoSort order (for get_slow_queries and get_stats operations)mean_time
includeNormalizedNoInclude normalized query text (for get_slow_queries operation)
minCallsNoMinimum number of calls (for get_stats operation)
queryPatternNoFilter queries containing this pattern (for get_stats operation)
queryIdNoSpecific query ID to reset (for reset_stats operation, resets all if not provided)

Implementation Reference

  • The primary handler function for the 'pg_manage_query' tool. Validates input using the schema, dispatches to operation-specific logic via executeManageQuery, handles errors, and returns formatted ToolOutput.
    async execute(params: unknown, getConnectionString: GetConnectionStringFn): Promise<ToolOutput> {
      const validationResult = ManageQueryInputSchema.safeParse(params);
      if (!validationResult.success) {
        return { 
          content: [{ type: 'text', text: `Invalid input: ${validationResult.error.format()}` }], 
          isError: true 
        };
      }
    
      try {
        const result = await executeManageQuery(validationResult.data, getConnectionString);
        
        let message: string;
        switch (validationResult.data.operation) {
          case 'explain':
            message = validationResult.data.analyze 
              ? 'Query execution plan with runtime statistics' 
              : 'Query execution plan';
            break;
          case 'get_slow_queries':
            message = `Top ${validationResult.data.limit || 10} slow queries ordered by ${validationResult.data.orderBy || 'mean_time'}`;
            break;
          case 'get_stats':
            message = `Query statistics ordered by ${validationResult.data.orderBy || 'total_time'}`;
            break;
          case 'reset_stats':
            message = (result as { message: string }).message;
            break;
          default:
            message = 'Query operation completed';
        }
        
        return { 
          content: [
            { type: 'text', text: message }, 
            { type: 'text', text: JSON.stringify(result, null, 2) }
          ] 
        };
      } catch (error) {
        const errorMessage = error instanceof McpError ? error.message : (error instanceof Error ? error.message : String(error));
        return { 
          content: [{ type: 'text', text: `Error in query operation: ${errorMessage}` }], 
          isError: true 
        };
      }
  • Zod schema defining the input parameters for the pg_manage_query tool, including operation type and specific parameters for explain, slow queries, stats, and reset operations.
    const ManageQueryInputSchema = z.object({
      operation: z.enum(['explain', 'get_slow_queries', 'get_stats', 'reset_stats']).describe(
        'Operation: explain (EXPLAIN/EXPLAIN ANALYZE query), get_slow_queries (find slow queries from pg_stat_statements), get_stats (query statistics with cache hit ratios), reset_stats (reset pg_stat_statements)'
      ),
      connectionString: z.string().optional(),
      
      // EXPLAIN operation parameters
      query: z.string().optional().describe('SQL query to explain (required for explain operation)'),
      analyze: z.boolean().optional().default(false).describe('Use EXPLAIN ANALYZE - actually executes the query (for explain operation)'),
      buffers: z.boolean().optional().default(false).describe('Include buffer usage information (for explain operation)'),
      verbose: z.boolean().optional().default(false).describe('Include verbose output (for explain operation)'),
      costs: z.boolean().optional().default(true).describe('Include cost estimates (for explain operation)'),
      format: z.enum(['text', 'json', 'xml', 'yaml']).optional().default('json').describe('Output format (for explain operation)'),
      
      // GET_SLOW_QUERIES operation parameters
      limit: z.number().optional().default(10).describe('Number of slow queries to return (for get_slow_queries operation)'),
      minDuration: z.number().optional().describe('Minimum average duration in milliseconds (for get_slow_queries operation)'),
      orderBy: z.enum(['mean_time', 'total_time', 'calls', 'cache_hit_ratio']).optional().default('mean_time').describe('Sort order (for get_slow_queries and get_stats operations)'),
      includeNormalized: z.boolean().optional().default(true).describe('Include normalized query text (for get_slow_queries operation)'),
      
      // GET_STATS operation parameters
      minCalls: z.number().optional().describe('Minimum number of calls (for get_stats operation)'),
      queryPattern: z.string().optional().describe('Filter queries containing this pattern (for get_stats operation)'),
      
      // RESET_STATS operation parameters
      queryId: z.string().optional().describe('Specific query ID to reset (for reset_stats operation, resets all if not provided)'),
    });
  • src/index.ts:225-257 (registration)
    Registration of the pg_manage_query tool (as manageQueryTool) in the allTools array, which is passed to the PostgreSQLServer constructor to enable the tool in the MCP server.
    const allTools: PostgresTool[] = [
      // Core Analysis & Debugging
      analyzeDatabaseTool,
      debugDatabaseTool,
      
      // Schema & Structure Management (Meta-Tools)
      manageSchemaTools,
      manageFunctionsTool,
      manageTriggersTools,
      manageIndexesTool,
      manageConstraintsTool,
      manageRLSTool,
      
      // User & Security Management
      manageUsersTool,
      
      // Query & Performance Management
      manageQueryTool,
      
      // Data Operations (Enhancement Tools)
      executeQueryTool,
      executeMutationTool,
      executeSqlTool,
      
      // Documentation & Metadata
      manageCommentsTool,
      
      // Data Migration & Monitoring
      exportTableDataTool,
      importTableDataTool,
      copyBetweenDatabasesTool,
      monitorDatabaseTool
    ];
  • Helper dispatch function that routes the validated input to specific operation handlers (explain, get_slow_queries, get_stats, reset_stats).
    async function executeManageQuery(
      input: ManageQueryInput,
      getConnectionString: GetConnectionStringFn
    ): Promise<ExplainResult | SlowQuery[] | QueryStats[] | { message: string; queryId?: string }> {
      switch (input.operation) {
        case 'explain':
          return executeExplainQuery(input, getConnectionString);
          
        case 'get_slow_queries':
          return executeGetSlowQueries(input, getConnectionString);
          
        case 'get_stats':
          return executeGetQueryStats(input, getConnectionString);
          
        case 'reset_stats':
          return executeResetQueryStats(input, getConnectionString);
          
        default:
          throw new McpError(ErrorCode.InvalidParams, `Unsupported operation: ${input.operation}`);
      }
    }
  • Helper function for the 'explain' operation: executes EXPLAIN (with options like ANALYZE, BUFFERS, etc.) on the provided query and extracts key plan metrics.
    async function executeExplainQuery(
      input: ManageQueryInput,
      getConnectionString: GetConnectionStringFn
    ): Promise<ExplainResult> {
      if (!input.query) {
        throw new McpError(ErrorCode.InvalidParams, 'query parameter is required for explain operation');
      }
    
      const resolvedConnectionString = getConnectionString(input.connectionString);
      const db = DatabaseConnection.getInstance();
      const { query, analyze, buffers, verbose, costs, format } = input;
      
      try {
        await db.connect(resolvedConnectionString);
        
        // Build EXPLAIN options
        const options = [];
        if (analyze) options.push('ANALYZE');
        if (buffers) options.push('BUFFERS');
        if (verbose) options.push('VERBOSE');
        if (!costs) options.push('COSTS false');
        options.push(`FORMAT ${format?.toUpperCase()}`);
        
        const explainQuery = `EXPLAIN (${options.join(', ')}) ${query}`;
        
        const result = await db.query(explainQuery);
        
        // Extract timing information if available (from EXPLAIN ANALYZE)
        let execution_time: number | undefined;
        let planning_time: number | undefined;
        let total_cost: number | undefined;
        let actual_rows: number | undefined;
        let estimated_rows: number | undefined;
        
        if (format === 'json' && result.length > 0) {
          const plan = result[0]['QUERY PLAN'];
          if (Array.isArray(plan) && plan.length > 0) {
            const planData = plan[0];
            execution_time = planData['Execution Time'];
            planning_time = planData['Planning Time'];
            
            if (planData.Plan) {
              total_cost = planData.Plan['Total Cost'];
              actual_rows = planData.Plan['Actual Rows'];
              estimated_rows = planData.Plan['Plan Rows'];
            }
          }
        }
        
        return {
          query,
          plan: result,
          execution_time,
          planning_time,
          total_cost,
          actual_rows,
          estimated_rows
        };
        
      } catch (error) {
        throw new McpError(ErrorCode.InternalError, `Failed to explain query: ${error instanceof Error ? error.message : String(error)}`);
      } finally {
        await db.disconnect();
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but provides minimal behavioral disclosure. It mentions what each operation does but doesn't cover important behavioral aspects like: whether operations require specific permissions, if reset_stats is destructive/irreversible, performance implications of analyze=true, rate limits, or what the output looks like. For a tool with potentially destructive operations (reset_stats) and complex behaviors, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise and front-loaded: a single sentence efficiently communicates the tool's scope and all four operations. Every word earns its place with zero waste or redundancy. The structure clearly presents the operation-to-purpose mapping in a compact format.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (15 parameters, 4 distinct operations including potentially destructive reset_stats), no annotations, and no output schema, the description is inadequate. It doesn't explain what the tool returns, doesn't warn about the destructive nature of reset_stats, doesn't mention prerequisites like pg_stat_statements extension, and provides minimal guidance on parameter interactions across different operations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is high (93%), so the baseline is 3. The description adds minimal value beyond the schema - it maps operation values to their purposes but doesn't explain parameter interactions or provide additional context about when to use specific parameters. The schema already documents most parameters well, so the description doesn't significantly enhance understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Manage PostgreSQL query analysis and performance' with specific operations listed (explain, get_slow_queries, get_stats, reset_stats). It distinguishes from siblings like pg_execute_query (execution) and pg_analyze_database (database-wide analysis) by focusing specifically on query-level performance analysis.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use each operation (e.g., 'operation="explain" for EXPLAIN plans'), but doesn't explicitly state when NOT to use this tool or mention specific alternatives among siblings. The operational breakdown gives good guidance on selecting the right operation within this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other 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/HenkDz/postgresql-mcp-server'

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