UNPKG

n8n

Version:

n8n Workflow Automation Tool

236 lines 11.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createCreateWorkflowFromCodeTool = void 0; const db_1 = require("@n8n/db"); const zod_1 = __importDefault(require("zod")); const constants_1 = require("./constants"); const credentials_auto_assign_1 = require("./credentials-auto-assign"); const mcp_constants_1 = require("../../mcp.constants"); const workflow_validation_utils_1 = require("../workflow-validation.utils"); const workflow_helpers_1 = require("../../../../workflow-helpers"); const inputSchema = { code: zod_1.default .string() .describe(`Full TypeScript/JavaScript workflow code using the n8n Workflow SDK. Must be validated first with ${constants_1.CODE_BUILDER_VALIDATE_TOOL.toolName}.`), name: zod_1.default .string() .max(128) .optional() .describe('Optional workflow name. If not provided, uses the name from the code.'), description: zod_1.default .string() .max(255) .optional() .describe('Short workflow description summarizing what it does (1-2 sentences, max 255 chars).'), projectId: zod_1.default .string() .optional() .describe("Optional project ID to create the workflow in. Use search_projects to find a project by name. Defaults to the user's personal project."), folderId: zod_1.default .string() .optional() .describe('Optional folder ID to create the workflow in. Requires projectId to be set. Use search_folders to find a folder by name within a project.'), }; const outputSchema = { workflowId: zod_1.default.string().describe('The ID of the created workflow'), name: zod_1.default.string().describe('The name of the created workflow'), nodeCount: zod_1.default.number().describe('The number of nodes in the workflow'), url: zod_1.default.string().describe('The URL to open the workflow in n8n'), autoAssignedCredentials: zod_1.default .array(zod_1.default.object({ nodeName: zod_1.default.string().describe('The name of the node that had credentials auto-assigned'), credentialName: zod_1.default.string().describe('The name of the credential that was auto-assigned'), credentialType: zod_1.default.string().describe('The credential type that was auto-assigned'), })) .describe('List of credentials that were automatically assigned to nodes'), note: zod_1.default .string() .optional() .describe('Additional notes about the workflow creation, such as any nodes that were skipped during credential auto-assignment.'), hint: zod_1.default .string() .optional() .describe('Actionable hint for recovering from the error. When present, follow the suggested action before retrying.'), }; const createCreateWorkflowFromCodeTool = (user, workflowCreationService, workflowFinderService, urlService, telemetry, nodeTypes, credentialsService, projectRepository) => ({ name: constants_1.MCP_CREATE_WORKFLOW_FROM_CODE_TOOL.toolName, config: { description: `Create a workflow in n8n from validated SDK code. This tool expects code that already follows the n8n Workflow SDK patterns and has passed ${constants_1.CODE_BUILDER_VALIDATE_TOOL.toolName}. If code fails to parse, call get_sdk_reference, rewrite the code using the reference, validate again, then retry creation.`, inputSchema, outputSchema, annotations: { title: constants_1.MCP_CREATE_WORKFLOW_FROM_CODE_TOOL.displayTitle, readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false, }, }, handler: async ({ code, name, description, projectId, folderId, }) => { const telemetryPayload = { user_id: user.id, tool_name: constants_1.MCP_CREATE_WORKFLOW_FROM_CODE_TOOL.toolName, parameters: { codeLength: code.length, hasName: !!name, hasProjectId: !!projectId, hasFolderId: !!folderId, }, }; if (folderId && !projectId) { const errorMessage = 'projectId is required when folderId is provided'; telemetryPayload.results = { success: false, error: errorMessage }; telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); return { content: [{ type: 'text', text: JSON.stringify({ error: errorMessage }, null, 2) }], structuredContent: { error: errorMessage }, isError: true, }; } let newWorkflow; try { const { ParseValidateHandler, stripImportStatements } = await Promise.resolve().then(() => __importStar(require('@n8n/ai-workflow-builder'))); const handler = new ParseValidateHandler({ generatePinData: false }); const strippedCode = stripImportStatements(code); const result = await handler.parseAndValidate(strippedCode); const workflowJson = result.workflow; newWorkflow = new db_1.WorkflowEntity(); Object.assign(newWorkflow, { name: name ?? workflowJson.name ?? 'Untitled Workflow', ...(description ? { description } : {}), nodes: workflowJson.nodes, connections: workflowJson.connections, settings: { ...workflowJson.settings, executionOrder: 'v1', availableInMCP: true }, pinData: workflowJson.pinData, meta: { ...workflowJson.meta, aiBuilderAssisted: true, builderVariant: 'mcp' }, }); (0, workflow_helpers_1.resolveNodeWebhookIds)(newWorkflow, nodeTypes); (0, credentials_auto_assign_1.stripNullCredentialStubs)(newWorkflow.nodes); let effectiveProjectId = projectId; if (!effectiveProjectId) { const personalProject = await projectRepository.getPersonalProjectForUserOrFail(user.id); effectiveProjectId = personalProject.id; } const { assignments: credentialAssignments, skippedHttpNodes } = await (0, credentials_auto_assign_1.autoPopulateNodeCredentials)(newWorkflow, user, nodeTypes, credentialsService, effectiveProjectId); const savedWorkflow = await workflowCreationService.createWorkflow(user, newWorkflow, { projectId, parentFolderId: folderId, source: 'n8n-mcp', }); const baseUrl = urlService.getInstanceBaseUrl(); const workflowUrl = `${baseUrl}/workflow/${savedWorkflow.id}`; telemetryPayload.results = { success: true, data: { workflowId: savedWorkflow.id, nodeCount: savedWorkflow.nodes.length, }, }; telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); const output = { workflowId: savedWorkflow.id, name: savedWorkflow.name, nodeCount: savedWorkflow.nodes.length, url: workflowUrl, autoAssignedCredentials: credentialAssignments, note: skippedHttpNodes.length ? `HTTP Request nodes (${skippedHttpNodes.join(', ')}) were skipped during credential auto-assignment. Their credentials must be configured manually.` : undefined, }; return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }], structuredContent: output, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (newWorkflow?.id) { let persisted = null; try { persisted = await workflowFinderService.findWorkflowForUser(newWorkflow.id, user, [ 'workflow:read', ]); } catch { } if (persisted) { const baseUrl = urlService.getInstanceBaseUrl(); const workflowUrl = `${baseUrl}/workflow/${persisted.id}`; telemetryPayload.results = { success: true, data: { workflowId: persisted.id, nodeCount: persisted.nodes.length, postSaveError: errorMessage, }, }; telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); const output = { workflowId: persisted.id, name: persisted.name, nodeCount: persisted.nodes.length, url: workflowUrl, autoAssignedCredentials: [], note: `Workflow was created successfully, but a post-save operation failed: ${errorMessage}`, }; return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }], structuredContent: output, }; } } telemetryPayload.results = { success: false, error: errorMessage, }; telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload); const hint = (0, workflow_validation_utils_1.getSdkReferenceHint)(error, { afterReference: `Rewrite the code, call ${constants_1.CODE_BUILDER_VALIDATE_TOOL.toolName} until it returns valid=true, then call ${constants_1.MCP_CREATE_WORKFLOW_FROM_CODE_TOOL.toolName} again.`, }); const output = { error: errorMessage, ...(hint ? { hint } : {}) }; return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }], structuredContent: output, isError: true, }; } }, }); exports.createCreateWorkflowFromCodeTool = createCreateWorkflowFromCodeTool; //# sourceMappingURL=create-workflow-from-code.tool.js.map