Skip to main content
Glama

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
NameRequiredDescriptionDefault
nameYesWorkflow name (use dashes, not underscores)
workflowYesThe workflow JSON object
projectNoOptional project name (only for multi-project repos)

Implementation Reference

  • 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}`);
      }
    }
  • 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'],
      },
    },
  • 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
      );
    });
  • 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
      );
Behavior3/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 adds useful context about filename conventions ('use dashes, not underscores') and node requirements ('REAL nodes only'), which aren't captured in the schema. However, it doesn't mention important behavioral aspects like whether this operation is idempotent, what permissions are required, or what happens on failure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately concise with two sentences that each serve distinct purposes: the first states the core functionality, the second provides important constraints. It's front-loaded with the main purpose, though the filename convention could be more integrated with the first sentence.

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

Completeness3/5

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

For a creation tool with no annotations and no output schema, the description provides adequate basic information about what the tool does and some constraints. However, it doesn't address important contextual aspects like what the return value looks like, error conditions, or how this tool relates to other workflow management tools in the sibling list.

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 three parameters thoroughly. The description adds some value by reinforcing the filename convention for the 'name' parameter, but doesn't provide additional semantic context beyond what's in the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Create a new n8n workflow') and specifies the resource type ('workflow'), which distinguishes it from other creation tools like 'create_module'. However, it doesn't explicitly differentiate from all sibling tools like 'generate' or 'generate_app' that might also create workflows.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides some usage context by specifying 'REAL nodes only (no mock/placeholder nodes allowed)', which implies when to use this tool versus alternatives that might create mock workflows. However, it doesn't explicitly state when to choose this over other creation-related tools like 'generate' or 'create_module'.

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/mckinleymedia/mcflow-mcp'

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