Tunnel HTTP requests from MCP servers through Ably Pub/Sub to access services behind firewalls
MCP Tunnel enables Model Context Protocol (MCP) servers to securely access internal APIs and services behind firewalls without exposing them to the public internet. It uses Ably Pub/Sub for real-time bidirectional communication between a wrapper (npm package) and a worker (Docker container).
Core Value Proposition: Enable AI assistants to interact with internal APIs and services without exposing them to the public internet or requiring complex VPN setups.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AI Assistant / Client β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β MCP Server (stdio, JSON-RPC 2.0) β β
β β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Wrapper (@mcp-tunnel/wrapper) β β β
β β β - Wraps MCP server execution β β β
β β β - Intercepts HTTP/HTTPS requests β β β
β β β - Publishes requests to Ably β β β
β β β - Receives responses from Ably β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β Ably Pub/Sub
β (Internet)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Private Network / Behind Firewall β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Worker (Docker Container) β β
β β - Subscribes to Ably request channel β β
β β - Executes HTTP requests to internal services β β
β β - Publishes responses to Ably β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Internal APIs / Services β β
β β (not publicly accessible) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- β Universal HTTP interception - Works with fetch, axios, http/https, and all Node.js HTTP clients
- β Transparent tunneling - MCP servers work without code changes
- β Firewall-friendly - No inbound ports required
- β Secure by design - Multi-tenant isolation via Ably capabilities
- β Host allow-lists - Worker enforces allowed target hosts
- β Low latency - Real-time pub/sub communication
- β TypeScript - Full type safety with strict mode
- β Production-ready - Rate limiting, request size limits, health checks
- β Debug logging - Optional file-based logging for troubleshooting
- β Smart bypassing - Automatically bypasses Ably domains to prevent loops
- Sign up for a free Ably account
- Create a new app in the Ably dashboard
- Create an API key with scoped capabilities:
{ "mcp-tunnel:your-tenant-id:*": ["publish", "subscribe"] }
npm install -g @mcp-tunnel/wrapper# Pull the Docker image
docker pull ghcr.io/mslavov/mcp-tunnel-worker:latest
# Create .env file
cat > .env <<EOF
ABLY_API_KEY=your-ably-api-key
TENANT_ID=your-tenant-id
ALLOWED_HOSTS=api.internal.company.com,*.internal.company.com
EOF
# Run the worker
docker run -d \
--name mcp-tunnel-worker \
--env-file .env \
ghcr.io/mslavov/mcp-tunnel-worker:latest# Set environment variables
export ABLY_API_KEY=your-ably-api-key
export TENANT_ID=your-tenant-id
# Test the tunnel
mcp-tunnel --test https://api.internal.company.com/health
# Or run with an MCP server
mcp-tunnel --server ./my-mcp-server.jsClaude Desktop Example:
See examples/slack-mcp-tunneled.json for a real-world configuration example using the Slack MCP server.
The tunnel uses @mswjs/interceptors to intercept all HTTP requests, regardless of which library your MCP server uses:
β Supported HTTP Clients:
- Native
fetch(Node.js 18+) axios(used by official MCP weather server example)got,node-fetch,request- Native
http/httpsmodules - Any library built on top of Node.js http/https
How it works:
// Your MCP server code - NO CHANGES NEEDED!
import axios from 'axios';
// This request is automatically intercepted and tunneled
const response = await axios.get('https://api.internal.company.com/data');The wrapper intercepts requests at the http/https module level, so it works transparently with any HTTP client library.
Environment variables:
ABLY_API_KEY(required) - Ably API key with publish/subscribe capabilitiesTENANT_ID(required) - Unique tenant identifierTUNNEL_TIMEOUT(optional) - Request timeout in milliseconds (default: 30000)MCP_TUNNEL_DEBUG(optional) - Enable debug logging to.mcp-tunnel/wrapper.log
Environment variables:
ABLY_API_KEY(required) - Ably API key with publish/subscribe capabilitiesTENANT_ID(required) - Unique tenant identifierALLOWED_HOSTS(optional) - Comma-separated list of allowed hosts (supports wildcards like*.example.com)MAX_REQUEST_SIZE(optional) - Maximum request size in bytes (default: 10485760 = 10MB)RATE_LIMIT_PER_TENANT(optional) - Requests per minute per tenant (default: 100)
Each tenant is isolated using Ably's capability system:
{
"mcp-tunnel:tenant-123:*": ["publish", "subscribe"]
}This ensures:
- β Tenants can only access their own channels
- β No cross-tenant data leakage
- β API keys are scoped to specific tenants
The worker enforces:
- Host allow-lists - Only allowed hosts can be accessed
- Request size limits - Prevents abuse
- Rate limiting - Per-tenant request limits
Enable detailed logging for troubleshooting:
# Enable debug logging
export MCP_TUNNEL_DEBUG=1
export ABLY_API_KEY=your-key
export TENANT_ID=your-tenant-id
# Run your MCP server with tunnel
mcp-tunnel --server ./my-mcp-server.js
# View logs in real-time
tail -f .mcp-tunnel/wrapper.logDebug logs include:
- Preload script initialization
- HTTP request/response interception
- Ably connection status
- Request/response details
- Error stack traces
Note: Debug logging writes to a file to avoid polluting stdio (which would break MCP protocol communication).
- Node.js 18+
- npm 8+
- Docker 20+ (for worker)
# Clone the repository
git clone https://github.com/mslavov/mcp-tunnel.git
cd mcp-tunnel
# Install dependencies
npm install
# Build packages
npm run build
# Run linter
npm run lintmcp-tunnel/
βββ packages/
β βββ wrapper/ # npm package for wrapping MCP servers
β β βββ src/
β β β βββ ably-client.ts
β β β βββ tunnel-fetch.ts
β β β βββ cli.ts
β β β βββ types.ts
β β β βββ index.ts
β β βββ package.json
β β βββ tsconfig.json
β βββ worker/ # Docker container for executing requests
β βββ src/
β β βββ ably-client.ts
β β βββ request-handler.ts
β β βββ types.ts
β β βββ index.ts
β βββ Dockerfile
β βββ package.json
β βββ tsconfig.json
βββ .github/
β βββ workflows/
β βββ test.yml
β βββ publish.yml
βββ package.json
βββ tsconfig.base.json
βββ README.md
Contributions are welcome!
MIT License - see LICENSE for details.
- Model Context Protocol by Anthropic
- Ably Realtime for pub/sub infrastructure
- Inspired by the Language Server Protocol
Built with β€οΈ for the MCP community