data/skills/n8n-mcp-tools-expert/WORKFLOW_GUIDE.md
Complete guide for creating, updating, and managing n8n workflows.
Requires n8n API: All tools in this guide need N8N_API_URL and N8N_API_KEY configured.
If unavailable, use template examples and validation-only workflows.
Speed: 100-500ms
Use when: Creating new workflows from scratch
Syntax:
n8n_create_workflow({
name: "Webhook to Slack", // Required
nodes: [...], // Required: array of nodes
connections: {...}, // Required: connections object
settings: {...} // Optional: workflow settings
})
Returns: Created workflow with ID
Example:
n8n_create_workflow({
name: "Webhook to Slack",
nodes: [
{
id: "webhook-1",
name: "Webhook",
type: "n8n-nodes-base.webhook", // Full prefix!
typeVersion: 2,
position: [250, 300],
parameters: {
path: "slack-notify",
httpMethod: "POST"
}
},
{
id: "slack-1",
name: "Slack",
type: "n8n-nodes-base.slack",
typeVersion: 2,
position: [450, 300],
parameters: {
resource: "message",
operation: "post",
channel: "#general",
text: "={{$json.body.message}}"
}
}
],
connections: {
"Webhook": {
"main": [[{node: "Slack", type: "main", index: 0}]]
}
}
})
Notes:
activateWorkflow operation)Speed: 50-200ms | Uses: 38,287 (most used tool!)
Use when: Making incremental changes to workflows
Common pattern: 56s average between edits (iterative building!)
Node Operations (7 types):
addNode - Add new noderemoveNode - Remove node by ID or nameupdateNode - Update node properties (use dot notation)patchNodeField - Surgical string edits via strict find/replace (see below)moveNode - Change positionenableNode - Enable disabled nodedisableNode - Disable active nodeConnection Operations (5 types):
8. addConnection - Connect nodes (supports smart params)
9. removeConnection - Remove connection (supports ignoreErrors)
10. rewireConnection - Change connection target
11. cleanStaleConnections - Auto-remove broken connections
12. replaceConnections - Replace entire connections object
Metadata Operations (4 types):
13. updateSettings - Workflow settings
14. updateName - Rename workflow
15. addTag - Add tag
16. removeTag - Remove tag
Activation Operations (2 types):
17. activateWorkflow - Activate workflow for automatic execution
18. deactivateWorkflow - Deactivate workflow
Project Management Operations (1 type):
19. transferWorkflow - Transfer workflow to a different project (enterprise/cloud)
Always include intent for better responses:
n8n_update_partial_workflow({
id: "workflow-id",
intent: "Add error handling for API failures", // Describe what you're doing
operations: [...]
})
IF nodes - Use semantic branch names:
{
type: "addConnection",
source: "IF",
target: "True Handler",
branch: "true" // Instead of sourceIndex: 0
}
{
type: "addConnection",
source: "IF",
target: "False Handler",
branch: "false" // Instead of sourceIndex: 1
}
Switch nodes - Use semantic case numbers:
{
type: "addConnection",
source: "Switch",
target: "Handler A",
case: 0
}
{
type: "addConnection",
source: "Switch",
target: "Handler B",
case: 1
}
Full support for AI workflows:
// Language Model
{
type: "addConnection",
source: "OpenAI Chat Model",
target: "AI Agent",
sourceOutput: "ai_languageModel"
}
// Tool
{
type: "addConnection",
source: "HTTP Request Tool",
target: "AI Agent",
sourceOutput: "ai_tool"
}
// Memory
{
type: "addConnection",
source: "Window Buffer Memory",
target: "AI Agent",
sourceOutput: "ai_memory"
}
// All 8 types:
// - ai_languageModel
// - ai_tool
// - ai_memory
// - ai_outputParser
// - ai_embedding
// - ai_vectorStore
// - ai_document
// - ai_textSplitter
Remove properties by setting them to null:
// Remove a property
{
type: "updateNode",
nodeName: "HTTP Request",
updates: { onError: null }
}
// Migrate from deprecated property
{
type: "updateNode",
nodeName: "HTTP Request",
updates: {
continueOnFail: null, // Remove old
onError: "continueErrorOutput" // Add new
}
}
Use patchNodeField for strict find/replace edits on string fields — code, HTML, email templates, JSON bodies. Unlike updateNode with __patch_find_replace (which silently warns on misses), patchNodeField is strict: it errors if the find string is not found, and errors if multiple matches are found (preventing ambiguous replacements).
When to use which:
patchNodeField — preferred for most string edits. Strict error handling catches mistakes early.updateNode with __patch_find_replace — legacy approach. Tolerant (warns but continues on miss). Use only when you want lenient behavior.Syntax:
{
type: "patchNodeField",
nodeName: "Code", // or nodeId
fieldPath: "parameters.jsCode", // Dot-notation path to the string field
patches: [
{
find: "const limit = 10;",
replace: "const limit = 50;",
replaceAll: false, // Default: false. Set true to replace all occurrences
regex: false // Default: false. Set true to treat find as regex
}
]
}
Examples:
// Basic strict find/replace in code
n8n_update_partial_workflow({
id: "wf-123",
intent: "Update API limit",
operations: [{
type: "patchNodeField",
nodeName: "Code",
fieldPath: "parameters.jsCode",
patches: [{find: "const limit = 10;", replace: "const limit = 50;"}]
}]
})
// Replace all occurrences of a URL
n8n_update_partial_workflow({
id: "wf-123",
intent: "Migrate API domain",
operations: [{
type: "patchNodeField",
nodeName: "Code",
fieldPath: "parameters.jsCode",
patches: [{find: "api.old.com", replace: "api.new.com", replaceAll: true}]
}]
})
// Regex-based replacement (whitespace-insensitive)
n8n_update_partial_workflow({
id: "wf-123",
intent: "Update limit with regex",
operations: [{
type: "patchNodeField",
nodeName: "Code",
fieldPath: "parameters.jsCode",
patches: [{find: "const\\s+limit\\s*=\\s*\\d+", replace: "const limit = 100", regex: true}]
}]
})
// Multiple sequential patches on an email template
n8n_update_partial_workflow({
id: "wf-123",
intent: "Update email footer",
operations: [{
type: "patchNodeField",
nodeName: "Set Email",
fieldPath: "parameters.assignments.assignments.6.value",
patches: [
{find: "© 2025", replace: "© 2026"},
{find: "<p>Unsubscribe</p>", replace: ""}
]
}]
})
Error behavior (this is what makes it strict):
replaceAll → operation fails (ambiguity detected)Security limits:
(a+)+ and overlapping alternations like (\w|\d)+// Activate workflow
n8n_update_partial_workflow({
id: "workflow-id",
intent: "Activate workflow for production",
operations: [{type: "activateWorkflow"}]
})
// Deactivate workflow
n8n_update_partial_workflow({
id: "workflow-id",
intent: "Deactivate workflow for maintenance",
operations: [{type: "deactivateWorkflow"}]
})
n8n_update_partial_workflow({
id: "workflow-id",
intent: "Add transform node after IF condition",
operations: [
// Add node
{
type: "addNode",
node: {
name: "Transform",
type: "n8n-nodes-base.set",
position: [400, 300],
parameters: {}
}
},
// Connect it (smart parameter)
{
type: "addConnection",
source: "IF",
target: "Transform",
branch: "true" // Clear and semantic!
}
]
})
cleanStaleConnections - Remove broken connections:
{type: "cleanStaleConnections"}
rewireConnection - Change target atomically:
{
type: "rewireConnection",
source: "Webhook",
from: "Old Handler",
to: "New Handler"
}
Best-effort mode - Apply what works:
n8n_update_partial_workflow({
id: "workflow-id",
operations: [...],
continueOnError: true // Don't fail if some operations fail
})
Validate before applying:
n8n_update_partial_workflow({
id: "workflow-id",
operations: [...],
validateOnly: true // Preview without applying
})
Speed: 200-500ms
Use when: Deploying a template directly to n8n instance
n8n_deploy_template({
templateId: 2947, // Required: from n8n.io
name: "My Weather to Slack", // Optional: custom name
autoFix: true, // Default: auto-fix common issues
autoUpgradeVersions: true, // Default: upgrade node versions
stripCredentials: true // Default: remove credential refs
})
Returns:
Example:
// Deploy a webhook to Slack template
const result = n8n_deploy_template({
templateId: 2947,
name: "Production Slack Notifier"
});
// Result includes:
// - id: "new-workflow-id"
// - requiredCredentials: ["slack"]
// - fixesApplied: ["typeVersion upgraded", "expression format fixed"]
Speed: Proposals ~2s, fresh generation 5–15s, deploy ~3s
Use when: User describes the workflow in plain English and wants the system to draft (and optionally deploy) it.
⚠️ Hosted-only feature. On self-hosted instances the tool returns
{hosted_only: true}with a redirect message rather than a workflow. For self-hosted, usen8n_deploy_template(templates) orn8n_create_workflow(manual).
It's a multi-step flow with a review checkpoint — proposals/preview are returned first, deployment requires a second call. This avoids deploying low-quality drafts.
// Step 1: Generate proposals (NOT deployed, returns up to 5 candidates)
n8n_generate_workflow({
description: "Slack daily standup reminder at 9am every weekday"
})
// → {
// status: "proposals",
// proposals: [
// {
// id: "uuid-1",
// name: "Daily Standup Reminder",
// description: "...",
// flow_summary: "Schedule trigger → Slack message",
// credentials_needed: ["slackApi"]
// },
// ...
// ]
// }
// Step 2: Deploy the proposal you picked
n8n_generate_workflow({
description: "Slack daily standup reminder at 9am every weekday",
deploy_id: "uuid-1"
})
// → { status: "deployed", workflow_id, workflow_name, workflow_url,
// node_count, node_summary }
Use when none of the proposals match what you want.
// Step 1: Bypass proposals; get a fresh preview (NOT deployed)
n8n_generate_workflow({
description: "Webhook receives JSON, transforms it, POSTs to a REST API",
skip_cache: true
})
// → { status: "preview", ... }
// Step 2: Deploy the preview
n8n_generate_workflow({
description: "Webhook receives JSON, transforms it, POSTs to a REST API",
confirm_deploy: true
})
// → { status: "deployed", ... }
The quality of the generated workflow is bound by the clarity of the description. Always include:
webhook, schedule (with cadence — "every 15 min", "weekdays at 9am"), manual, form, chatBad: "Send a notification when something happens"
Good: "When a new row is added to the 'leads' Postgres table, enrich it with Clearbit, then post a summary to the #sales Slack channel. Skip rows where 'company' is empty."
| Parameter | Type | Use |
|---|---|---|
description | string (required) | Natural-language description |
deploy_id | string | ID of a proposal from a prior call — deploys it |
skip_cache | boolean | Skip proposals; generate from scratch and return a preview |
confirm_deploy | boolean | Deploy the most recent preview from this session |
n8n_deploy_template or n8n_create_workflowdeploy_id or confirm_deploy, nothing exists in n8nAlways validate after deploying:
n8n_generate_workflow({description: "...", deploy_id: "uuid-1"})
// → workflow_id: "abc"
n8n_validate_workflow({id: "abc"})
// → catches any node-version or connection issues the generator missed
// If issues found, n8n_autofix_workflow can resolve common ones
n8n_autofix_workflow({id: "abc", applyFixes: true})
Use when: Managing workflow history, rollback, cleanup
n8n_workflow_versions({
mode: "list",
workflowId: "workflow-id",
limit: 10
})
n8n_workflow_versions({
mode: "get",
versionId: 123
})
n8n_workflow_versions({
mode: "rollback",
workflowId: "workflow-id",
versionId: 123, // Optional: specific version
validateBefore: true // Default: validate before rollback
})
// Delete specific version
n8n_workflow_versions({
mode: "delete",
workflowId: "workflow-id",
versionId: 123
})
// Delete all versions for workflow
n8n_workflow_versions({
mode: "delete",
workflowId: "workflow-id",
deleteAll: true
})
n8n_workflow_versions({
mode: "prune",
workflowId: "workflow-id",
maxVersions: 10 // Keep 10 most recent
})
Use when: Testing workflow execution
Auto-detects trigger type (webhook, form, chat)
// Test webhook workflow
n8n_test_workflow({
workflowId: "workflow-id",
triggerType: "webhook", // Optional: auto-detected
httpMethod: "POST",
data: {message: "Hello!"},
waitForResponse: true,
timeout: 120000
})
// Test chat workflow
n8n_test_workflow({
workflowId: "workflow-id",
triggerType: "chat",
message: "Hello, AI agent!",
sessionId: "session-123" // For conversation continuity
})
Speed: 50-500ms
Use when: Creating, updating, listing, or deleting credentials; discovering credential schemas
list - List all credentials (id, name, type, timestamps)get - Get credential by ID (data field stripped)create - Create credential (requires name, type, data)update - Update credential by ID (name, data, and/or type)delete - Permanently delete credential by IDgetSchema - Discover required fields for a credential typelist and get also accept an optional includeUsage: true flag that attaches workflow-usage info to each credential (see "Find Which Workflows Use a Credential" below).
n8n_manage_credentials({action: "list"})
// → [{id, name, type, createdAt, updatedAt}, ...]
n8n_manage_credentials({action: "get", id: "123"})
// → {id, name, type, ...} (data field stripped for security)
// Falls back to list+filter if GET returns 403/405
n8n's public API has no native "which workflows use credential X" endpoint, so n8n-mcp builds the reverse index for you by scanning workflows client-side. Pass includeUsage: true to either list or get.
// Every credential, with the workflows that reference it
n8n_manage_credentials({action: "list", includeUsage: true})
// → {
// credentials: [
// {
// id: "123",
// name: "Production Slack",
// type: "slackApi",
// createdAt: "...", updatedAt: "...",
// usedIn: [
// {id: "wf_abc", name: "Daily digest", active: true},
// {id: "wf_xyz", name: "Alert fan-out", active: false}
// ],
// usageCount: 2
// },
// ...
// ],
// count: N,
// // usageScanError: "..." // present only if the workflow scan failed
// }
// One credential, with its workflow references
n8n_manage_credentials({action: "get", id: "123", includeUsage: true})
// → Same shape as `get` plus usedIn and usageCount.
// On scan failure: response sets usageScanError and omits usedIn/usageCount.
When to use it:
delete: confirm nothing references the credentialupdate: see exactly which workflows you'll affectn8n_audit_instance flags a credential: locate the workflows that need remediationBehavior and limits:
n8n_audit_instance)usageScanError field rather than failing the whole calln8n_manage_credentials({
action: "getSchema",
type: "httpHeaderAuth"
})
// → Required fields, types, descriptions for this credential type
n8n_manage_credentials({
action: "create",
name: "My Slack Token",
type: "slackApi",
data: {accessToken: "xoxb-your-token"}
})
// → Created credential (data field stripped from response)
n8n_manage_credentials({
action: "update",
id: "123",
name: "Updated Slack Token",
data: {accessToken: "xoxb-new-token"},
type: "slackApi" // Optional, some n8n versions require it
})
// → Updated credential (data field stripped from response)
n8n_manage_credentials({action: "delete", id: "123"})
// 1. Discover what fields are needed
n8n_manage_credentials({
action: "getSchema",
type: "slackApi"
})
// 2. Create the credential
n8n_manage_credentials({
action: "create",
name: "Production Slack",
type: "slackApi",
data: {accessToken: "xoxb-..."}
})
// 3. Verify it was created
n8n_manage_credentials({action: "list"})
// 1. Check what would break
n8n_manage_credentials({action: "get", id: "123", includeUsage: true})
// → Inspect usedIn — the {id, name, active} of every workflow that references it
// 2a. If nothing depends on it, delete
n8n_manage_credentials({action: "delete", id: "123"})
// 2b. If something does, rotate the secret instead and notify owners
n8n_manage_credentials({
action: "update",
id: "123",
data: {accessToken: "xoxb-new-..."}
})
get, create, and update all strip the data field from responses (defense-in-depth — secrets are never returned)get falls back to list+filter when GET /credentials/:id returns 403/405 (endpoint not in all n8n versions)includeUsage: true triggers a workflow scan that fails, the response includes usageScanError and still returns the base credentials rather than erroring outSpeed: 500-5000ms (scans all workflows)
Use when: Auditing instance security, finding hardcoded secrets, checking for unauthenticated webhooks, verifying error handling
1. Built-in Audit (via n8n's POST /audit API):
credentials, database, nodes, instance, filesystem2. Custom Deep Scan (workflow analysis):
hardcoded_secrets — 50+ regex patterns for API keys/tokens/passwords plus PII detectionunauthenticated_webhooks — Webhook/form triggers without authenticationerror_handling — Workflows with 3+ nodes and no error handlingdata_retention — Workflows saving all execution data// Full audit (default)
n8n_audit_instance()
// Built-in audit only
n8n_audit_instance({
categories: ["credentials", "nodes", "instance"],
includeCustomScan: false
})
// Custom scan only — specific checks
n8n_audit_instance({
customChecks: ["hardcoded_secrets", "unauthenticated_webhooks"]
})
// Custom abandoned workflow threshold
n8n_audit_instance({
daysAbandonedWorkflow: 90
})
Returns an actionable markdown report with:
Detected secrets are masked in output — shows first 6 + last 4 characters only. Raw values are never stored or returned.
auto_fixable — Can be fixed with MCP tools (e.g., add webhook auth)review_recommended — Needs human judgment (e.g., PII detection)user_input_needed — Requires user decision (e.g., choose auth method)user_action_needed — Manual action required (e.g., rotate exposed API key)Use when: Validating workflow stored in n8n
n8n_validate_workflow({
id: "workflow-id",
options: {
validateNodes: true,
validateConnections: true,
validateExpressions: true,
profile: "runtime"
}
})
Use when: Retrieving workflow details
Modes:
full (default) - Complete workflow JSONdetails - Full + execution statsstructure - Nodes + connections onlyminimal - ID, name, active, tags// Full workflow
n8n_get_workflow({id: "workflow-id"})
// Just structure
n8n_get_workflow({id: "workflow-id", mode: "structure"})
// Minimal metadata
n8n_get_workflow({id: "workflow-id", mode: "minimal"})
Use when: Managing workflow executions
n8n_executions({
action: "get",
id: "execution-id",
mode: "summary" // preview, summary, filtered, full, error
})
// Error mode for debugging
n8n_executions({
action: "get",
id: "execution-id",
mode: "error",
includeStackTrace: true
})
n8n_executions({
action: "list",
workflowId: "workflow-id",
status: "error", // success, error, waiting
limit: 100
})
n8n_executions({
action: "delete",
id: "execution-id"
})
Standard pattern:
1. CREATE
n8n_create_workflow({...})
→ Returns workflow ID
2. VALIDATE
n8n_validate_workflow({id})
→ Check for errors
3. EDIT (iterative! 56s avg between edits)
n8n_update_partial_workflow({id, intent: "...", operations: [...]})
→ Make changes
4. VALIDATE AGAIN
n8n_validate_workflow({id})
→ Verify changes
5. ACTIVATE
n8n_update_partial_workflow({
id,
intent: "Activate workflow",
operations: [{type: "activateWorkflow"}]
})
→ Workflow now runs on triggers!
6. MONITOR
n8n_executions({action: "list", workflowId: id})
n8n_executions({action: "get", id: execution_id})
n8n_update_partial_workflow({...})
// ↓ 23s (thinking about what to validate)
n8n_validate_workflow({id})
n8n_validate_workflow({id})
// ↓ 58s (fixing errors)
n8n_update_partial_workflow({...})
update → update → update → ... (56s avg between edits)
This shows: Workflows are built iteratively, not in one shot!
n8n_deploy_template for quick startsMost Important:
activateWorkflow operation)Additional Tools:
n8n_deploy_template - Deploy templates directlyn8n_workflow_versions - Version control & rollbackn8n_test_workflow - Trigger executionn8n_executions - Manage executionsn8n_manage_datatable - Data table and row managementn8n_manage_credentials - Credential CRUD + schema discoveryn8n_audit_instance - Security audit (built-in + custom scan)n8n_delete_workflow - Permanently delete workflowsn8n_list_workflows - List workflows with filteringn8n_update_full_workflow - Full workflow replacementRelated: