create
Generate n8n workflows with real nodes using proper naming conventions and project organization for automation development.
Instructions
Create a new n8n workflow with REAL nodes only (no mock/placeholder nodes allowed). IMPORTANT: Use dashes in filenames, not underscores (e.g., "my-workflow" not "my_workflow")
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Workflow name (use dashes, not underscores) | |
| workflow | Yes | The workflow JSON object | |
| project | No | Optional project name (only for multi-project repos) |
Implementation Reference
- src/workflows/manager.ts:86-193 (handler)Core implementation of the 'create' tool: handles file creation, validation, auto-fixing, documentation updates, and returns success message.async createWorkflow(name: string, workflow: any, project?: string): Promise<any> { try { // Enforce dash naming convention if (name.includes('_')) { name = name.replace(/_/g, '-'); console.log(`📝 Converting underscores to dashes in filename: ${name}`); } // Initialize workflows structure if needed (first time) const wasInitialized = await this.initializer.initialize(); let targetPath: string; let relativePath: string; // Determine where to create the workflow based on structure if (this.structure.type === 'simple' || !project) { // Simple structure: use ./flows/ (workflowsPath already points to workflows dir) targetPath = path.join(this.workflowsPath, 'flows'); relativePath = `flows/${name}.json`; } else if (this.structure.type === 'multi-project' && project) { // Multi-project structure with project specified targetPath = path.join(this.workflowsPath, project, 'workflows'); relativePath = `${project}/workflows/${name}.json`; } else { // Unknown structure: default to simple with flows targetPath = path.join(this.workflowsPath, 'flows'); relativePath = `flows/${name}.json`; } // Create directory if it doesn't exist await fs.mkdir(targetPath, { recursive: true }); // Generate a succinct, unique name const finalName = await this.generateUniqueName(name, targetPath); // Update the workflow's internal name to match if (workflow.name) { workflow.name = finalName; } // Write temporary file for validation in /tmp const tempPath = `/tmp/mcflow_validate_${Date.now()}_${finalName}.json`; await fs.writeFile(tempPath, stringifyWorkflowFile(workflow)); // Validate and auto-fix if needed const validationResult = await this.validator.validateWorkflow(tempPath); if (!validationResult.valid) { // Try to auto-fix const fixResult = await this.validator.autoFixWorkflow(tempPath); if (fixResult.fixed) { // Re-read the fixed workflow const fixedContent = await fs.readFile(tempPath, 'utf-8'); workflow = JSON.parse(fixedContent); } } // Write the final workflow file with proper formatting const filePath = path.join(targetPath, `${finalName}.json`); await fs.writeFile(filePath, stringifyWorkflowFile(workflow)); // Remove temp file try { await fs.unlink(tempPath); } catch {} // Update the relative path with the final name if (this.structure.type === 'simple' || !project) { relativePath = `workflows/flows/${finalName}.json`; } else if (project) { relativePath = `${project}/workflows/${finalName}.json`; } // Update documentation try { const customInstructions = await this.documenter.getCustomInstructions(); await this.documenter.updateWorkflowDocumentation(finalName, workflow, 'create', customInstructions); // Extract and update credentials const credentials = this.initializer.extractCredentialsFromWorkflow(workflow); if (credentials.size > 0) { await this.initializer.updateEnvExample(credentials); } // Update README with workflow list const allWorkflows = await this.getWorkflowList(); await this.initializer.updateReadmeWorkflowList(allWorkflows); } catch (docError) { console.error('Failed to update documentation:', docError); // Don't fail the workflow creation if documentation fails } return { content: [ { type: 'text', text: `✅ Workflow Created Successfully!\n\n` + `📁 File: ${relativePath}\n` + `📝 Name: ${finalName}\n` + `${project ? `📂 Project: ${project}\n` : ''}` + `\n` + `The workflow has been saved and documented.`, }, ], }; } catch (error) { throw new Error(`Failed to create workflow: ${error}`); } }
- src/tools/registry.ts:33-54 (schema)Input schema and description for the MCP 'create' tool.{ name: 'create', description: 'Create a new n8n workflow with REAL nodes only (no mock/placeholder nodes allowed). IMPORTANT: Use dashes in filenames, not underscores (e.g., "my-workflow" not "my_workflow")', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Workflow name (use dashes, not underscores)', }, workflow: { type: 'object', description: 'The workflow JSON object', }, project: { type: 'string', description: 'Optional project name (only for multi-project repos)', }, }, required: ['name', 'workflow'], }, },
- src/server/mcflow.ts:76-85 (registration)Registers the MCP tools (including 'create') by providing tool definitions and handling tool calls via ToolHandler.this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: getToolDefinitions(), })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { return await this.toolHandler.handleTool( request.params.name, request.params.arguments ); });
- src/tools/handler.ts:35-40 (handler)ToolHandler switch case that routes 'create' tool calls to WorkflowManager.createWorkflow.case 'create': return await this.workflowManager.createWorkflow( args?.name as string, args?.workflow as any, args?.project as string );