Skip to main content
Glama

apply_ops

Apply multiple graph operations atomically to modify n8n workflows, enabling batch updates to nodes, connections, parameters, and properties in a single transaction.

Instructions

Apply multiple graph operations atomically to a workflow

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
workflowIdYesThe workflow ID
opsYesArray of operations to apply

Implementation Reference

  • src/index.ts:76-101 (registration)
    Tool registration in the list of tools returned by list_tools, including name, description, and input schema definition.
    {
      name: 'apply_ops',
      description: 'Apply multiple graph operations atomically to a workflow',
      inputSchema: {
        type: 'object',
        properties: {
          workflowId: { oneOf: [{ type: 'string' }, { type: 'number' }], description: 'The workflow ID' },
          ops: {
            type: 'array',
            description: 'Array of operations to apply',
            items: {
              type: 'object',
              properties: {
                type: {
                  type: 'string',
                  enum: ['addNode', 'deleteNode', 'updateNode', 'setParam', 'unsetParam', 'connect', 'disconnect', 'setWorkflowProperty', 'addTag', 'removeTag'],
                  description: 'The type of operation'
                }
              },
              required: ['type']
            }
          }
        },
        required: ['workflowId', 'ops'],
      },
    },
  • Main MCP tool handler: resolves workflow ID, delegates to N8nClient.applyOperations, handles success/error response formatting.
    private async handleApplyOps(args: ApplyOpsRequest) {
      const workflowId = this.resolveWorkflowId(args.workflowId);
      const result = await this.n8nClient.applyOperations(workflowId, args.ops);
      
      if (result.success) {
        if (result.workflow) this.withAlias(result.workflow);
        return { content: [{ type: 'text', text: JSON.stringify(jsonSuccess(result.workflow), null, 2) }] };
      } else {
        return { content: [{ type: 'text', text: JSON.stringify(jsonError('Operations failed', 'APPLY_OPS_FAILED', { errors: result.errors }), null, 2) }] };
      }
    }
  • N8nClient method: fetches current workflow, applies ops via processor, updates workflow atomically with error handling.
    async applyOperations(workflowId: string | number, operations: PatchOperation[]): Promise<ApplyOpsResponse> {
      try {
        // Get the current workflow
        const currentWorkflow = await this.getWorkflow(workflowId);
    
        // Apply operations using the processor
        const result = await this.operationsProcessor.applyOperations(currentWorkflow, operations);
    
        if (!result.success) {
          return result;
        }
    
        // If operations were successful, update the workflow
        try {
          const updatedWorkflow = await this.updateWorkflow(workflowId, result.workflow!);
          return {
            success: true,
            workflow: updatedWorkflow
          };
        } catch (error) {
          // Handle version drift or other update errors
          const errorMessage = error instanceof Error ? error.message : 'Failed to update workflow';
          
          // Check if it's a version drift error (this depends on n8n's error response format)
          if (errorMessage.includes('version') || errorMessage.includes('conflict') || errorMessage.includes('409')) {
            return {
              success: false,
              errors: [{
                operationIndex: -1,
                operation: operations[0], // fallback
                error: 'Version drift detected: workflow was modified by another process',
                details: errorMessage
              }]
            };
          }
    
          return {
            success: false,
            errors: [{
              operationIndex: -1,
              operation: operations[0], // fallback
              error: `Failed to save workflow: ${errorMessage}`,
              details: error
            }]
          };
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        return {
          success: false,
          errors: [{
            operationIndex: -1,
            operation: operations[0] || { type: 'unknown' } as any,
            error: `Failed to retrieve workflow: ${errorMessage}`,
            details: error
          }]
        };
      }
    }
  • Core operations processor: applies batch of ops atomically to workflow copy, with per-operation error handling and validation.
    export class WorkflowOperationsProcessor {
      
      /**
       * Apply a batch of operations to a workflow atomically
       */
      async applyOperations(workflow: N8nWorkflow, operations: PatchOperation[] | undefined | null): Promise<ApplyOpsResponse> {
        // Create a deep copy of the workflow to work with
        const workflowCopy = JSON.parse(JSON.stringify(workflow)) as N8nWorkflow;
        const errors: OperationError[] = [];
    
        try {
          // Normalize operations to an empty array if undefined/null
          const ops = Array.isArray(operations) ? operations : [];
    
          // Apply each operation
          for (let i = 0; i < ops.length; i++) {
            const operation = ops[i];
            try {
              this.applyOperation(workflowCopy, operation);
            } catch (error) {
              const errorMessage = error instanceof Error ? error.message : 'Unknown error';
              errors.push({
                operationIndex: i,
                operation,
                error: errorMessage,
                details: error instanceof Error ? error.stack : undefined
              });
              
              // Atomic behavior: if any operation fails, return errors without applying changes
              return {
                success: false,
                errors
              };
            }
          }
    
          // All operations succeeded
          return {
            success: true,
            workflow: workflowCopy
          };
        } catch (error) {
          // Unexpected error during processing
          const errorMessage = error instanceof Error ? error.message : 'Unknown error during batch processing';
          return {
            success: false,
            errors: [{
              operationIndex: -1,
              operation: (Array.isArray(operations) && operations.length > 0 ? operations[0] : ({ type: 'unknown' } as any)),
              error: errorMessage
            }]
          };
        }
      }
  • Type definitions for ApplyOpsRequest, OperationError, and ApplyOpsResponse used throughout the implementation.
    export interface ApplyOpsRequest {
      workflowId: string | number;
      ops: PatchOperation[];
    }
    
    export interface OperationError {
      operationIndex: number;
      operation: PatchOperation;
      error: string;
      details?: any;
    }
    
    export interface ApplyOpsResponse {
      success: boolean;
      workflow?: N8nWorkflow;
      errors?: OperationError[];
    }

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/get2knowio/n8n-mcp'

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