claude-n8n-bridge
This project is an MCP OAuth middleware that securely integrates Claude.ai with n8n workflows. It provides an OAuth 2.0 authentication flow, dynamic client registration for Claude, and supports SSE streaming. There is a current limitation due to a bug in handling OAuth tokens, but it can be circumvented by using an API key.
GitHub Stars
1
User Rating
Not Rated
Favorites
0
Views
18
Forks
2
Issues
0
MCP OAuth Middleware for n8n
A Node.js middleware server that bridges Claude's OAuth requirements with n8n's MCP (Model Context Protocol) server, enabling secure integration between Claude.ai and your n8n workflows.
⚠️ Important OAuth Limitation:
Due to a current bug in Claude's MCP implementation, OAuth tokens are not properly passed to the middleware after authentication. As a workaround, you MUST include your API key in the integration URL as shown below.
Overview
This middleware provides:
- OAuth 2.0 authentication flow with Keycloak
- Dynamic client registration for Claude
- SSE (Server-Sent Events) streaming support
- Secure proxying of MCP requests to n8n
- API key authentication fallback
Prerequisites
- Ubuntu server (20.04 or later)
- Domain name pointing to your server's IP address
- Docker and Docker Compose installed
- Node.js 16+ and npm (for manual installation)
- Apache web server for reverse proxy
- Basic understanding of OAuth 2.0 and MCP
Quick Start
1. Domain Setup
You'll need a domain with SSL certificates. All services will run on the same server with different ports:
mcp.my-domain.com
→ localhost:3000 (MCP middleware)mcp.my-domain.com:8080
→ localhost:8080 (Keycloak)mcp.my-domain.com:5678
→ localhost:5678 (n8n) (optional if you are not self-hosting n8n)
Getting SSL Certificates with Let's Encrypt
Make sure your domain DNS A record points to your server IP.
# Install certbot and Apache
sudo apt update
sudo apt install certbot python3-certbot-apache apache2
# Enable required Apache modules
sudo a2enmod proxy proxy_http proxy_wstunnel ssl headers rewrite
# Get certificate for your domain
sudo certbot certonly --standalone -d mcp.my-domain.com
# Certificates will be in:
# /etc/letsencrypt/live/mcp.my-domain.com/fullchain.pem
# /etc/letsencrypt/live/mcp.my-domain.com/privkey.pem
2. Apache Reverse Proxy Setup
- Create Apache configuration:
sudo nano /etc/apache2/sites-available/mcp-services.conf
- Add the following configuration:
<VirtualHost *:80>
ServerName mcp.my-domain.com
# Redirect HTTP to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>
<VirtualHost *:443>
ServerName mcp.my-domain.com
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mcp.my-domain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mcp.my-domain.com/privkey.pem
# Security Headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set X-XSS-Protection "1; mode=block"
# Enable WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://localhost:3000/$1" [P,L]
# Proxy to MCP Middleware (default)
ProxyPreserveHost On
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
# SSE Support
ProxyPass /mcp http://localhost:3000/mcp
ProxyPassReverse /mcp http://localhost:3000/mcp
ProxyPass /sse http://localhost:3000/sse
ProxyPassReverse /sse http://localhost:3000/sse
# Disable buffering for SSE
ProxyPass /mcp http://localhost:3000/mcp flushpackets=on
ErrorLog ${APACHE_LOG_DIR}/mcp-error.log
CustomLog ${APACHE_LOG_DIR}/mcp-access.log combined
</VirtualHost>
# Keycloak on port 8080
<VirtualHost *:8080>
ServerName mcp.my-domain.com
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mcp.my-domain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mcp.my-domain.com/privkey.pem
# Proxy to Keycloak
ProxyPreserveHost On
ProxyPass / http://localhost:8081/
ProxyPassReverse / http://localhost:8081/
# Keycloak specific headers
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "8080"
ErrorLog ${APACHE_LOG_DIR}/keycloak-error.log
CustomLog ${APACHE_LOG_DIR}/keycloak-access.log combined
</VirtualHost>
# n8n on port 5678
<VirtualHost *:5678>
ServerName mcp.my-domain.com
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mcp.my-domain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mcp.my-domain.com/privkey.pem
# Enable WebSocket support for n8n
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://localhost:5679/$1" [P,L]
# Proxy to n8n
ProxyPreserveHost On
ProxyPass / http://localhost:5679/
ProxyPassReverse / http://localhost:5679/
ErrorLog ${APACHE_LOG_DIR}/n8n-error.log
CustomLog ${APACHE_LOG_DIR}/n8n-access.log combined
</VirtualHost>
- Enable the site and required ports:
# Enable the site
sudo a2ensite mcp-services.conf
# Enable SSL module
sudo a2enmod ssl
# Add listen ports to Apache
sudo bash -c 'echo "Listen 8080" >> /etc/apache2/ports.conf'
sudo bash -c 'echo "Listen 5678" >> /etc/apache2/ports.conf'
# Test configuration
sudo apache2ctl configtest
# Restart Apache
sudo systemctl restart apache2
3. Quick Installation with Docker Compose
The fastest way to get everything running is using Docker Compose, which sets up all services at once:
- Clone the repository:
git clone https://github.com/yourusername/mcp-oauth-middleware.git
cd mcp-oauth-middleware
- Configure environment:
cp .env.example .env
Edit .env
with your values:
Note: Even though you authenticate via OAuth, the API key in the URL is currently required for the integration to work properly.
# Server Configuration
PORT=3000 # Change to 3001 or another port if 3000 is already in use
PUBLIC_URL=https://mcp.my-domain.com
# Keycloak Configuration
KEYCLOAK_REALM=mcp
KEYCLOAK_SERVER_URL=https://mcp.my-domain.com:8080
KEYCLOAK_CLIENT_ID=mcp-middleware
KEYCLOAK_CLIENT_SECRET= # Optional for public clients
# n8n Configuration
# For local n8n access only (recommended for security):
N8N_MCP_URL=http://host.docker.internal:5678/webhook/mcp/YOUR_WEBHOOK_ID
# For public n8n access:
# N8N_MCP_URL=https://mcp.my-domain.com:5678/webhook/mcp/YOUR_WEBHOOK_ID
N8N_BEARER_TOKEN=your-n8n-bearer-token # Optional
# Security
API_KEY=your-secret-api-key
DISABLE_AUTH=false # Set to true for testing only
- Build and start all services:
# Build the middleware image
docker build -t mcp-oauth-middleware .
# Start all services (Keycloak, n8n, and MCP middleware)
docker-compose up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f
Configure services:
Keycloak: Access at
https://mcp.my-domain.com:8080
- Create a new realm called
mcp
- Create a client with ID
mcp-claude
(public client) - Set Valid Redirect URIs:
https://claude.ai/api/mcp/auth_callback
- Enable "Anonymous access" in Realm Settings → Client Registration
- Create a new realm called
n8n: Access at
https://mcp.my-domain.com:5678
- Create a workflow with "MCP Server Trigger" node
- Configure Bearer token if needed
- Activate the workflow and copy the webhook URL
Update .env if needed and restart services:
docker-compose restart mcp-middleware # Or restart all services docker-compose down && docker-compose up -d
Skip to section 6 to configure Claude integration.
Alternative: Manual Installation of Individual Services
If you prefer to install services individually instead of using Docker Compose:
Keycloak Setup
- Install Keycloak (using Docker with different internal port):
docker run -d \
--name keycloak \
-p 8081:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
-e KC_PROXY=edge \
-e KC_HOSTNAME_STRICT=false \
-e KC_HOSTNAME_URL=https://mcp.my-domain.com:8080 \
quay.io/keycloak/keycloak:latest \
start-dev
- Configure Keycloak (same as step 4 above)
n8n Setup
- Install n8n (using Docker with different internal port):
docker run -d \
--name n8n \
-p 5679:5678 \
-v ~/.n8n:/home/node/.n8n \
-e N8N_PROTOCOL=https \
-e N8N_HOST=mcp.my-domain.com \
-e N8N_PORT=5678 \
-e WEBHOOK_URL=https://mcp.my-domain.com:5678 \
n8nio/n8n
- Create MCP Workflow (same as step 4 above)
Middleware Installation
Option A: Docker
# Build the Docker image first
docker build -t mcp-oauth-middleware .
# Run the container
# Note: If port 3000 is already in use, change both PORT in .env and the port mapping
# Example: PORT=3001 in .env, then use -p 3001:3001
docker run -d \
--name mcp-middleware \
-p 3000:3000 \
--add-host host.docker.internal:host-gateway \
--env-file .env \
--restart unless-stopped \
mcp-oauth-middleware
# Check logs
docker logs -f mcp-middleware
Important Notes:
- The
--add-host host.docker.internal:host-gateway
flag is required for the container to access local services like n8n - If port 3000 is taken, update PORT in
.env
(e.g.,PORT=3001
) and use matching port mapping (e.g.,-p 3001:3001
) - For debugging, remove
-d
to run in foreground and see logs immediately
Option B: Manual with PM2
# Install dependencies
npm install
# Production with PM2
npm install -g pm2
pm2 start mcp-oauth-middleware.js --name mcp-middleware
pm2 save
pm2 startup
6. Configure Claude Integration
- Go to Claude.ai
- Navigate to Settings → Integrations
- Add a new MCP integration:
- Name: Your Integration Name
- URL:
https://mcp.my-domain.com?api_key=your-secret-api-key
- Click "Connect"
- You'll be redirected to Keycloak to authenticate
- After login, the integration will be active
Testing
Using MCP Inspector
npx @modelcontextprotocol/inspector https://mcp.my-domain.com?api_key=your-secret-api-key
Testing OAuth Flow
# Check OAuth discovery
curl https://mcp.my-domain.com/.well-known/oauth-authorization-server
# Test with API key
curl -H "X-API-Key: your-secret-api-key" https://mcp.my-domain.com/health
Testing SSE Connection
# With API key
curl -H "X-API-Key: your-secret-api-key" \
-H "Accept: text/event-stream" \
https://mcp.my-domain.com/mcp
Firewall Configuration
If using UFW (Ubuntu Firewall):
# Allow SSH (if not already allowed)
sudo ufw allow 22/tcp
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow Keycloak
sudo ufw allow 8080/tcp
# Allow n8n
sudo ufw allow 5678/tcp
# Enable firewall
sudo ufw enable
Security Considerations
- SSL/TLS: Always use HTTPS in production (handled by Apache)
- Internal Ports: Services run on different internal ports than external
- Token Validation: The middleware validates all Keycloak tokens
- API Key: Use strong API keys as fallback authentication
- CORS: Configure CORS appropriately for your use case
- Firewall: Only expose necessary ports
- Local n8n Access: For better security, configure n8n to only be accessible locally (via
host.docker.internal
) rather than publicly
Troubleshooting
Common Issues
"Failed to connect" in Claude
- Check OAuth discovery endpoint is accessible:
curl https://mcp.my-domain.com/.well-known/oauth-authorization-server
- Verify Keycloak is running and accessible at
https://mcp.my-domain.com:8080
- Check middleware logs:
pm2 logs mcp-middleware
- Check Apache logs:
sudo tail -f /var/log/apache2/mcp-error.log
- Check OAuth discovery endpoint is accessible:
401 Unauthorized
- Verify Bearer token or API key is correct
- Check Keycloak token validation
- Ensure client registration is enabled in Keycloak
SSE Connection Fails
- Check n8n MCP trigger is active
- Verify n8n webhook URL is correct
- Test SSE connection directly with curl
- Check Apache proxy configuration for SSE support
- If using local n8n access, ensure
--add-host host.docker.internal:host-gateway
is included in docker run
Port Conflicts
- If port 3000 is already in use, change PORT in
.env
to an available port (e.g., 3001) - Update the docker run command to match:
-p 3001:3001
- Update Apache ProxyPass directives to use the new port
- If port 3000 is already in use, change PORT in
Apache Configuration Issues
- Test config:
sudo apache2ctl configtest
- Check if all modules are enabled:
sudo a2enmod proxy proxy_http proxy_wstunnel ssl headers rewrite
- Verify SSL certificates are valid:
sudo certbot certificates
- Test config:
Debug Mode
Enable detailed logging:
DEBUG=* node mcp-oauth-middleware.js
Monitor all logs:
# Apache logs
sudo tail -f /var/log/apache2/*.log
# PM2 logs
pm2 logs mcp-middleware
# Docker logs
docker logs -f keycloak
docker logs -f n8n
API Endpoints
GET /.well-known/oauth-authorization-server
- OAuth metadataPOST /oauth/register
- Dynamic client registrationGET /mcp
- SSE connection (with auth)POST /mcp
- JSON-RPC requests (with auth)GET /health
- Health check
Environment Variables
Variable | Description | Required | Default |
---|---|---|---|
PORT | Server port | No | 3000 |
PUBLIC_URL | Public URL of middleware | Yes | - |
KEYCLOAK_REALM | Keycloak realm name | Yes | - |
KEYCLOAK_SERVER_URL | Keycloak server URL | Yes | - |
KEYCLOAK_CLIENT_ID | Keycloak client ID | No | mcp-middleware |
KEYCLOAK_CLIENT_SECRET | Keycloak client secret | No | - |
N8N_MCP_URL | n8n MCP webhook URL | Yes | - |
N8N_BEARER_TOKEN | n8n bearer token | No | - |
API_KEY | API key for fallback auth | No | - |
DISABLE_AUTH | Disable authentication | No | false |
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
License
MIT License - see LICENSE file for details