Skip to main content
Glama
jimsimoy

n8n MCP

README.md
# n8n MCP over HTTPS — Remote n8n Workflow Control for AI Assistants

<div align="center">

<img src="https://img.shields.io/badge/python-3.10%2B-blue.svg?style=flat-square" alt="Python 3.10+">
<a href="https://github.com/jimsimoy/n8n-mcp/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License: MIT"></a>
<a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-compatible-green.svg?style=flat-square" alt="MCP Compatible"></a>
<img src="https://img.shields.io/badge/tools-6-brightgreen.svg?style=flat-square" alt="6 Tools">
<img src="https://img.shields.io/badge/transport-streamable%20http-lightgrey.svg?style=flat-square" alt="Transport: Streamable HTTP">
<img src="https://img.shields.io/badge/auth-bearer%20token-orange.svg?style=flat-square" alt="Auth: Bearer Token">

**6 tools for inspecting and controlling an n8n instance's workflows and executions — over a standard HTTPS MCP endpoint, reachable from any client, anywhere, behind a bearer token.**

by [Jan Ivan Simoy](https://github.com/jimsimoy)

</div>

---

## What is this?

n8n MCP is a [Model Context Protocol](https://modelcontextprotocol.io) server that gives AI assistants structured access to [n8n](https://n8n.io)'s [Public REST API](https://docs.n8n.io/api/) — listing and inspecting workflows, flipping them active/inactive, and reading execution history.

It speaks MCP's **streamable HTTP** transport, so any client that supports remote MCP servers can point at a URL and connect. No subprocess, no SSH, no client-side install — which also means it works from clients that can't spawn local processes at all.

Because the endpoint is reachable from anywhere, it carries **its own auth**: a static bearer token that every request must present. Requests without it get a `401` before touching n8n.

> **Looking for the no-public-endpoint version?** Its sibling project **[n8n-mcp-via-ssh](https://github.com/jimsimoy/n8n-mcp-via-ssh)** exposes the same 6 tools over SSH/stdio, where SSH access *is* the auth boundary and nothing new listens publicly. Same tools, opposite trade-off — pick by whether you need reach or minimal exposure.

**Supported platform:** any MCP client that supports remote HTTP servers, talking to a server running Python 3.10+ (or Docker).

---

## Tools

| Category | Tools | What you can do |
|---|---|---|
| **Workflows** | 4 | List all workflows, fetch one's full detail, turn its triggers on or off |
| **Executions** | 2 | List recent execution history (optionally filtered to one workflow), fetch full detail for one execution |

<details>
<summary>Full tool reference</summary>

| Tool | Description |
|---|---|
| `list_workflows` | id, name, and active status for every workflow |
| `get_workflow(workflow_id)` | full detail for one workflow |
| `activate_workflow(workflow_id)` | turn a workflow's triggers on |
| `deactivate_workflow(workflow_id)` | turn a workflow's triggers off |
| `list_executions(workflow_id?, limit=20)` | recent execution history |
| `get_execution(execution_id)` | full detail for one execution |

</details>

There's deliberately no generic "run this workflow now" tool — n8n's Public API doesn't expose a clean manual-trigger endpoint for arbitrary workflows. If a workflow has a webhook trigger, running it is just an HTTP call to that webhook's URL; that's a detail of your own workflow, not something this server fabricates.

---

## Requirements

| Requirement | Version |
|---|---|
| Docker + Compose | any current version (or Python 3.10+ for the bare-process path) |
| n8n | Public API enabled (Settings → n8n API → Create an API key) |
| Reverse proxy | anything that terminates TLS — Caddy, nginx, Traefik |
| Domain | a hostname pointed at your server |

---

## Installation

```bash
git clone https://github.com/jimsimoy/n8n-mcp.git
cd n8n-mcp
cp .env.example .env && chmod 600 .env
```

Fill in `.env`:

```bash
# generate a bearer token — the server refuses to start without one
openssl rand -hex 32
```

| Variable | What to set it to |
|---|---|
| `N8N_BASE_URL` | `http://n8n:5678` if sharing a Docker network with n8n; `http://127.0.0.1:5678` for a bare process on n8n's host |
| `N8N_API_KEY` | n8n UI → Settings → n8n API → Create an API key |
| `MCP_AUTH_TOKEN` | the `openssl rand -hex 32` output |
| `MCP_ALLOWED_HOSTS` | your public hostname, e.g. `mcp.example.com` |
| `MCP_ALLOWED_ORIGINS` | `https://mcp.example.com` |

Then bring it up:

```bash
docker compose up -d --build
docker compose logs -f
```

The container publishes to `127.0.0.1:8097` — **loopback only**. Your reverse proxy is what puts it on the internet, over TLS.

<details>
<summary>Without Docker</summary>

```bash
python3 -m venv venv
./venv/bin/pip install -r requirements.txt
# set MCP_HOST=127.0.0.1 in .env, then:
./run.sh
```

</details>

---

## Reverse Proxy

Full walkthrough — Caddy, nginx, DNS, and how to verify each layer by hand — in **[docs/https-access-guide.md](docs/https-access-guide.md)**. Caddy, which handles TLS certificates automatically:

```caddy
mcp.example.com {
	reverse_proxy 127.0.0.1:8097 {
		# MCP replies on long-lived SSE streams; buffering stalls them
		flush_interval -1
		transport http {
			read_timeout 300s
			write_timeout 300s
		}
	}
}
```

Auth deliberately lives in the app, not the proxy — so the token stays out of your shared proxy config and this repo works the same behind any proxy.

---

## Client Setup

**Claude Code:**

```bash
claude mcp add --transport http n8n https://mcp.example.com/mcp \
  --header "Authorization: Bearer YOUR_TOKEN"
```

**Any client using the standard JSON config:**

```json
{
  "mcpServers": {
    "n8n": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}
```

Restart your MCP client after saving. The 6 tools will appear automatically.

---

## Usage Examples

### See what's running

```
List all workflows in n8n and tell me which ones are inactive
```

### Check recent runs

```
Show me the last 10 executions for workflow abc123, and get full detail on any
that failed
```

### Turn something off

```
Deactivate the workflow called "Old Backup Job"
```

---

## Security

This server puts workflow control on the public internet. That's the point of it, and it's also the thing to be deliberate about:

- **The bearer token is the whole boundary.** Generate it with `openssl rand -hex 32` — not a guessable string. The server **refuses to start** if `MCP_AUTH_TOKEN` is unset, so an open endpoint can't happen by accident. Tokens are compared in constant time.
- **`.env` is gitignored and holds two secrets** (the n8n API key and the bearer token). Never commit it. `chmod 600` it.
- **Always terminate TLS.** A bearer token over plain HTTP is a token in cleartext. Never publish this on `:80`.
- **Bind the app to loopback.** The compose file publishes `127.0.0.1:8097` on purpose — the proxy is the only way in.
- **Set `MCP_ALLOWED_HOSTS`/`MCP_ALLOWED_ORIGINS`** to your real hostname. This enables DNS-rebinding protection; requests with a mismatched `Host`/`Origin` get a `421`.
- **`/healthz` is intentionally unauthenticated** so proxies and orchestrators can probe it. It returns `{"status":"ok"}` and nothing else — no version, config, or n8n detail.
- **Scope the n8n key to what you're willing to expose.** It can activate/deactivate workflows and read execution data, which may contain whatever your workflows processed.
- **Rotate by changing `MCP_AUTH_TOKEN` and running `docker compose up -d`**, then update your clients.

---

## Project Structure

```
n8n-mcp/
  server.py           # MCP server, tool definitions, ASGI app assembly
  n8n_client.py       # n8n Public API client (workflows, executions)
  auth.py             # bearer-token ASGI middleware
  Dockerfile
  docker-compose.yml
  run.sh              # bare-process entry point (non-Docker)
  docs/
    https-access-guide.md   # proxy, DNS, TLS, and client wiring
```

The server communicates over MCP's streamable HTTP transport using JSON-RPC 2.0.

---

## A note on testing

Verified end-to-end against a live n8n instance through a real public HTTPS endpoint behind Caddy and Cloudflare: Let's Encrypt certificate issuance, the MCP `initialize` handshake, `list_tools` returning all 6 tools, and `call_tool("list_workflows")` returning real workflow data (16 workflows, a mix of active and inactive) from a production instance. The auth gate was verified in all four states — no token → `401`, wrong token → `401`, correct token → `200`, mismatched `Host` → `421`. `get_workflow`, `activate_workflow`, `deactivate_workflow`, `list_executions`, and `get_execution` all go through the same `n8n_client.py` request/error-handling path.

---

## License

[MIT](./LICENSE) — free to use, modify, and distribute.

---

<div align="center">

[Report a Bug](https://github.com/jimsimoy/n8n-mcp/issues) · [Request a Feature](https://github.com/jimsimoy/n8n-mcp/issues)

</div>