n8n MCP
by jimsimoy
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>
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues