Skip to main content
Glama
HenkDz

PostgreSQL MCP Server

pg_manage_triggers

Manage PostgreSQL triggers by listing, creating, removing, or enabling/disabling them on database tables to automate data operations.

Instructions

Manage PostgreSQL triggers - get, create, drop, and enable/disable triggers. Examples: operation="get" to list triggers, operation="create" with triggerName, tableName, functionName, operation="drop" with triggerName and tableName, operation="set_state" with triggerName, tableName, enable

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
connectionStringNoPostgreSQL connection string (optional)
operationYesOperation: get (list triggers), create (new trigger), drop (remove trigger), set_state (enable/disable trigger)
schemaNoSchema name (defaults to public)
tableNameNoTable name (optional filter for get, required for create/drop/set_state)
triggerNameNoTrigger name (required for create/drop/set_state)
functionNameNoFunction name (required for create operation)
timingNoTrigger timing (for create operation, defaults to AFTER)
eventsNoTrigger events (for create operation, defaults to ["INSERT"])
forEachNoFOR EACH ROW or STATEMENT (for create operation, defaults to ROW)
whenNoWHEN clause condition (for create operation)
replaceNoWhether to replace trigger if exists (for create operation)
ifExistsNoInclude IF EXISTS clause (for drop operation)
cascadeNoInclude CASCADE clause (for drop operation)
enableNoWhether to enable the trigger (required for set_state operation)

Implementation Reference

  • src/index.ts:225-257 (registration)
    Central registration of all tools in allTools array, including manageTriggersTools (pg_manage_triggers) at line 233, which is passed to the PostgreSQLServer constructor.
    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
    ];
  • The core handler implementation: the PostgresTool object for pg_manage_triggers, including the execute function that parses input, switches on 'operation' parameter, and delegates to specific helper execute functions for each trigger management action.
    export const manageTriggersTools: PostgresTool = {
      name: 'pg_manage_triggers',
      description: 'Manage PostgreSQL triggers - get, create, drop, and enable/disable triggers. Examples: operation="get" to list triggers, operation="create" with triggerName, tableName, functionName, operation="drop" with triggerName and tableName, operation="set_state" with triggerName, tableName, enable',
      inputSchema: z.object({
        connectionString: z.string().optional().describe('PostgreSQL connection string (optional)'),
        operation: z.enum(['get', 'create', 'drop', 'set_state']).describe('Operation: get (list triggers), create (new trigger), drop (remove trigger), set_state (enable/disable trigger)'),
        
        // Common parameters
        schema: z.string().optional().describe('Schema name (defaults to public)'),
        tableName: z.string().optional().describe('Table name (optional filter for get, required for create/drop/set_state)'),
        
        // Trigger identification (for create/drop/set_state)
        triggerName: z.string().optional().describe('Trigger name (required for create/drop/set_state)'),
        
        // Create trigger parameters
        functionName: z.string().optional().describe('Function name (required for create operation)'),
        timing: z.enum(['BEFORE', 'AFTER', 'INSTEAD OF']).optional().describe('Trigger timing (for create operation, defaults to AFTER)'),
        events: z.array(z.enum(['INSERT', 'UPDATE', 'DELETE', 'TRUNCATE'])).optional().describe('Trigger events (for create operation, defaults to ["INSERT"])'),
        forEach: z.enum(['ROW', 'STATEMENT']).optional().describe('FOR EACH ROW or STATEMENT (for create operation, defaults to ROW)'),
        when: z.string().optional().describe('WHEN clause condition (for create operation)'),
        replace: z.boolean().optional().describe('Whether to replace trigger if exists (for create operation)'),
        
        // Drop trigger parameters
        ifExists: z.boolean().optional().describe('Include IF EXISTS clause (for drop operation)'),
        cascade: z.boolean().optional().describe('Include CASCADE clause (for drop operation)'),
        
        // Set state parameters
        enable: z.boolean().optional().describe('Whether to enable the trigger (required for set_state operation)')
      }),
      // biome-ignore lint/suspicious/noExplicitAny: <explanation>
      execute: async (args: any, getConnectionStringVal: GetConnectionStringFn): Promise<ToolOutput> => {
        const { 
          connectionString: connStringArg,
          operation,
          schema,
          tableName,
          triggerName,
          functionName,
          timing,
          events,
          forEach,
          when,
          replace,
          ifExists,
          cascade,
          enable
        } = args as {
          connectionString?: string;
          operation: 'get' | 'create' | 'drop' | 'set_state';
          schema?: string;
          tableName?: string;
          triggerName?: string;
          functionName?: string;
          timing?: 'BEFORE' | 'AFTER' | 'INSTEAD OF';
          events?: ('INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE')[];
          forEach?: 'ROW' | 'STATEMENT';
          when?: string;
          replace?: boolean;
          ifExists?: boolean;
          cascade?: boolean;
          enable?: boolean;
        };
    
        try {
          switch (operation) {
            case 'get': {
              const result = await executeGetTriggers({
                connectionString: connStringArg,
                tableName,
                schema: schema || 'public'
              }, getConnectionStringVal);
              const message = tableName 
                ? `Triggers for table ${schema || 'public'}.${tableName}` 
                : `Found ${result.length} triggers in schema ${schema || 'public'}`;
              return { content: [{ type: 'text', text: message }, { type: 'text', text: JSON.stringify(result, null, 2) }] };
            }
    
            case 'create': {
              if (!triggerName || !tableName || !functionName) {
                return { 
                  content: [{ type: 'text', text: 'Error: triggerName, tableName, and functionName are required for create operation' }], 
                  isError: true 
                };
              }
              const result = await executeCreateTrigger({
                connectionString: connStringArg,
                triggerName,
                tableName,
                functionName,
                schema: schema || 'public',
                timing: timing || 'AFTER',
                events: events || ['INSERT'],
                forEach: forEach || 'ROW',
                when,
                replace: replace || false
              }, getConnectionStringVal);
              return { content: [{ type: 'text', text: `Trigger ${result.name} created successfully on ${result.schema}.${result.table}` }, { type: 'text', text: JSON.stringify(result, null, 2) }] };
            }
    
            case 'drop': {
              if (!triggerName || !tableName) {
                return { 
                  content: [{ type: 'text', text: 'Error: triggerName and tableName are required for drop operation' }], 
                  isError: true 
                };
              }
              const result = await executeDropTrigger({
                connectionString: connStringArg,
                triggerName,
                tableName,
                schema: schema || 'public',
                ifExists: ifExists || false,
                cascade: cascade || false
              }, getConnectionStringVal);
              return { content: [{ type: 'text', text: `Trigger ${result.name} dropped successfully from ${result.schema}.${result.table}` }, { type: 'text', text: JSON.stringify(result, null, 2) }] };
            }
    
            case 'set_state': {
              if (!triggerName || !tableName || enable === undefined) {
                return { 
                  content: [{ type: 'text', text: 'Error: triggerName, tableName, and enable are required for set_state operation' }], 
                  isError: true 
                };
              }
              const result = await executeSetTriggerState({
                connectionString: connStringArg,
                triggerName,
                tableName,
                enable,
                schema: schema || 'public'
              }, getConnectionStringVal);
              return { content: [{ type: 'text', text: `Trigger ${result.name} ${result.enabled ? 'enabled' : 'disabled'} on ${result.schema}.${result.table}` }, { type: 'text', text: JSON.stringify(result, null, 2) }] };
            }
    
            default:
              return { 
                content: [{ type: 'text', text: `Error: Unknown operation "${operation}". Supported operations: get, create, drop, set_state` }], 
                isError: true 
              };
          }
    
        } catch (error) {
          const errorMessage = error instanceof McpError ? error.message : (error instanceof Error ? error.message : String(error));
          return { content: [{ type: 'text', text: `Error executing ${operation} operation: ${errorMessage}` }], isError: true };
        }
      }
    };
  • Zod schema for validating input parameters to the pg_manage_triggers tool, supporting all operations via conditional fields.
    inputSchema: z.object({
      connectionString: z.string().optional().describe('PostgreSQL connection string (optional)'),
      operation: z.enum(['get', 'create', 'drop', 'set_state']).describe('Operation: get (list triggers), create (new trigger), drop (remove trigger), set_state (enable/disable trigger)'),
      
      // Common parameters
      schema: z.string().optional().describe('Schema name (defaults to public)'),
      tableName: z.string().optional().describe('Table name (optional filter for get, required for create/drop/set_state)'),
      
      // Trigger identification (for create/drop/set_state)
      triggerName: z.string().optional().describe('Trigger name (required for create/drop/set_state)'),
      
      // Create trigger parameters
      functionName: z.string().optional().describe('Function name (required for create operation)'),
      timing: z.enum(['BEFORE', 'AFTER', 'INSTEAD OF']).optional().describe('Trigger timing (for create operation, defaults to AFTER)'),
      events: z.array(z.enum(['INSERT', 'UPDATE', 'DELETE', 'TRUNCATE'])).optional().describe('Trigger events (for create operation, defaults to ["INSERT"])'),
      forEach: z.enum(['ROW', 'STATEMENT']).optional().describe('FOR EACH ROW or STATEMENT (for create operation, defaults to ROW)'),
      when: z.string().optional().describe('WHEN clause condition (for create operation)'),
      replace: z.boolean().optional().describe('Whether to replace trigger if exists (for create operation)'),
      
      // Drop trigger parameters
      ifExists: z.boolean().optional().describe('Include IF EXISTS clause (for drop operation)'),
      cascade: z.boolean().optional().describe('Include CASCADE clause (for drop operation)'),
      
      // Set state parameters
      enable: z.boolean().optional().describe('Whether to enable the trigger (required for set_state operation)')
    }),
  • Helper function executeGetTriggers used by main handler for listing triggers information.
    async function executeGetTriggers(  input: GetTriggersInput,  getConnectionString: GetConnectionStringFn): Promise<TriggerInfo[]> {  const resolvedConnectionString = getConnectionString(input.connectionString);  const db = DatabaseConnection.getInstance();  const { tableName, schema } = input;    try {    await db.connect(resolvedConnectionString);        let query = `
          SELECT 
            t.tgname AS name,
            c.relname AS "tableName",
            n.nspname AS "tableSchema",
            CASE
              WHEN (t.tgtype & (1<<0)) != 0 THEN 'ROW'
              ELSE 'STATEMENT'
            END AS level,
            CASE
              WHEN (t.tgtype & (1<<1)) != 0 THEN 'BEFORE'
              WHEN (t.tgtype & (1<<6)) != 0 THEN 'INSTEAD OF'
              ELSE 'AFTER'
            END AS timing,
            CASE
              WHEN (t.tgtype & (1<<2)) != 0 THEN 'INSERT'
              WHEN (t.tgtype & (1<<3)) != 0 THEN 'DELETE'
              WHEN (t.tgtype & (1<<4)) != 0 THEN 'UPDATE'
              WHEN (t.tgtype & (1<<5)) != 0 THEN 'TRUNCATE'
              ELSE 'UNKNOWN'
            END AS event,
            p.proname AS function,
            pg_get_triggerdef(t.oid) AS definition,
            -- PostgreSQL version-compatible enabled status
            -- Note: For older PostgreSQL versions without tgenabled, this will always return true
            true AS enabled
          FROM pg_trigger t
          JOIN pg_class c ON t.tgrelid = c.oid
          JOIN pg_namespace n ON c.relnamespace = n.oid
          JOIN pg_proc p ON t.tgfoid = p.oid
          WHERE NOT t.tgisinternal
        `;
        
        const params = [];
        
        if (schema) {
          query += ` AND n.nspname = $${params.length + 1}`;
          params.push(schema);
        }
        
        if (tableName) {
          query += ` AND c.relname = $${params.length + 1}`;
          params.push(tableName);
        }
        
        query += ' ORDER BY c.relname, t.tgname';
        
        const triggers = await db.query<TriggerInfo>(query, params);
        return triggers;
      } catch (error) {
        throw new McpError(ErrorCode.InternalError, `Failed to get trigger information: ${error instanceof Error ? error.message : String(error)}`);
      } finally {
        await db.disconnect();
      }
    }
  • Helper function executeCreateTrigger used by main handler for creating new triggers.
    async function executeCreateTrigger(
      input: CreateTriggerInput,
      getConnectionString: GetConnectionStringFn
    ): Promise<{ name: string; table: string; schema: string; timing: string; events: string[]; function: string }> {
      const resolvedConnectionString = getConnectionString(input.connectionString);
      const db = DatabaseConnection.getInstance();
      const { triggerName, tableName, functionName, schema, timing, events, when, forEach, replace } = input;
      
      try {
        await db.connect(resolvedConnectionString);
        
        const createOrReplace = replace ? 'CREATE OR REPLACE' : 'CREATE';
        const qualifiedTableName = `"${schema}"."${tableName}"`;
        const qualifiedFunctionName = `"${functionName}"`; // Assuming functionName might also need quoting or schema qualification
    
        let sql = `
          ${createOrReplace} TRIGGER "${triggerName}"
          ${timing} ${events.join(' OR ')}
          ON ${qualifiedTableName}
        `;
        
        if (forEach) {
          sql += ` FOR EACH ${forEach}`;
        }
        
        if (when) {
          sql += ` WHEN (${when})`;
        }
        
        sql += ` EXECUTE FUNCTION ${qualifiedFunctionName}()`; // Ensure function has () if it's a procedure/function call
        
        await db.query(sql);
        
        return {
          name: triggerName,
          table: tableName,
          schema,
          timing,
          events,
          function: functionName
        };
      } catch (error) {
        throw new McpError(ErrorCode.InternalError, `Failed to create trigger: ${error instanceof Error ? error.message : String(error)}`);
      } finally {
        await db.disconnect();
      }
    }

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