asannikov/claude-mcp-server-template
If you are the rightful owner of claude-mcp-server-template and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.
This document provides a comprehensive guide to setting up and running a Model Context Protocol (MCP) server with HTTP/SSE transport support.
MCP Server Template
A minimal working template for an MCP (Model Context Protocol) server with HTTP/SSE transport support.
Quick Start
1. Installation
npm install
cp .env.example .env
# Edit .env and set your AUTH_TOKEN
2. Running
Local stdio (for development):
npm start
HTTP server (for remote access):
npm run http
With PM2 (for production):
pm2 start ecosystem.config.cjs
pm2 save
3. Connecting with Claude CLI
Local stdio:
claude mcp add my-server \
--transport stdio \
node /path/to/src/index.js
Local HTTP:
claude mcp add my-server-http \
http://localhost:3020/sse \
--transport sse \
--header "Authorization: Bearer your-token"
Remote HTTPS (via nginx):
claude mcp add my-server-remote \
https://your-domain.com:8443/sse \
--transport sse \
--header "Authorization: Bearer your-token"
Ports
The default port is 3020 (configurable via PORT
in .env
).
Why 3020? Port 3010 is used by the main project, so 3020 was chosen for the template.
Critical Pitfalls
1. HTTP/2 Is Incompatible with SSE
Problem: HTTP/2 multiplexing breaks SSE long-lived connections.
Solution: Use a separate HTTPS endpoint with HTTP/1.1:
server {
listen 8443 ssl; # WITHOUT http2!
listen [::]:8443 ssl;
location / {
proxy_pass http://localhost:3020/;
proxy_http_version 1.1; # REQUIRED
proxy_set_header Connection ""; # REQUIRED
proxy_buffering off; # REQUIRED
proxy_read_timeout 86400; # 24 hours for long-lived connections
}
}
ā DON'T DO THIS:
listen 443 ssl http2; # HTTP/2 will break SSE!
2. Nginx Proxy Settings
For SSE to work through nginx, special settings are required:
# HTTP/1.1 and remove Connection: close
proxy_http_version 1.1;
proxy_set_header Connection "";
# Disable buffering for streaming
proxy_buffering off;
proxy_cache off;
proxy_request_buffering off;
# Long timeouts for SSE (24 hours)
proxy_connect_timeout 30;
proxy_send_timeout 86400;
proxy_read_timeout 86400;
3. Docker Port Mapping
When adding a new port in docker-compose, --force-recreate
is required:
# ā Insufficient
docker-compose restart nginx
# ā
Correct
docker-compose up -d --force-recreate nginx
4. Claude CLI Health Check
Important: Claude CLI may show "Failed to connect" for public endpoints, even when they work. This is a cosmetic issue.
Functionality check:
curl -N -H "Accept: text/event-stream" \
-H "Authorization: Bearer your-token" \
http://localhost:3020/sse
If you see event: endpoint
, it's working!
5. IPv4 vs IPv6
CRITICAL: Claude Code requires IPv6 support. You MUST listen on both IPv4 and IPv6!
In your Node.js server:
httpServer.listen(PORT, '0.0.0.0', () => { ... }); // IPv4
In nginx (BOTH lines are REQUIRED):
listen 8443 ssl; # IPv4
listen [::]:8443 ssl; # IPv6 - REQUIRED for Claude Code!
ā DON'T DO THIS:
listen 8443 ssl; # Missing IPv6 - Claude Code won't work!
Nginx Configuration for Production
Option 1: Separate HTTPS Port (Recommended)
Create a new server block on port 8443:
# /etc/nginx/sites-available/mcp-server
server {
listen 8443 ssl;
listen [::]:8443 ssl;
server_name your-domain.com;
# SSL certificates
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
# Modern SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
location / {
proxy_pass http://localhost:3020/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
# CRITICAL for SSE
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 86400;
}
}
Add the port to docker-compose.yml (if using Docker):
nginx:
ports:
- "443:443"
- "8443:8443" # MCP server
Option 2: SSH Tunnel
For maximum security:
# On the client
ssh -L 3020:localhost:3020 user@your-server.com -N &
# Then connect locally
claude mcp add my-server \
http://localhost:3020/sse \
--transport sse \
--header "Authorization: Bearer your-token"
Health Checks
Health Check
curl http://localhost:3020/health
SSE Connection Test
curl -N -H "Accept: text/event-stream" \
-H "Authorization: Bearer your-token" \
http://localhost:3020/sse
Expected result:
event: endpoint
data: /messages?sessionId=template-xxx-xxx-xxx
HTTP/1.1 Verification (for HTTPS)
curl -v https://your-domain.com:8443/health 2>&1 | grep ALPN
Should show:
* ALPN, server accepted to use http/1.1
Should NOT show h2
(HTTP/2)!
Security
Token Generation
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Recommendations
- Don't use HTTP without SSL for production
- Rotate tokens regularly
- Use different tokens for different clients
- Monitor logs for suspicious activity
- Use SSH tunnel or VPN for maximum security
Project Structure
mcp-server-template/
āāā src/
ā āāā index.js # Stdio server (local)
ā āāā http-server.js # HTTP server (remote)
āāā package.json
āāā .env.example
āāā .gitignore
āāā ecosystem.config.cjs # PM2 configuration
āāā README.md
Extension
Add your tools in the tools/list
handler:
server.setRequestHandler('tools/list', async () => {
return {
tools: [
{
name: 'your_tool',
description: 'What your tool does',
inputSchema: {
type: 'object',
properties: {
param: { type: 'string', description: 'Parameter description' }
},
required: ['param']
}
}
]
};
});
server.setRequestHandler('tools/call', async (request) => {
if (request.params.name === 'your_tool') {
// Your implementation
return {
content: [{ type: 'text', text: 'Result' }]
};
}
});
Troubleshooting
"Failed to connect" in Claude CLI
-
Check the health endpoint:
curl http://localhost:3020/health
-
Check the SSE endpoint:
curl -N -H "Authorization: Bearer TOKEN" http://localhost:3020/sse
-
For HTTPS, verify HTTP/1.1:
curl -v https://domain:8443/health 2>&1 | grep ALPN
Nginx won't start
nginx -t # Check configuration
tail -f /var/log/nginx/error.log
Server not responding
pm2 status
pm2 logs mcp-template-http --lines 50
netstat -tlnp | grep 3020
Useful Links
License
MIT