n8n-lint
Provides tools for linting n8n workflow exports, detecting reliability issues such as missing error workflows, HTTP requests without retries, retries that swallow errors, unauthenticated webhooks, hardcoded secrets, disabled nodes, default node names, and orphan nodes. It returns machine-readable findings with one-line fixes for each issue.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@n8n-lintlint this n8n workflow export and list any reliability issues"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-n8n-lint
An MCP (Model Context Protocol) server that lints n8n workflow exports for reliability problems: missing error workflows, HTTP calls without retries, retries that swallow errors, unauthenticated webhooks, hardcoded secrets, and a few readability issues.
It reads the exported workflow JSON only. It is deterministic, needs no network access and never calls the n8n API, so it is safe to run on exports you do not want to send anywhere.
Why an MCP server: an AI agent that builds or reviews n8n workflows can call lint_workflow after every change and get machine-readable findings with a one-line fix for each, instead of guessing what "reliable" means.
Install and run
Requires Node.js 20 or newer.
npm install
npm run build
npm test # builds, then runs the vitest suiteThe server speaks MCP over stdio. Run it directly with node dist/index.js, or register it in a client.
Claude Code
claude mcp add n8n-lint -- node /absolute/path/to/mcp-n8n-lint/dist/index.jsAny MCP client (JSON config)
{
"mcpServers": {
"n8n-lint": {
"command": "node",
"args": ["/absolute/path/to/mcp-n8n-lint/dist/index.js"]
}
}
}Related MCP server: n8n-ops-mcp
Tools
Tool | Input | Output |
|
|
|
| none |
|
|
|
|
Bad input (invalid JSON, no nodes array, unknown rule id) is returned as an MCP tool error, not a crash. The error text never quotes the input.
Rules
Id | Name | Severity | What it checks |
R001 | no-error-workflow | warning |
|
R002 | http-no-retry | warning | An |
R003 | retry-with-continue | warning |
|
R004 | webhook-no-auth | warning | An |
R005 | hardcoded-secret | error | A node parameter that looks like a secret: a Bearer token, an |
R006 | disabled-node | info | A node with |
R007 | default-node-name | info | A node still named like its default ( |
R008 | orphan-node | warning | A node (not a trigger, not a sticky note) with no incoming and no outgoing connection. |
R005 never prints the value it found. Findings contain only the parameter path and the kind of match, with the value replaced by ********. Tests plant fake secrets in fixtures and assert that no part of them appears in any output.
Example
Input to lint_workflow (a small workflow with a fake placeholder token):
{
"name": "Order intake",
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": { "httpMethod": "POST", "path": "orders" },
"position": [0, 0]
},
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.example.invalid/orders",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "Authorization", "value": "Bearer fake-bearer-token-0000000000000000" }
]
}
},
"position": [200, 0]
}
],
"connections": {
"Webhook": { "main": [[{ "node": "HTTP Request", "type": "main", "index": 0 }]] }
},
"settings": { "executionOrder": "v1" }
}Output:
{
"summary": { "error": 1, "warning": 3, "info": 1, "total": 5 },
"findings": [
{
"ruleId": "R005",
"severity": "error",
"node": "HTTP Request",
"message": "Parameter \"parameters.headerParameters.parameters[0].value\" looks like a hardcoded secret (literal value of \"Authorization\" header/field): ********",
"fix": "Rotate the secret and move it into an n8n credential; clear \"parameters.headerParameters.parameters[0].value\"."
},
{
"ruleId": "R001",
"severity": "warning",
"node": null,
"message": "No error workflow is configured (settings.errorWorkflow is missing or empty).",
"fix": "Set an error workflow (one that starts with an Error Trigger node) in Workflow Settings."
},
{
"ruleId": "R002",
"severity": "warning",
"node": "HTTP Request",
"message": "HTTP Request node has no retries (retryOnFail is not true).",
"fix": "Turn on Retry On Fail for this node and set Max Tries and Wait Between Tries."
},
{
"ruleId": "R004",
"severity": "warning",
"node": "Webhook",
"message": "Webhook node does not require authentication.",
"fix": "Set Authentication to Basic auth, Header auth or JWT auth on this node."
},
{
"ruleId": "R007",
"severity": "info",
"node": "HTTP Request",
"message": "Node uses a default name (\"HTTP Request\").",
"fix": "Rename the node to describe what it does."
}
]
}How the n8n field names were checked
Field names were checked against docs.n8n.io (October 2026):
Confirmed in the Public API schema for the workflow and node objects:
retryOnFail,maxTries,waitBetweenTries,onError,continueOnFail(marked deprecated, "use onError instead"),disabled,parameters,settings.errorWorkflow(the ID of the workflow that contains the Error Trigger node), andconnections(keyed by source node name).Confirmed in the n8n docs' reference for editing workflows with the n8n MCP tools (
setNodeSettings): theonErrorvaluesstopWorkflow,continueRegularOutput,continueErrorOutput.Confirmed by example workflows embedded in the docs: the node types
n8n-nodes-base.httpRequest,n8n-nodes-base.webhookandn8n-nodes-base.stickyNote, the default nameHTTP Request, and theconnectionsstructure ({ "<source>": { "main": [[ { "node", "type", "index" } ]] } }).Behavior: the Handle errors gracefully page says an error workflow "runs if an execution fails", and the node settings page says that On Error "Continue" proceeds despite the error. That is the basis for R001 and R003.
Pages used: Handle errors gracefully, Work with nodes, Webhook node, and the Public API reference.
Not confirmed by the documentation
These parts are based on observed export format, not documented. The rules are kept, and explain_rule repeats the note:
R004:
parameters.authenticationon the Webhook node, and the value"none". The docs list the options only by their UI labels (Basic auth, Header auth, JWT auth, None) and do not show the JSON key or values. The rule treats an absent key the same as"none", which is consistent with the documented example webhook (it has noauthenticationkey), but this is an inference.R007: default display names other than
HTTP Request(for exampleEdit Fields). The list in the code is incomplete.R008 (and R007): which node types count as triggers is decided by a type-name heuristic (type ends with
Trigger, pluswebhook,start,cron,interval).R002: the docs describe
retryOnFailas an optional boolean without stating its default. A missing field is treated asfalse.R005: not tied to an n8n field. It is a pattern heuristic.
Limitations
Heuristics, not proof. R005 will produce false positives (for example a long identifier that looks like base64, or a placeholder in a
tokenfield) and false negatives (secrets split across fields, encoded, or short). R007 and R008 depend on lists and naming conventions.Export formats change between n8n versions, and the rules only look at the fields listed above. Older exports, exports copied from the editor canvas (which have no
settings), and workflows from other tools may be read differently. Run it on a current export if results look off.Rules do not skip disabled nodes (R006 reports them, R002 to R005 and R008 still look at them), and R001 also flags a workflow that is itself an error handler (one that starts with an Error Trigger).
It reads one JSON document. It does not call the n8n API, check that the referenced error workflow exists, resolve credentials, run the workflow, or follow sub-workflows.
R002 says nothing about whether a call is safe to retry. Retrying non-idempotent requests (payments, emails) can cause duplicates.
It does not replace a human review. It finds a fixed set of common reliability problems, not logic errors.
Only the eight rules above exist.
Development
npm install
npm run build # tsc -> dist/
npm test # build + vitest (unit tests, fixture tests, end-to-end MCP client test)Layout: src/rules.ts holds the rules, src/secrets.ts the secret detection, src/lint.ts the runner, src/server.ts the MCP tools. Test fixtures in tests/fixtures are synthetic workflows written for this project; all keys in them are obviously fake. Each rule has a rNNN-bad.json that triggers only that rule and a rNNN-good.json that does not trigger it.
License
MIT. See LICENSE.
Available Tools
3 toolsexplain_ruleExplain lint ruleA
Explain why a rule exists and how to fix a finding. Also lists fields the rule relies on that the n8n docs do not confirm.
| Name | Required | Description | Default |
|---|---|---|---|
| ruleId | Yes | Rule id, for example "R002" |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It does disclose the content of the response (rationale, fix guidance, and docs-unconfirmed field dependencies), but says nothing about side effects, whether it is read-only, or what happens for an unknown ruleId.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the primary purpose and with no filler. The trailing clause about docs-unconfirmed fields is slightly cryptic but still informative rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter lookup with no output schema and no annotations, the description adequately covers what the tool returns. It leaves gaps only around error behavior for invalid rule ids and explicit relationship to the linting workflow.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single ruleId parameter is fully documented in the schema, including the 'R002' format example, so the description adds no parameter-level information. Baseline 3 applies when schema coverage is 100%.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (explain a lint rule) and adds the useful detail that it covers both rule rationale and remediation of a finding. It is distinguishable from list_rules and lint_workflow by function, though it never names those siblings explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'how to fix a finding' implies the natural trigger point (after lint_workflow surfaces a finding), but there is no explicit when-to-use statement, no when-not-to-use, and no routing to or from the sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lint_workflowLint n8n workflowB
Check an n8n workflow export (JSON) for reliability problems. Pass the workflow as an object or as a JSON string. Returns a severity summary and a list of findings.
| Name | Required | Description | Default |
|---|---|---|---|
| workflow | Yes | n8n workflow export, as an object or a JSON string |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. "Check ... for reliability problems" implies a non-mutating analysis and it discloses the return content (severity summary plus findings), which helps. However, it does not confirm the operation is read-only, that it has no side effects on the workflow or server, or whether the workflow must already exist/be valid before linting.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with what the tool does, followed by how to pass input and what comes back. No filler, though the input-format sentence overlaps with the schema description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter, non-mutating analysis tool with no output schema and full schema coverage, this is nearly sufficient: it explains input form and return shape. The main omission is the relationship to the rule-exploration siblings and any statement of statelessness/side-effect profile.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a single well-documented parameter, so the schema does the heavy lifting. The description's "Pass the workflow as an object or as a JSON string" largely restates the schema's own description and anyOf typing rather than adding new meaning (e.g., size limits or expected export shape). Baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: checking an n8n workflow export for reliability problems, and the title reinforces it. It is clearly distinct in function from list_rules and explain_rule, though it never explicitly differentiates itself from those siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies a use case (validating a workflow) but never states when to reach for this tool versus alternatives, nor any prerequisites such as the workflow needing to be a valid export. With sibling tools list_rules and explain_rule present, guidance about their relationship would have been valuable, and none is given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_rulesList lint rulesB
List all lint rules with id, name, severity and description.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It implies a read-only operation by saying 'List', but does not explicitly state read-only/safe behavior, auth requirements, rate limits, pagination, or ordering.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence that states the action, the resource, and the returned fields with no wasted words. It is appropriately sized for a simple list endpoint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter list tool with no output schema, the description helpfully lists the returned fields (id, name, severity, description). It is mostly complete, though it omits pagination, ordering, and any behavioral context that would help an agent use it confidently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is no parameter semantics to clarify and the baseline score is 4. The description appropriately focuses on the return fields instead.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('List') and resource ('lint rules') and enumerates the returned fields. It is clear what the tool does, but it does not explicitly distinguish itself from sibling tools like explain_rule or lint_workflow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool versus explain_rule or lint_workflow, nor any prerequisites, exclusions, or filtering context. The only implied usage comes from the word 'List', which is not enough to route among siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
explain_rule - First observed
lint_workflow - First observed
list_rules
TDQS
Scored across 3 tools
Each tool has a clearly distinct role: lint_workflow performs the analysis, list_rules enumerates rule metadata, and explain_rule provides rationale and fix guidance for a specific rule. There is no meaningful overlap, and an agent can easily select the right tool from the descriptions alone.
All tools follow a consistent verb_noun pattern in snake_case: lint_workflow, list_rules, explain_rule. This predictability makes the toolset easy to scan and remember.
Three tools is exactly right for this focused linter server: one action tool and two rule-inspection tools. Each tool earns its place without redundancy or unnecessary surface area.
The core workflow of linting a workflow, listing rules, and explaining rules is fully covered. Minor gaps exist around rule configuration (e.g., enabling/disabling rules) or automated fixes, which are common in linters but not strictly necessary for the stated purpose.
Maintenance
Related MCP Connectors
Security scanner for n8n workflows + live MCP Trust-Check. 18 rules, OWASP mapped. Paid x402 API.
Know when your n8n workflows, URLs, and AI apps break, before your customers do.
Audit GitHub Actions workflows for script injection, unpinned actions and missing permissions.
Diagnose AI workflows for failure, security, and handoff risks — RED/AMBER/GREEN per node.
Related MCP Servers
- AlicenseBqualityCmaintenanceSafe MCP server and CLI tool for testing, scoring, and inspecting n8n workflows. Features 19 tools for workflow testing, execution traces, tiered scoring, and a built-in node catalog with 800+ node types. Designed with safety-first approach: no credential management, no secrets lifecycle, no auto-fix loops.191MIT
- AlicenseAqualityBmaintenanceProvides ops-focused n8n tools for MCP-compatible agents, enabling listing, inspecting, triggering, validating, managing tags, running security audits, and safely editing n8n workflows with auto-backup and confirm gates.207 npm1MIT
- FlicenseNot gradedqualityCmaintenanceConverts natural language descriptions into validated, production-ready n8n workflows with automatic error fixing and architecture recommendations.17 npm22-
- AlicenseNot gradedqualityDmaintenanceStatically analyzes exported n8n automation workflows for security issues and returns structured findings.MIT