docs/HTTP_DEPLOYMENT.md
Deploy n8n-MCP as a remote HTTP server to provide n8n knowledge to compatible MCP Client from anywhere.
n8n-MCP HTTP mode enables:
Use stdio mode - Claude Desktop connects directly to the Node.js process:
Claude Desktop ā n8n-mcp (stdio mode)
Run HTTP server locally for testing remote features:
Claude Desktop ā http-bridge.js ā localhost:3000
Deploy to cloud for access from anywhere:
Claude Desktop ā mcp-remote ā https://your-server.com
Server Requirements:
Client Requirements:
# 1. Create environment file
cat > .env << EOF
AUTH_TOKEN=$(openssl rand -base64 32)
MCP_MODE=http
PORT=3000
# Optional: Enable n8n management tools
# N8N_API_URL=https://your-n8n-instance.com
# N8N_API_KEY=your-api-key-here
# Security Configuration (v2.16.3+)
# Rate limiting (default: 20 attempts per 15 minutes)
AUTH_RATE_LIMIT_WINDOW=900000
AUTH_RATE_LIMIT_MAX=20
# SSRF protection mode (default: strict)
# Use 'moderate' for local n8n, 'strict' for production
WEBHOOK_SECURITY_MODE=strict
EOF
# 2. Deploy with Docker
docker run -d \
--name n8n-mcp \
--restart unless-stopped \
--env-file .env \
-p 3000:3000 \
ghcr.io/czlonkowski/n8n-mcp:latest
# 3. Verify deployment
curl http://localhost:3000/health
# 1. Clone and setup
git clone https://github.com/czlonkowski/n8n-mcp.git
cd n8n-mcp
npm install
npm run build
npm run rebuild
# 2. Configure environment
export MCP_MODE=http
export AUTH_TOKEN=$(openssl rand -base64 32)
export PORT=3000
# 3. Start server
npm run start:http
Skip HTTP entirely and use stdio mode directly:
{
"mcpServers": {
"n8n-local": {
"command": "node",
"args": [
"/path/to/n8n-mcp/dist/mcp/index.js"
],
"env": {
"N8N_API_URL": "https://your-n8n-instance.com",
"N8N_API_KEY": "your-api-key-here"
}
}
}
}
š” Save your AUTH_TOKEN - clients will need it to connect!
| Variable | Description | Example |
|---|---|---|
MCP_MODE | Must be set to http | http |
AUTH_TOKEN or AUTH_TOKEN_FILE | Authentication method | See security section |
| Variable | Description | Default | Since |
|---|---|---|---|
PORT | Server port | 3000 | v1.0 |
HOST | Bind address | 0.0.0.0 | v1.0 |
LOG_LEVEL | Log verbosity (error/warn/info/debug) | info | v1.0 |
NODE_ENV | Environment | production | v1.0 |
TRUST_PROXY | Trust proxy headers (0=off, 1+=hops) | 0 | v2.7.6 |
BASE_URL | Explicit public URL | Auto-detected | v2.7.14 |
PUBLIC_URL | Alternative to BASE_URL | Auto-detected | v2.7.14 |
CORS_ORIGIN | CORS allowed origins | * | v2.7.8 |
AUTH_TOKEN_FILE | Path to token file | - | v2.7.10 |
Enable 16 additional tools for managing n8n workflows by configuring API access:
ā ļø Requires v2.7.1+ - Earlier versions had an issue with tool registration in Docker environments.
| Variable | Description | Example |
|---|---|---|
N8N_API_URL | Your n8n instance URL | https://your-n8n.com |
N8N_API_KEY | n8n API key (from Settings > API) | n8n_api_key_xxx |
N8N_API_TIMEOUT | Request timeout (ms) | 30000 |
N8N_API_MAX_RETRIES | Max retry attempts | 3 |
When configured, you get 16 additional tools (total: 39 tools):
Workflow Management (11 tools):
n8n_create_workflow - Create new workflowsn8n_get_workflow - Get workflow by IDn8n_update_full_workflow - Update entire workflown8n_update_partial_workflow - Update using diff operations (v2.7.0+)n8n_delete_workflow - Delete workflowsn8n_list_workflows - List all workflowsExecution Management (4 tools):
n8n_trigger_webhook_workflow - Execute via webhooksn8n_get_execution - Get execution detailsn8n_list_executions - List workflow runsn8n_delete_execution - Delete execution recordsSystem Tools:
n8n_health_check - Check n8n connectivityn8n_diagnostic - System diagnosticsn8n_validate_workflow - Validate from n8n instanceā ļø Security Note: Store API keys securely and never commit them to version control.
āāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāā āāāāāāāāāāāāāāāā
ā Claude Desktop ā stdio ā mcp-remote ā HTTP ā n8n-MCP ā
ā (stdio only) āāāāāāāāāŗā (bridge) āāāāāāāāāŗā HTTP Server ā
āāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāā āāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāā
ā Your n8n ā
ā Instance ā
āāāāāāāāāāāāāāāā
Key Points:
mcp-remote acts as a bridge, converting stdio ā HTTPn8n-MCP intelligently detects your public URL:
Explicit Configuration (highest priority):
BASE_URL=https://n8n-mcp.example.com # Full public URL
# or
PUBLIC_URL=https://api.company.com:8443/mcp
Auto-Detection (when TRUST_PROXY is enabled):
TRUST_PROXY=1 # Required for proxy header detection
# Server reads X-Forwarded-Proto and X-Forwarded-Host
Fallback (local binding):
# No configuration needed
# Shows: http://localhost:3000 (or configured HOST:PORT)
[INFO] Starting n8n-MCP HTTP Server v2.7.17...
[INFO] Server running at https://n8n-mcp.example.com
[INFO] Endpoints:
[INFO] Health: https://n8n-mcp.example.com/health
[INFO] MCP: https://n8n-mcp.example.com/mcp
When running n8n-MCP behind a reverse proxy (Nginx, Traefik, etc.), enable trust proxy to log real client IPs instead of proxy IPs:
# Enable trust proxy in your environment
TRUST_PROXY=1 # Trust 1 proxy hop (standard setup)
# or
TRUST_PROXY=2 # Trust 2 proxy hops (CDN ā Load Balancer ā n8n-mcp)
Without TRUST_PROXY:
[INFO] GET /health { ip: '172.19.0.2' } # Docker internal IP
With TRUST_PROXY=1:
[INFO] GET /health { ip: '203.0.113.1' } # Real client IP
This is especially important when:
All requests require Bearer token authentication:
# Test authentication
curl -H "Authorization: Bearer $AUTH_TOKEN" \
https://your-server.com/health
Use a reverse proxy for SSL termination:
Nginx example:
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /mcp {
proxy_pass http://localhost:3000;
proxy_set_header Authorization $http_authorization;
# Important: Forward client IP headers
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Caddy example (automatic HTTPS):
your-domain.com {
reverse_proxy /mcp localhost:3000
}
ā ļø Requirements: Node.js 18+ must be installed on the client machine for mcp-remote
{
"mcpServers": {
"n8n-remote": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://your-server.com/mcp",
"--header",
"Authorization: Bearer YOUR_AUTH_TOKEN_HERE"
]
}
}
}
Note: Replace YOUR_AUTH_TOKEN_HERE with your actual token. Do NOT use ${AUTH_TOKEN} syntax - Claude Desktop doesn't support environment variable substitution in args.
For local testing or when mcp-remote isn't available:
{
"mcpServers": {
"n8n-local-http": {
"command": "node",
"args": [
"/path/to/n8n-mcp/scripts/http-bridge.js"
],
"env": {
"MCP_URL": "http://localhost:3000/mcp",
"AUTH_TOKEN": "your-auth-token-here"
}
}
}
}
When testing locally with Docker:
{
"mcpServers": {
"n8n-docker-http": {
"command": "node",
"args": [
"/path/to/n8n-mcp/scripts/http-bridge.js"
],
"env": {
"MCP_URL": "http://localhost:3001/mcp",
"AUTH_TOKEN": "docker-test-token"
}
}
}
}
version: '3.8'
services:
n8n-mcp:
image: ghcr.io/czlonkowski/n8n-mcp:latest
container_name: n8n-mcp
restart: unless-stopped
environment:
# Core configuration
MCP_MODE: http
NODE_ENV: production
# Security - Using file-based secret
AUTH_TOKEN_FILE: /run/secrets/auth_token
# Networking
HOST: 0.0.0.0
PORT: 3000
TRUST_PROXY: 1 # Behind Nginx/Traefik
CORS_ORIGIN: https://app.example.com # Restrict in production
# URL Configuration
BASE_URL: https://n8n-mcp.example.com
# Logging
LOG_LEVEL: info
# Optional: n8n API Integration
N8N_API_URL: ${N8N_API_URL}
N8N_API_KEY_FILE: /run/secrets/n8n_api_key
secrets:
- auth_token
- n8n_api_key
ports:
- "127.0.0.1:3000:3000" # Only expose to localhost
volumes:
- n8n-mcp-data:/app/data:ro # Read-only database
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 128M
cpus: '0.1'
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
secrets:
auth_token:
file: ./secrets/auth_token.txt
n8n_api_key:
file: ./secrets/n8n_api_key.txt
volumes:
n8n-mcp-data:
# /etc/systemd/system/n8n-mcp.service
[Unit]
Description=n8n-MCP HTTP Server
Documentation=https://github.com/czlonkowski/n8n-mcp
After=network.target
Requires=network.target
[Service]
Type=simple
User=n8n-mcp
Group=n8n-mcp
WorkingDirectory=/opt/n8n-mcp
# Use file-based secret
Environment="AUTH_TOKEN_FILE=/etc/n8n-mcp/auth_token"
Environment="MCP_MODE=http"
Environment="NODE_ENV=production"
Environment="TRUST_PROXY=1"
Environment="BASE_URL=https://n8n-mcp.example.com"
# Additional config from file
EnvironmentFile=-/etc/n8n-mcp/config.env
ExecStartPre=/usr/bin/test -f /etc/n8n-mcp/auth_token
ExecStart=/usr/bin/node dist/mcp/index.js --http
# Restart configuration
Restart=always
RestartSec=10
StartLimitBurst=5
StartLimitInterval=60s
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/n8n-mcp/data
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictSUIDSGID=true
LockPersonality=true
# Resource limits
LimitNOFILE=65536
MemoryLimit=512M
CPUQuota=50%
[Install]
WantedBy=multi-user.target
Setup:
# Create user and directories
sudo useradd -r -s /bin/false n8n-mcp
sudo mkdir -p /opt/n8n-mcp /etc/n8n-mcp
sudo chown n8n-mcp:n8n-mcp /opt/n8n-mcp
# Create secure token
sudo sh -c 'openssl rand -base64 32 > /etc/n8n-mcp/auth_token'
sudo chmod 600 /etc/n8n-mcp/auth_token
sudo chown n8n-mcp:n8n-mcp /etc/n8n-mcp/auth_token
# Deploy application
sudo -u n8n-mcp git clone https://github.com/czlonkowski/n8n-mcp.git /opt/n8n-mcp
cd /opt/n8n-mcp
sudo -u n8n-mcp npm install --production
sudo -u n8n-mcp npm run build
sudo -u n8n-mcp npm run rebuild
# Start service
sudo systemctl daemon-reload
sudo systemctl enable n8n-mcp
sudo systemctl start n8n-mcp
Enable:
sudo systemctl enable n8n-mcp
sudo systemctl start n8n-mcp
# Basic health check
curl -H "Authorization: Bearer $AUTH_TOKEN" \
https://your-server.com/health
# Response:
{
"status": "ok",
"mode": "http-fixed",
"version": "2.7.17",
"uptime": 3600,
"memory": {
"used": 95,
"total": 512,
"percentage": 18.5
},
"node": {
"version": "v20.11.0",
"platform": "linux"
},
"features": {
"n8nApi": true, // If N8N_API_URL configured
"authFile": true // If using AUTH_TOKEN_FILE
}
}
Built-in rate limiting protects authentication endpoints from brute force attacks:
Configuration:
# Defaults (15 minutes window, 20 attempts per IP)
AUTH_RATE_LIMIT_WINDOW=900000 # milliseconds
AUTH_RATE_LIMIT_MAX=20
Features:
Behavior:
Prevents Server-Side Request Forgery attacks. The same gate applies to webhook trigger URLs (chat, form, generic webhook), the n8n API client base URL (N8N_API_URL), and per-request URLs supplied via the x-n8n-url header in multi-tenant mode.
Three Security Modes:
Strict Mode (default) - Production deployments
WEBHOOK_SECURITY_MODE=strict
Moderate Mode - Local development with local n8n
WEBHOOK_SECURITY_MODE=moderate
Permissive Mode - Internal networks only
WEBHOOK_SECURITY_MODE=permissive
Important: Cloud metadata endpoints are ALWAYS blocked in all modes for security.
DO:
DON'T:
# Generate strong token
openssl rand -base64 32
# Secure storage options:
# 1. Docker secrets (recommended)
echo $(openssl rand -base64 32) | docker secret create auth_token -
# 2. Kubernetes secrets
kubectl create secret generic n8n-mcp-auth \
--from-literal=token=$(openssl rand -base64 32)
# 3. HashiCorp Vault
vault kv put secret/n8n-mcp token=$(openssl rand -base64 32)
# Run as non-root user (already configured)
# Read-only filesystem
docker run --read-only \
--tmpfs /tmp \
-v n8n-mcp-data:/app/data \
n8n-mcp
# Security scanning
docker scan ghcr.io/czlonkowski/n8n-mcp:latest
"Unauthorized" error:
# Check token is set correctly
docker exec n8n-mcp env | grep AUTH
# Test with curl
curl -v -H "Authorization: Bearer YOUR_TOKEN" \
https://your-server.com/health
# Common causes:
# - Extra spaces in token
# - Missing "Bearer " prefix
# - Token file has newline at end
# - Wrong quotes in JSON config
Default token warning:
ā ļø SECURITY WARNING: Using default AUTH_TOKEN
"TransformStream is not defined":
# Check Node.js version on CLIENT machine
node --version # Must be 18+
# Update Node.js
# macOS: brew upgrade node
# Linux: Use NodeSource repository
# Windows: Download from nodejs.org
"Cannot connect to server":
# 1. Check server is running
docker ps | grep n8n-mcp
# 2. Check logs for errors
docker logs n8n-mcp --tail 50
# 3. Test locally first
curl http://localhost:3000/health
# 4. Check firewall
sudo ufw status # Linux
"Stream is not readable":
Bridge script not working:
# Test the bridge manually
export MCP_URL=http://localhost:3000/mcp
export AUTH_TOKEN=your-token
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node /path/to/http-bridge.js
Connection refused:
# Check server is running
curl http://localhost:3000/health
# Check Docker status
docker ps
docker logs n8n-mcp
# Check firewall
sudo ufw status
Authentication failed:
"Why use 'node' instead of 'docker' in Claude config?"
Claude Desktop only supports stdio. The architecture is:
Claude ā stdio ā mcp-remote ā HTTP ā Docker container
The node command runs mcp-remote (the bridge), not the server directly.
"Command not found: npx":
# Install Node.js 18+ which includes npx
# Or use full path:
which npx # Find npx location
# Use that path in Claude config
# 1. Enable debug logging
docker run -e LOG_LEVEL=debug ...
# 2. Test MCP endpoint
curl -X POST https://your-server.com/mcp \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}'
# 3. Test with mcp-remote directly
MCP_URL=https://your-server.com/mcp \
AUTH_TOKEN=your-token \
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | \
npx mcp-remote $MCP_URL --header "Authorization: Bearer $AUTH_TOKEN"
Railway: See our Railway Deployment Guide
When n8n API is configured, Claude can manage workflows directly:
# Test n8n connectivity first
curl -X POST https://your-server.com/mcp \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "n8n_health_check",
"params": {},
"id": 1
}'
# Check current version
docker exec n8n-mcp node -e "console.log(require('./package.json').version)"
# Update to latest
docker pull ghcr.io/czlonkowski/n8n-mcp:latest
docker stop n8n-mcp
docker rm n8n-mcp
# Re-run with same environment
# Update to specific version
docker pull ghcr.io/czlonkowski/n8n-mcp:v2.7.17
# The database is read-only and pre-built
# No backups needed for the node database
# Updates include new database versions
# Check database stats
curl -X POST https://your-server.com/mcp \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "get_database_statistics",
"id": 1
}'