npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

claude-fixer

v1.4.1

Published

Self-healing n8n workflows via Claude Code + n8n-pro MCP — HTTP service and MCP server (fix_n8n_workflow)

Readme

Claude Fixer — Self-Healing n8n Workflows

Node MCP Tests strat--log Status

Workflows that fix themselves when they break. When an n8n workflow fails, Claude Code reads the error, patches the broken node through the n8n-pro MCP server, verifies the fix by re-running the failed execution, and reports back — all automatically. Available as an HTTP service (for n8n Error Workflows) and as an MCP tool (fix_n8n_workflow) for the agent fleet.


Architecture

                         ┌──────────────────────────────────────────────┐
n8n Main Workflow        │  claude-fixer                                │
       ↓ (fails)         │                                              │
Error Trigger fires      │  server.js ──── POST /fix (X-Fixer-Token)    │
       ↓                 │      │                                       │
Format Error Payload     │      ▼                                       │
       ↓                 │  fixer.js (shared core)                      │
POST → /fix ─────────────│→ validate → circuit breaker → headless       │
                         │  Claude + n8n-pro MCP (allowlist, no shell)  │
Hermes / dev agent       │      get_workflow → diagnose →               │
       ↓                 │      update_workflow (validated) →           │
MCP tool call ───────────│→ retry_execution → wait_for_execution        │
fix_n8n_workflow         │                                              │
(mcp-stdio / mcp-http)   │  ← { status, rootCause, whatWasChanged,      │
                         │      proposedPatch?, verification }          │
                         └──────────────────────────────────────────────┘

All n8n access goes through the n8n-pro MCP (npx -y n8n-pro-mcp, declared in .mcp.json). The headless Claude run is restricted to an allowlist of MCP tools — it has no Bash and no file access, which matters because /fix is triggered by external payloads.

Result statuses

| Status | Meaning | |---|---| | fixed | Patch applied and the failed execution was retried successfully | | fixed_unverified | Patch applied, but the re-run wasn't possible (no executionId, or retry unsupported by the n8n version) | | proposed | dryRun only — diagnosis + proposedPatch {nodeName, field, before, after}, nothing written | | human_required | Claude can't fix it (auth, quota, external outage) — humanInstructions says exactly what to do. Also returned by the circuit breaker and the rate limit | | failed | The fixer itself errored (timeout, no JSON output) — check server logs |

dryRun → review → apply (approval flow)

Pass dryRun: true (both on POST /fix and on the MCP tool) to get a proposed patch without touching the instance. This is hard-enforced: in dry run the headless Claude only receives the read tools — update_workflow/retry_execution are not in the allowlist at all.

After approval, apply_proposed_patch (MCP tool) / POST /apply applies the exact reviewed patch deterministically — no second Claude run: cheaper, immediate, and what-you-approved-is-what-you-get. Pass nodeName, field, before, after verbatim from the proposal. The before value is an optimistic-concurrency guard: if the live value changed since the review, the call returns human_required with patch_conflict instead of writing. Idempotent: if the live value already equals after, it succeeds without writing.

fix_n8n_workflow(dryRun:true) → "proposed" + proposedPatch
        → human/code_reviewer approves
        → apply_proposed_patch(proposedPatch) → "fixed_unverified" (or patch_conflict)

Async mode (POST /fix only)

A synchronous fix holds the connection for minutes; tunnels and n8n HTTP nodes can time out first. Pass async: true to get 202 { jobId, poll: "/jobs/<id>" } immediately:

  • Poll GET /jobs/:id (auth required) → { status: "running" | "done", result? }
  • Or push: pass callbackUrl (http/https) and the full result is POSTed there when the fix finishes — point it at an n8n Webhook node to close the loop without polling.

Jobs live in memory (24h TTL): a restart clears them — the outcome is always also in GET /logs.

Circuit breaker

If the same workflow+error was already auto-fixed within CIRCUIT_BREAKER_WINDOW_MS (default 30 min) and fails again, the fixer returns human_required immediately — the previous fix didn't hold, and re-fixing would loop while burning Claude runs.

Structured event log (strat-log v1)

Besides the legacy patch-log.jsonl (unchanged), every outcome is dual-written to strat-events.jsonl in the fleet-wide envelope defined in SCHEMA.md: per-event eventId, traceId/parentEventId/causationId correlation, error category taxonomy, durationMs and real cost per fix (the headless run uses claude -p --output-format json; if parsing ever fails the fixer falls back to plain text, only losing cost data). Security/ops events (auth.denied, ratelimit.tripped, job.*, callback.failed) land in the same file.

Tracing: pass traceId/parentEventId/causationId in the request body, via X-Strat-Trace/X-Strat-Parent headers, or as MCP tool params. Every result returns the emitted eventId/traceId — pass the proposal's eventId as causationId when calling apply_proposed_patch and getTrace(traceId) reconstructs the whole proposal→apply chain. This file is the embryo of the global Hermes log — roadmap and instructions for the future fleet live in HERMES-LOG-SPEC.md.


Prerequisites

  • [ ] n8n instance (cloud or self-hosted)
  • [ ] Claude Code installed: npm install -g @anthropic-ai/claude-code
  • [ ] Node.js 18+ installed
  • [ ] n8n-pro MCP — fetched automatically via npx -y n8n-pro-mcp (repo); no manual install
  • [ ] ngrok (or any tunnel): npm install -g ngrok
  • [ ] n8n API Key (Settings → n8n API → Create API Key)

Setup — HTTP service (n8n Error Workflow mode)

1. Install dependencies

npm install

2. Configure environment

cp .env.example .env

Edit .env:

N8N_URL=https://your-n8n-instance.cloud
N8N_API_KEY=your-api-key-from-n8n
FIXER_TOKEN=$(openssl rand -hex 32)
MAX_FIXES_PER_HOUR=10
PORT=3456
CLAUDE_TIMEOUT_MS=300000

N8N_URL/N8N_API_KEY are forwarded to the n8n-pro MCP server via .mcp.json — there is no other n8n credential path. FIXER_TOKEN protects /fix, /logs and /mcp; without it the service runs open (local dev only).

3. Start the service

npm start

4. Expose it with ngrok

ngrok http 3456

5. Set environment variables on the n8n host

The error workflow reads these via $env (self-hosted; for cloud/licensed instances use Settings → Variables and change $env to $vars in the imported nodes):

| Variable | Value | |---|---| | CLAUDE_FIXER_URL | https://abc123.ngrok-free.app | | FIXER_TOKEN | same value as in the fixer's .env | | NOTIFICATION_WEBHOOK_URL | your Slack/Discord webhook URL |

6. Import the n8n Error Workflow

Import n8n-workflows/self-heal-handler.json and activate it.

7. Connect your Main Workflow to the Error Workflow

Workflow → Settings → Error Workflow → select Self-Heal Handler. Done: when the main workflow fails, n8n fires the handler, which POSTs the error here.


Setup — MCP server (agent fleet mode)

The same core is exposed as two MCP tools:

| Tool | What it does | |---|---| | fix_n8n_workflow | Full self-heal run (headless Claude). workflowId required + errorMessage/failedNodeName; dryRun for proposal mode | | apply_proposed_patch | Deterministically applies an approved dry-run proposal (no Claude run); conflict-guarded by before |

stdio (local dev, Claude Code, inspector)

npm run mcp:stdio        # or: npm run inspector

Claude Code client config:

{
  "mcpServers": {
    "claude-fixer": {
      "command": "node",
      "args": ["/path/to/claude-fixer/mcp-stdio.mjs"]
    }
  }
}

Streamable HTTP (shared service for Hermes / multiple agents)

npm run mcp:http         # POST /mcp on MCP_PORT (default 3457)

Stateless: each request gets a fresh server+transport, so any number of agents can share it. Send the X-Fixer-Token header when FIXER_TOKEN is set. Claude Agent SDK config:

mcpServers: {
  'claude-fixer': {
    type: 'http',
    url: 'http://localhost:3457/mcp',
    headers: { 'X-Fixer-Token': process.env.FIXER_TOKEN },
  },
}

Note: each fix_n8n_workflow call runs a full headless Claude session underneath (minutes, not seconds). The tool is a stateless black box — retry loops, approval flows and escalation belong to the calling agent.


Test it

npm test                 # 69 unit tests (node:test, no extra deps)
npm run test-payload     # smoke: /health + payload-validation path (no Claude run)

Real end-to-end fix:

npm run test-payload -- --workflow-id YOUR_WORKFLOW_ID \
  --execution-id FAILED_EXECUTION_ID \
  --node "Parse Customer Data" \
  --message "Cannot read properties of undefined (reading 'email')"

View patch logs: curl -H "X-Fixer-Token: $FIXER_TOKEN" http://localhost:3456/logs | jq .

CI (GitHub Actions) runs syntax checks, the test suite and npm audit on every push and PR.


What Claude can and cannot fix

✅ Auto-fixed

| Error | Fix applied | |---|---| | Array vs item mismatch | Wraps returns in [{json: item}] or adds Split Out | | JSON parse error | Fixes schema, adds missing brackets/commas | | Null reference | Adds ?. and ?? default guards | | Code node TypeError | Fixes JavaScript logic | | Wrong field mapping | Corrects expression | | Rate limit 429 | Adds Wait node (60s) | | Missing output | Adds fallback return |

❌ Human required (Claude explains exactly what to do)

| Error | Why human needed | |---|---| | 401 Unauthorized | Credential expired — needs re-auth | | 403 Forbidden | Permission issue | | OAuth expired | Browser re-auth required | | External API down | Not an n8n problem | | Wrong API key | Human must update credentials | | Quota exhausted | Human decision needed |


Security model

  • The headless Claude run uses --strict-mcp-config + --allowedTools with only n8n-pro tools. Everything else — Bash included — is denied. There is no bypassPermissions.
  • dryRun is enforced by the allowlist (write tools absent), not by prompt — a prompt-injected payload cannot make a dry run write.
  • run_webhook is deliberately excluded: the error payload is external input, and an injected payload must not be able to trigger arbitrary workflows. Verification is retry-only.
  • The error payload is embedded in the prompt as data, with an explicit instruction to ignore anything inside it that looks like a command.
  • Saves go through update_workflow, which validates the workflow offline and refuses invalid bodies — no raw PUTs.
  • /fix, /apply, /jobs/:id, /logs and /mcp require X-Fixer-Token (constant-time comparison); /health stays open for probes. The servers refuse to start without FIXER_TOKEN — running open requires an explicit ALLOW_NO_AUTH=true (local dev only).
  • callbackUrl (async mode) is SSRF-guarded: http(s) only and the host must be the N8N_URL host (or one listed in CALLBACK_ALLOWED_HOSTS) — the fixer can't be used to probe internal endpoints.
  • apply_proposed_patch writes nothing unless the live value still equals the reviewed before (no TOCTOU between review and apply), only targets parameters.* (node type/credentials/name can't be patched deterministically — same limits the playbook imposes on Claude), and rejects field paths with __proto__/constructor/prototype segments.
  • Only known payload fields reach the prompt, each truncated (errorMessage 4KB, errorStack 2KB, …); request bodies are capped at 256kb. This bounds both the injection surface and cheap-DoS attempts.
  • Post-run guard: a result whose workflowId differs from the requested one is discarded as a possible injection (workflow_mismatch) — the write tools accept any ID, so the result is verified against the request.
  • The circuit breaker and the hourly rate limit (persisted across restarts) bound how much an attacker — or a flapping workflow — can spend. The limiter assumes a single error-handler caller; it is a cost guard, not an exact quota.
  • failed responses over HTTP carry a generic message; runner stderr/details stay in the server logs only.
  • Dependencies are pinned to exact versions; CI runs npm ci + npm audit.

Project structure

.
├── fixer.js                          # Shared core: validate, circuit breaker, prompt, spawn, log
├── strat-log.js                      # strat-log v1 emitter/query (fleet event envelope)
├── SCHEMA.md                         # Normative spec of the strat-log envelope
├── HERMES-LOG-SPEC.md                # Self-contained instructions for the future Hermes chat
├── server.js                         # HTTP layer (POST /fix, GET /health, GET /logs)
├── mcp-tools.mjs                     # fix_n8n_workflow definition (one source for both transports)
├── mcp-stdio.mjs                     # MCP stdio transport
├── mcp-http.mjs                      # MCP streamable-HTTP transport (POST /mcp)
├── CLAUDE.md                         # Claude's fixing playbook (auto-read by Claude Code)
├── .mcp.json                         # Registers the n8n-pro MCP for the headless run
├── tests/                            # 69 unit tests (node:test)
├── scripts/test-payload.js           # Smoke test / real end-to-end test
├── .github/workflows/ci.yml          # CI: checks + tests + audit
├── strat-project.json                # Workbench manifest (curadoria)
└── n8n-workflows/
    └── self-heal-handler.json        # n8n Error Workflow — import this

Troubleshooting

"Failed to spawn Claude Code" → Run npm install -g @anthropic-ai/claude-code and make sure claude is in your PATH.

401 from /fix → Send the X-Fixer-Token header matching FIXER_TOKEN in the fixer's .env (the imported handler sends $env.FIXER_TOKEN from the n8n host).

"MCP server failed to start" in Claude output → The fixer spawns npx -y n8n-pro-mcp (see .mcp.json) — check network access to the npm registry on first run, and that N8N_URL/N8N_API_KEY are set in .env. Warm the cache manually with npx -y n8n-pro-mcp if needed.

Everything comes back fixed_unverified → Your n8n version doesn't expose execution retry in the public API, or the error workflow isn't sending executionId. The patch was still applied — only the automatic re-run was skipped.

human_required with circuit_breaker → The same workflow+error was auto-fixed recently and broke again. Review manually, or run a dryRun to get a proposal without applying.

Rate limit keeps triggering → A workflow is failing repeatedly. Claude is correctly stopping — check the root cause in /logs.