Skip to main content
Glama
Redsf

mcp-n8n-ops

by Redsf
README.md
# mcp-n8n-ops

[![ci](https://github.com/Redsf/mcp-n8n-ops/actions/workflows/ci.yml/badge.svg)](https://github.com/Redsf/mcp-n8n-ops/actions/workflows/ci.yml)
[![python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
[![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)

An [MCP](https://modelcontextprotocol.io) server for operating an n8n instance.
Ask an AI assistant what is failing, why, and since when — instead of clicking
through the executions list.

> **You:** Anything broken in n8n today?
>
> **Assistant:** *(calls `failing_workflows`)* Two workflows are failing.
> "Invoice sync" has 14 failures since yesterday 09:40, all
> `ECONNREFUSED talking to Google Sheets`. "Cart recovery" failed twice, both
> rate limits. The Sheets one looks like a credential or network problem, not
> a logic bug — the failures start abruptly and share one error.

**Read-only by default.** Tools that change state are not merely refused —
they are *absent from the tool list* unless you start the server with
`--allow-write`. A model cannot be talked into calling a tool it cannot see.

**Zero dependencies.** The MCP stdio transport is implemented directly, so this
runs on any Python 3.9+ with nothing installed — which is what you want for a
tool you drop onto the box next to n8n.

## Install

```bash
pip install git+https://github.com/Redsf/mcp-n8n-ops
```

> Not on PyPI yet, so install from git.

## Configure

Get an API key from n8n: **Settings → n8n API → Create an API key.**

### Claude Desktop / Claude Code

```json
{
  "mcpServers": {
    "n8n-ops": {
      "command": "mcp-n8n-ops",
      "env": {
        "N8N_URL": "https://n8n.example.com",
        "N8N_API_KEY": "n8n_api_..."
      }
    }
  }
}
```

Add `"args": ["--allow-write"]` only if you want the assistant able to activate
and deactivate workflows. Read the [safety](#safety) section first.

### Any other MCP client

It speaks MCP over stdio:

```bash
N8N_URL=https://n8n.example.com N8N_API_KEY=n8n_api_... mcp-n8n-ops
```

## Tools

### Read (always available)

| Tool | Answers |
|---|---|
| `failing_workflows` | **Start here.** Which workflows are failing, ranked by failure count, each with a sample error and the time of the last failure. |
| `health_summary` | Instance overview: workflow count, how many active, recent error rate. |
| `list_executions` | Recent executions, filterable by status or workflow. |
| `get_execution` | One execution, including the actual error message. |
| `list_workflows` | Workflows with id, name, active state, node count, tags. |
| `get_workflow` | One workflow's summary, optionally its node list. |

### Write (only with `--allow-write`)

| Tool | Does |
|---|---|
| `set_workflow_active` | Activates or deactivates a workflow. |

There is deliberately **no delete tool**, and none is planned. Deleting a
workflow or an execution is not something an assistant should be able to do on
your behalf, whatever the flag says.

## Aggregation happens server-side

`failing_workflows` returns a small ranked answer, not 200 raw executions for
the model to tally:

```json
{
  "failed_executions_examined": 16,
  "distinct_workflows_failing": 2,
  "workflows": [
    {
      "workflow_id": "1",
      "workflow_name": "Invoice sync",
      "failures": 14,
      "last_failure": "2026-07-21T10:00:00Z",
      "sample_error": "ECONNREFUSED talking to Google Sheets"
    }
  ]
}
```

Counting in Python instead of in the model is faster, cheaper, and it does not
get the arithmetic wrong.

## Safety

This server is driven by a model, which changes what "safe defaults" means.

- **Read-only unless you opt in.** Write tools are hidden, not just blocked.
  Asked for anyway, the server explains why rather than returning a bare
  "unknown tool", so nobody wastes time hunting a typo.
- **No delete, at any privilege level.**
- **The API key is never returned.** It goes out in a header and never appears
  in a tool result or an error message.
- **n8n failures are tool errors, not crashes.** An unreachable instance or a
  bad key comes back as `isError` with a readable message, and the server keeps
  serving. A dead n8n should not take down the assistant's session.
- **stdout carries protocol only.** All logging goes to stderr. Writing anything
  else to stdout corrupts the JSON-RPC stream — the most common way a
  hand-written MCP server breaks.

A read-only key is still a broad grant: it can read every workflow, including
node parameters. Give this server its own key, and revoke it when you are done.

## Verifying it works

The test suite drives the real protocol — handshake, notification handling,
malformed frames, tool dispatch — against a fake n8n, so it runs with no
instance and no network:

```bash
pip install -e ".[dev]"
pytest
```

CI additionally starts the compiled entry point as a subprocess against a stub
n8n HTTP server and drives it with real MCP messages over stdio, asserting the
handshake, the tool list, an aggregated diagnosis, and that a write tool is
refused in read-only mode.

## Why not the official MCP SDK?

The SDK requires Python 3.10+. This targets 3.9 so it runs on the long-lived
boxes n8n tends to live on, and stays dependency-free so `pip install` cannot
drag in a transitive CVE on a machine that has production credentials on it.
MCP's stdio transport is newline-delimited JSON-RPC 2.0 — small enough to
implement correctly and test exhaustively, which is what the suite does.

## Related

- [n8n-workflow-linter](https://github.com/Redsf/n8n-workflow-linter) — catch problems before deploy
- [agent-evals](https://github.com/Redsf/agent-evals) — check behaviour before users do
- [automation-roi-toolkit](https://github.com/Redsf/automation-roi-toolkit) — decide what to build
- [same-system-three-ways](https://github.com/Redsf/same-system-three-ways) — n8n vs Python vs TypeScript, measured

## License

MIT — see [LICENSE](LICENSE).