mcpField
N
n8n-connections

n8n-connections

github.com/n8n-ioDevelopment558113 installsUpdated 2w ago
Repository

About n8n-connections

n8n-connections is a wiring guide for n8n workflow builders. It explains SDK connection patterns, highlights a syntax trap that can silently drop wires, and recommends checking connections after creating or updating a workflow.

Key features

  1. 01Explains linear and multi-branch wiring syntax
  2. 02Covers IF, Switch, and error branches
  3. 03Handles fan-in, fan-out, and Merge inputs
  4. 04Checks connections after create or update
  5. 05Provides scenario-specific reference guides

Use cases

  1. 01Wire different outputs from IF or Switch
  2. 02Route multiple nodes into a Merge node
  3. 03Configure an error-handling branch
  4. 04Check for missing wires before publishing

MCP support

The skill guides users through creating or updating workflows with n8n MCP tools, then checking the connections object with get_workflow_details. It notes that validate_workflow does not detect certain missing wires.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/n8n-io/skills --skill n8n-connections

After installing, type/n8n-connectionsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
ERROR_OUTPUTS.md
FAN_OUT_FAN_IN.md
MERGE_INDEX_RULES.md
VERIFICATION.md

Details

From the project README

n8n Connections

n8n's SDK has a small connection grammar. Two of its shapes silently produce broken workflows that pass validation. This skill is mostly about not falling into those traps.

The non-negotiable: the .to() trap

.to() must go inside .add(), not after.

.add(node.output(0)).to(target)      // ❌ connection silently dropped
.add(node.output(0).to(target))      // ✅

validate_workflow does not catch this. The workflow validates, publishes, and runs without the wire. The bug looks identical to a misconfigured or "not firing" node.

If you've written .add(...).to(...) outside the parens, you have a bug. No exceptions.

The universal connection pattern

One shape covers IF/Switch branches, error outputs, merge inputs, and any generic multi-IO:

.add(source.output(n).to(target))
  • source.output(n): pick the output (0-indexed)
  • .to(target): pick the target (default input 0)
  • .to(target.input(m)): pick a specific input on the target (0-indexed)

Call .add() once per wire. To fan out, repeat .add().

Decision tree

Wiring a connection?
├── Linear (one source → one target, single output, single input)?
│   └── .add(source).to(target). The simple case; .to() outside is fine here
│       because there's no .output(n) selector inside .add()
│
├── Selector involved (.output(n) or composite handlers)?
│   └── .to() MUST go inside .add(). See "the trap" above
│
├── Targeting a specific input slot on a multi-input node (Merge)?
│   └── .add(source.output(n).to(target.input(m)))
│       AND check useDataOfInput. See references/MERGE_INDEX_RULES.md
│
├── Error branch?
│   └── .add(node.output(1).to(handler))
│       AND set onError: 'continueErrorOutput' on the node config.
│       See references/ERROR_OUTPUTS.md
│
└── Fan-out (one source → many targets) or fan-in (many sources → one target)?
    └── See references/FAN_OUT_FAN_IN.md

Composite handlers (.onTrue, .onFalse, .onCase, .onError)

The SDK provides convenience handlers on IF, Switch, and any node with an error output:

.add(ifNode.onTrue(targetA))     // same as .add(ifNode.output(0).to(targetA))
.add(ifNode.onFalse(targetB))    // same as .add(ifNode.output(1).to(targetB))
.add(sw.onCase(2, target))       // same as .add(sw.output(2).to(target))
.add(node.onError(handler))      // same as .add(node.output(1).to(handler))

These compose with .output(n) calls without conflict.

.add(ifNode.onTrue(targetA))
.add(ifNode.output(0).to(targetB))
// Result: BOTH targetA and targetB on IF's main[0]. Composite + .output(n) merge.

The only shape to avoid is the .add(selector).to(target) trap above. That's always wrong, regardless of whether a composite handler ran earlier.

After every create or update: verify

validate_workflow reports valid even when wires are missing. After create_workflow_from_code or update_workflow, pull with get_workflow_details and check the connections object:

  • Each main[i] has the expected set of targets (fan-outs preserved, not collapsed).
  • Merge inputs land on the right indices, and useDataOfInput matches the wiring.
  • Error-output nodes have onError: 'continueErrorOutput' AND main[1] wired to a handler.

If any check fails, the workflow is broken despite passing validation. Fix and re-update.

See references/VERIFICATION.md for the full post-create checklist.

Reference files

Read the file that matches the situation. Don't read all of them:

FileRead when
references/FAN_OUT_FAN_IN.mdOne source → many targets, or many sources → one target
references/MERGE_INDEX_RULES.mdWiring a Merge node, or you see useDataOfInput in node config
references/ERROR_OUTPUTS.mdAdding error handling on an individual node (not error workflow, that's n8n-error-handling)
references/VERIFICATION.mdJust created or updated a workflow with non-trivial connections

Anti-patterns

Anti-patternWhat goes wrongFix
.add(node.output(0)).to(target)Wire silently dropped, validation passesMove .to() inside .add()
Mixing useDataOfInput: "2" with .input(2)Off-by-one, wire feeds the wrong inputUse .input(N - 1) when useDataOfInput: "N". See MERGE_INDEX_RULES.md
Error branch wired without onError: 'continueErrorOutput'Branch is unreachable, and node fails the whole workflow on errorSet onError: 'continueErrorOutput' on the node config
Skipping get_workflow_details after createSilently broken workflows shipAlways pull and inspect after create/update
Reading all four reference files before wiring one connectionWasted contextRead only the file matching the situation

Comments

Markdown supported