Skip to main content
Glama

n8n-MCP

by 88-888
workflow.builder.tsβ€’9.68 kB
import { v4 as uuidv4 } from 'uuid'; // Type definitions export interface INodeParameters { [key: string]: any; } export interface INodeCredentials { [credentialType: string]: { id?: string; name: string; }; } export interface INode { id: string; name: string; type: string; typeVersion: number; position: [number, number]; parameters: INodeParameters; credentials?: INodeCredentials; disabled?: boolean; notes?: string; continueOnFail?: boolean; retryOnFail?: boolean; maxTries?: number; waitBetweenTries?: number; onError?: 'continueRegularOutput' | 'continueErrorOutput' | 'stopWorkflow'; } export interface IConnection { node: string; type: 'main'; index: number; } export interface IConnections { [nodeId: string]: { [outputType: string]: Array<Array<IConnection | null>>; }; } export interface IWorkflowSettings { executionOrder?: 'v0' | 'v1'; saveDataErrorExecution?: 'all' | 'none'; saveDataSuccessExecution?: 'all' | 'none'; saveManualExecutions?: boolean; saveExecutionProgress?: boolean; executionTimeout?: number; errorWorkflow?: string; timezone?: string; } export interface IWorkflow { id?: string; name: string; nodes: INode[]; connections: IConnections; active?: boolean; settings?: IWorkflowSettings; staticData?: any; tags?: string[]; pinData?: any; versionId?: string; meta?: { instanceId?: string; }; } // Type guard for INode validation function isValidNode(node: any): node is INode { return ( typeof node === 'object' && typeof node.id === 'string' && typeof node.name === 'string' && typeof node.type === 'string' && typeof node.typeVersion === 'number' && Array.isArray(node.position) && node.position.length === 2 && typeof node.position[0] === 'number' && typeof node.position[1] === 'number' && typeof node.parameters === 'object' ); } export class WorkflowBuilder { private workflow: IWorkflow; private nodeCounter = 0; private defaultPosition: [number, number] = [250, 300]; private positionIncrement = 280; constructor(name = 'Test Workflow') { this.workflow = { name, nodes: [], connections: {}, active: false, settings: { executionOrder: 'v1', saveDataErrorExecution: 'all', saveDataSuccessExecution: 'all', saveManualExecutions: true, saveExecutionProgress: true, }, }; } /** * Add a node to the workflow */ addNode(node: Partial<INode> & { type: string; typeVersion: number }): this { const nodeId = node.id || uuidv4(); const nodeName = node.name || `${node.type} ${++this.nodeCounter}`; const fullNode: INode = { ...node, // Spread first to allow overrides id: nodeId, name: nodeName, type: node.type, typeVersion: node.typeVersion, position: node.position || this.getNextPosition(), parameters: node.parameters || {}, }; this.workflow.nodes.push(fullNode); return this; } /** * Add a webhook node (common trigger) */ addWebhookNode(options: Partial<INode> = {}): this { return this.addNode({ type: 'n8n-nodes-base.webhook', typeVersion: 2, parameters: { path: 'test-webhook', method: 'POST', responseMode: 'onReceived', responseData: 'allEntries', responsePropertyName: 'data', ...options.parameters, }, ...options, }); } /** * Add a Slack node */ addSlackNode(options: Partial<INode> = {}): this { return this.addNode({ type: 'n8n-nodes-base.slack', typeVersion: 2.2, parameters: { resource: 'message', operation: 'post', channel: '#general', text: 'Test message', ...options.parameters, }, credentials: { slackApi: { name: 'Slack Account', }, }, ...options, }); } /** * Add an HTTP Request node */ addHttpRequestNode(options: Partial<INode> = {}): this { return this.addNode({ type: 'n8n-nodes-base.httpRequest', typeVersion: 4.2, parameters: { method: 'GET', url: 'https://api.example.com/data', authentication: 'none', ...options.parameters, }, ...options, }); } /** * Add a Code node */ addCodeNode(options: Partial<INode> = {}): this { return this.addNode({ type: 'n8n-nodes-base.code', typeVersion: 2, parameters: { mode: 'runOnceForAllItems', language: 'javaScript', jsCode: 'return items;', ...options.parameters, }, ...options, }); } /** * Add an IF node */ addIfNode(options: Partial<INode> = {}): this { return this.addNode({ type: 'n8n-nodes-base.if', typeVersion: 2, parameters: { conditions: { options: { caseSensitive: true, leftValue: '', typeValidation: 'strict', }, conditions: [ { id: uuidv4(), leftValue: '={{ $json.value }}', rightValue: 'test', operator: { type: 'string', operation: 'equals', }, }, ], combinator: 'and', }, ...options.parameters, }, ...options, }); } /** * Add an AI Agent node */ addAiAgentNode(options: Partial<INode> = {}): this { return this.addNode({ type: '@n8n/n8n-nodes-langchain.agent', typeVersion: 1.7, parameters: { agent: 'conversationalAgent', promptType: 'define', text: '={{ $json.prompt }}', ...options.parameters, }, ...options, }); } /** * Connect two nodes * @param sourceNodeId - ID of the source node * @param targetNodeId - ID of the target node * @param sourceOutput - Output index on the source node (default: 0) * @param targetInput - Input index on the target node (default: 0) * @returns The WorkflowBuilder instance for chaining * @example * builder.connect('webhook-1', 'slack-1', 0, 0); */ connect( sourceNodeId: string, targetNodeId: string, sourceOutput = 0, targetInput = 0 ): this { // Validate that both nodes exist const sourceNode = this.findNode(sourceNodeId); const targetNode = this.findNode(targetNodeId); if (!sourceNode) { throw new Error(`Source node not found: ${sourceNodeId}`); } if (!targetNode) { throw new Error(`Target node not found: ${targetNodeId}`); } if (!this.workflow.connections[sourceNodeId]) { this.workflow.connections[sourceNodeId] = { main: [], }; } // Ensure the output array exists while (this.workflow.connections[sourceNodeId].main.length <= sourceOutput) { this.workflow.connections[sourceNodeId].main.push([]); } // Add the connection this.workflow.connections[sourceNodeId].main[sourceOutput].push({ node: targetNodeId, type: 'main', index: targetInput, }); return this; } /** * Connect nodes in sequence */ connectSequentially(nodeIds: string[]): this { for (let i = 0; i < nodeIds.length - 1; i++) { this.connect(nodeIds[i], nodeIds[i + 1]); } return this; } /** * Set workflow settings */ setSettings(settings: IWorkflowSettings): this { this.workflow.settings = { ...this.workflow.settings, ...settings, }; return this; } /** * Set workflow as active */ setActive(active = true): this { this.workflow.active = active; return this; } /** * Add tags to the workflow */ addTags(...tags: string[]): this { this.workflow.tags = [...(this.workflow.tags || []), ...tags]; return this; } /** * Set workflow ID */ setId(id: string): this { this.workflow.id = id; return this; } /** * Build and return the workflow */ build(): IWorkflow { // Return a deep clone to prevent modifications return JSON.parse(JSON.stringify(this.workflow)); } /** * Get the next node position */ private getNextPosition(): [number, number] { const nodeCount = this.workflow.nodes.length; return [ this.defaultPosition[0] + (nodeCount * this.positionIncrement), this.defaultPosition[1], ]; } /** * Find a node by name or ID */ findNode(nameOrId: string): INode | undefined { return this.workflow.nodes.find( node => node.name === nameOrId || node.id === nameOrId ); } /** * Get all node IDs */ getNodeIds(): string[] { return this.workflow.nodes.map(node => node.id); } /** * Add a custom node type */ addCustomNode(type: string, typeVersion: number, parameters: INodeParameters, options: Partial<INode> = {}): this { return this.addNode({ type, typeVersion, parameters, ...options, }); } /** * Clear all nodes and connections */ clear(): this { this.workflow.nodes = []; this.workflow.connections = {}; this.nodeCounter = 0; return this; } /** * Clone the current workflow builder */ clone(): WorkflowBuilder { const cloned = new WorkflowBuilder(this.workflow.name); cloned.workflow = JSON.parse(JSON.stringify(this.workflow)); cloned.nodeCounter = this.nodeCounter; return cloned; } } // Export a factory function for convenience export function createWorkflow(name?: string): WorkflowBuilder { return new WorkflowBuilder(name); }

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

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