Skip to main content
Glama
tsmztech

Salesforce MCP Server

salesforce_write_apex_trigger

Create or update Apex triggers in Salesforce to automate business logic on database operations.

Instructions

Create or update Apex triggers in Salesforce.

Examples:

  1. Create a new Apex trigger: { "operation": "create", "triggerName": "AccountTrigger", "objectName": "Account", "apiVersion": "58.0", "body": "trigger AccountTrigger on Account (before insert, before update) { /* implementation */ }" }

  2. Update an existing Apex trigger: { "operation": "update", "triggerName": "AccountTrigger", "body": "trigger AccountTrigger on Account (before insert, before update, after update) { /* updated implementation */ }" }

Notes:

  • The operation must be either 'create' or 'update'

  • For 'create' operations, triggerName, objectName, and body are required

  • For 'update' operations, triggerName and body are required

  • apiVersion is optional for 'create' (defaults to the latest version)

  • The body must be valid Apex trigger code

  • The triggerName in the body must match the triggerName parameter

  • The objectName in the body must match the objectName parameter (for 'create')

  • Status information is returned after successful operations

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
operationYesWhether to create a new trigger or update an existing one
triggerNameYesName of the Apex trigger to create or update
objectNameNoName of the Salesforce object the trigger is for (required for 'create')
apiVersionNoAPI version for the Apex trigger (e.g., '58.0')
bodyYesFull body of the Apex trigger

Implementation Reference

  • Core handler function that validates arguments, performs create/update operations on ApexTriggers using Salesforce Tooling API, and returns formatted success/error responses.
    export async function handleWriteApexTrigger(conn: any, args: WriteApexTriggerArgs) {
      try {
        // Validate inputs
        if (!args.triggerName) {
          throw new Error('triggerName is required');
        }
        
        if (!args.body) {
          throw new Error('body is required');
        }
        
        // Check if the trigger name in the body matches the provided triggerName
        const triggerNameRegex = new RegExp(`\\btrigger\\s+${args.triggerName}\\b`);
        if (!triggerNameRegex.test(args.body)) {
          throw new Error(`The trigger name in the body must match the provided triggerName: ${args.triggerName}`);
        }
        
        // Handle create operation
        if (args.operation === 'create') {
          console.error(`Creating new Apex trigger: ${args.triggerName}`);
          
          // Validate object name for create operation
          if (!args.objectName) {
            throw new Error('objectName is required for creating a new trigger');
          }
          
          // Check if the object name in the body matches the provided objectName
          const objectNameRegex = new RegExp(`\\bon\\s+${args.objectName}\\b`);
          if (!objectNameRegex.test(args.body)) {
            throw new Error(`The object name in the body must match the provided objectName: ${args.objectName}`);
          }
          
          // Check if trigger already exists
          const existingTrigger = await conn.query(`
            SELECT Id FROM ApexTrigger WHERE Name = '${args.triggerName}'
          `);
          
          if (existingTrigger.records.length > 0) {
            throw new Error(`Apex trigger with name '${args.triggerName}' already exists. Use 'update' operation instead.`);
          }
          
          // Create the new trigger using the Tooling API
          const createResult = await conn.tooling.sobject('ApexTrigger').create({
            Name: args.triggerName,
            TableEnumOrId: args.objectName,
            Body: args.body,
            ApiVersion: args.apiVersion || '58.0', // Default to latest if not specified
            Status: 'Active'
          });
          
          if (!createResult.success) {
            throw new Error(`Failed to create Apex trigger: ${createResult.errors.join(', ')}`);
          }
          
          return {
            content: [{ 
              type: "text", 
              text: `Successfully created Apex trigger: ${args.triggerName}\n\n` +
                    `**ID:** ${createResult.id}\n` +
                    `**Object:** ${args.objectName}\n` +
                    `**API Version:** ${args.apiVersion || '58.0'}\n` +
                    `**Status:** Active`
            }]
          };
        } 
        // Handle update operation
        else if (args.operation === 'update') {
          console.error(`Updating Apex trigger: ${args.triggerName}`);
          
          // Find the existing trigger
          const existingTrigger = await conn.query(`
            SELECT Id, TableEnumOrId FROM ApexTrigger WHERE Name = '${args.triggerName}'
          `);
          
          if (existingTrigger.records.length === 0) {
            throw new Error(`No Apex trigger found with name: ${args.triggerName}. Use 'create' operation instead.`);
          }
          
          const triggerId = existingTrigger.records[0].Id;
          const objectName = existingTrigger.records[0].TableEnumOrId;
          
          // Check if the object name in the body matches the existing object
          const objectNameRegex = new RegExp(`\\bon\\s+${objectName}\\b`);
          if (!objectNameRegex.test(args.body)) {
            throw new Error(`The object name in the body must match the existing object: ${objectName}`);
          }
          
          // Update the trigger using the Tooling API
          const updateResult = await conn.tooling.sobject('ApexTrigger').update({
            Id: triggerId,
            Body: args.body
          });
          
          if (!updateResult.success) {
            throw new Error(`Failed to update Apex trigger: ${updateResult.errors.join(', ')}`);
          }
          
          // Get the updated trigger details
          const updatedTrigger = await conn.query(`
            SELECT Id, Name, TableEnumOrId, ApiVersion, Status, LastModifiedDate
            FROM ApexTrigger
            WHERE Id = '${triggerId}'
          `);
          
          const triggerDetails = updatedTrigger.records[0];
          
          return {
            content: [{ 
              type: "text", 
              text: `Successfully updated Apex trigger: ${args.triggerName}\n\n` +
                    `**ID:** ${triggerId}\n` +
                    `**Object:** ${triggerDetails.TableEnumOrId}\n` +
                    `**API Version:** ${triggerDetails.ApiVersion}\n` +
                    `**Status:** ${triggerDetails.Status}\n` +
                    `**Last Modified:** ${new Date(triggerDetails.LastModifiedDate).toLocaleString()}`
            }]
          };
        } else {
          throw new Error(`Invalid operation: ${args.operation}. Must be 'create' or 'update'.`);
        }
      } catch (error) {
        console.error('Error writing Apex trigger:', error);
        return {
          content: [{ 
            type: "text", 
            text: `Error writing Apex trigger: ${error instanceof Error ? error.message : String(error)}` 
          }],
          isError: true,
        };
      }
    }
  • Tool schema definition including name, description, and input validation schema for the salesforce_write_apex_trigger tool.
    export const WRITE_APEX_TRIGGER: Tool = {
      name: "salesforce_write_apex_trigger",
      description: `Create or update Apex triggers in Salesforce.
      
    Examples:
    1. Create a new Apex trigger:
       {
         "operation": "create",
         "triggerName": "AccountTrigger",
         "objectName": "Account",
         "apiVersion": "58.0",
         "body": "trigger AccountTrigger on Account (before insert, before update) { /* implementation */ }"
       }
    
    2. Update an existing Apex trigger:
       {
         "operation": "update",
         "triggerName": "AccountTrigger",
         "body": "trigger AccountTrigger on Account (before insert, before update, after update) { /* updated implementation */ }"
       }
    
    Notes:
    - The operation must be either 'create' or 'update'
    - For 'create' operations, triggerName, objectName, and body are required
    - For 'update' operations, triggerName and body are required
    - apiVersion is optional for 'create' (defaults to the latest version)
    - The body must be valid Apex trigger code
    - The triggerName in the body must match the triggerName parameter
    - The objectName in the body must match the objectName parameter (for 'create')
    - Status information is returned after successful operations`,
      inputSchema: {
        type: "object",
        properties: {
          operation: {
            type: "string",
            enum: ["create", "update"],
            description: "Whether to create a new trigger or update an existing one"
          },
          triggerName: {
            type: "string",
            description: "Name of the Apex trigger to create or update"
          },
          objectName: {
            type: "string",
            description: "Name of the Salesforce object the trigger is for (required for 'create')"
          },
          apiVersion: {
            type: "string",
            description: "API version for the Apex trigger (e.g., '58.0')"
          },
          body: {
            type: "string",
            description: "Full body of the Apex trigger"
          }
        },
        required: ["operation", "triggerName", "body"]
      }
    };
  • src/index.ts:270-286 (registration)
    Switch case registration in the main tool dispatcher that validates input, casts to WriteApexTriggerArgs, and invokes the handler.
    case "salesforce_write_apex_trigger": {
      const triggerArgs = args as Record<string, unknown>;
      if (!triggerArgs.operation || !triggerArgs.triggerName || !triggerArgs.body) {
        throw new Error('operation, triggerName, and body are required for writing Apex trigger');
      }
      
      // Type check and conversion
      const validatedArgs: WriteApexTriggerArgs = {
        operation: triggerArgs.operation as 'create' | 'update',
        triggerName: triggerArgs.triggerName as string,
        objectName: triggerArgs.objectName as string | undefined,
        apiVersion: triggerArgs.apiVersion as string | undefined,
        body: triggerArgs.body as string
      };
    
      return await handleWriteApexTrigger(conn, validatedArgs);
    }
  • src/index.ts:59-59 (registration)
    Registration of the tool schema in the listTools handler for tool discovery.
    WRITE_APEX_TRIGGER,
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively communicates that this is a write/mutation tool ('Create or update'), specifies validation requirements (valid Apex code, parameter matching), mentions default behavior (apiVersion defaults to latest), and indicates what happens after operations (status information returned). It doesn't cover permissions, rate limits, or error handling, but provides substantial operational context.

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 well-structured and front-loaded with the core purpose, followed by practical examples and important notes. Every sentence serves a clear purpose: the opening statement defines the tool, examples demonstrate usage, and notes clarify constraints. There's no redundant or unnecessary content.

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

Completeness4/5

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

For a write/mutation tool with 5 parameters, 100% schema coverage, but no annotations or output schema, the description provides strong context. It covers the tool's purpose, usage patterns, parameter requirements, and behavioral expectations. The main gap is the lack of output details (only mentioning 'status information' without specifics), but given the complexity and schema richness, it's mostly complete.

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?

The schema description coverage is 100%, so the schema already documents all parameters well. The description adds some value through examples showing parameter usage patterns and notes about required/optional fields for different operations, but doesn't provide significant semantic information beyond what's in the schema descriptions. This meets the baseline for high schema coverage.

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: 'Create or update Apex triggers in Salesforce.' It specifies the exact action (create/update) and resource (Apex triggers), and distinguishes it from sibling tools like salesforce_write_apex (for general Apex code) and salesforce_read_apex_trigger (for reading triggers).

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 this tool through examples and notes, explaining the required parameters for 'create' vs 'update' operations. However, it doesn't explicitly state when NOT to use it or mention alternatives like salesforce_write_apex for non-trigger Apex code, though the distinction is implied by the tool name.

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/tsmztech/mcp-server-salesforce'

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