Pre-commit, GitHub Actions and MCP

Pre-commit with lefthook

Add this to lefthook.yml in your repository, then run npx lefthook install.

pre-commit:
  parallel: true
  commands:
    lint-workflows:
      glob: '*.json'
      run: npx workflow-lint lint {staged_files} --fail-on error --no-error-on-unmatched-pattern
    format-workflows:
      glob: '*.json'
      run: npx workflow-lint fmt {staged_files} --check --no-error-on-unmatched-pattern

The pre-commit hook blocking a commit: fmt wants three nodes moved and lint lists its findings

Two details matter if you change it:

Pre-commit with husky

# .husky/pre-commit
git diff --cached --name-only -z --diff-filter=d -- '*.json' \
  | xargs -0 -r npx workflow-lint lint --fail-on error --no-error-on-unmatched-pattern
git diff --cached --name-only -z --diff-filter=d -- '*.json' \
  | xargs -0 -r npx workflow-lint fmt --check --no-error-on-unmatched-pattern

GitHub Action

- uses: LudwigGerdes/workflow-lint@main
  with:
    paths: workflows/
    n8n-version: 2.38.3
    fail-on: error        # info | warn | error
    # config: workflow-lint.config.yaml
    # format: github-actions | stylish | json | sarif | junit

GitHub code scanning

- run: npx workflow-lint lint workflows/ --format sarif > workflow-lint.sarif
  continue-on-error: true
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: workflow-lint.sarif

MCP server

workflow-lint-mcp is a stdio MCP server in the same package as the CLI. An AI agent that uses it runs the same checks the CLI does. Claude Code, Cursor and similar clients take this shape:

{
  "mcpServers": {
    "workflow-lint": {
      "command": "npx",
      "args": ["-y", "-p", "workflow-lint", "workflow-lint-mcp"],
      "env": { "WORKFLOW_LINT_N8N_VERSION": "2.38.3" }
    }
  }
}

Tools

ToolReturns
lint_workflow{ path, findings[], summary: { errors, warnings, infos } }
fix_workflow{ path, json, changed, remaining[] }. Safe fixes; unsafe: true adds the rest
format_workflow{ path, json, moved, changed }. stickies: true also resizes sticky notes
list_rulesEvery rule, its level and whether it is fixable
explain_ruleOne rule's reference page

The three workflow tools take exactly one of these inputs:

InputMeaning
jsonThe workflow, as an object or a JSON string
pathA file path relative to the server's working directory. It cannot leave that directory
instance + workflowIdA workflow on your n8n instance. See below

Anything that cannot be linted comes back as an MCP error, never as a clean result.

Reading from your n8n instance

This is off by default. Turn it on by setting both variables:

"env": {
  "WORKFLOW_LINT_N8N_API_KEY": "…",
  "WORKFLOW_LINT_N8N_INSTANCE": "https://n8n.example.com"
}

The server then fetches only from that instance. A request that names any other host is refused before anything is sent, so the API key cannot be steered elsewhere by untrusted workflow content. See SECURITY.md.