This guide covers deploying the BibTeX MCP server as a remote service using Docker and Caddy.
The deployment uses:
- Docker: Containerizes the FastMCP Python server
- Caddy: Reverse proxy for HTTPS termination and SSE support
- Server-Sent Events (SSE): Transport protocol for remote MCP communication
Client (Claude Desktop, etc.)
↓ HTTPS/SSE
Caddy Reverse Proxy
↓ HTTP (internal)
FastMCP Server Container
↓ HTTP APIs
Academic Providers (DBLP, arXiv, etc.)
- Docker and Docker Compose
- Domain name (for production) or localhost for development
- Optional: Semantic Scholar API key for increased rate limits
Create a .env file:
# Optional API keys
SEMANTIC_SCHOLAR_API_KEY=your_api_key_here
# Optional proxy settings
HTTP_PROXY=
PROVIDER_TIMEOUT=4For local testing:
# Start services
docker-compose up -d
# Test the server (check if port is listening)
curl http://localhost:8080/sse
# Test MCP endpoint (requires MCP client)
# Server URL: http://localhost:8080- Update Caddyfile: Replace
mcp.example.comwith your domain - DNS Setup: Point your domain to your server
- Deploy:
docker-compose up -dCaddy will automatically obtain Let's Encrypt certificates.
The Dockerfile uses:
- Multi-stage build for optimized image size
- Python 3.11 slim base image
- UV package manager for fast dependency installation
- Non-root user for security
- Health checks for monitoring
Key settings for MCP/SSE support:
reverse_proxy localhost:8000 {
# CRITICAL: Disable buffering for Server-Sent Events
flush_interval -1
}The server runs with SSE transport:
fastmcp run reference_mcp.server:mcp --transport sse --host 0.0.0.0 --port 8000Add to your Claude Desktop config file:
{
"mcpServers": {
"bibtex-search": {
"command": "node",
"args": [
"/path/to/mcp-client.js",
"https://your-domain.com"
]
}
}
}For SSE-based remote servers:
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
const transport = new SSEClientTransport(
new URL("https://your-domain.com/sse")
);For production deployments, consider adding authentication:
mcp.example.com {
basicauth /sse {
username $2a$14$hashed_password
}
reverse_proxy localhost:8000 {
flush_interval -1
}
}Modify the MCP server to require API keys:
@mcp.middleware
async def auth_middleware(request, call_next):
api_key = request.headers.get("Authorization")
if not api_key or not validate_api_key(api_key):
raise HTTPException(401, "Unauthorized")
return await call_next(request)- Docker health check: Socket connection test to port 8000
- Container status:
docker-compose ps - External monitoring: Test SSE endpoint connection
Logs are available:
- Docker logs:
docker-compose logs mcp-server - Caddy logs:
./logs/mcp-server.log - Application logs: Structured JSON output
Consider adding metrics collection:
from prometheus_client import Counter, Histogram
search_requests = Counter('mcp_search_requests_total', 'Total search requests')
search_duration = Histogram('mcp_search_duration_seconds', 'Search duration')For multiple instances:
services:
mcp-server:
deploy:
replicas: 3Update Caddy for load balancing:
reverse_proxy localhost:8000 localhost:8001 localhost:8002 {
flush_interval -1
lb_policy round_robin
}Adjust based on usage:
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
memory: 512M- Use HTTPS: Automatic with Caddy in production
- Network isolation: Use Docker networks
- Resource limits: Prevent resource exhaustion
- Regular updates: Keep base images updated
- Secrets management: Use Docker secrets or external secret stores
# Test SSE endpoint directly
curl -H "Accept: text/event-stream" https://your-domain.com/sse
# Check if buffering is disabled
curl -H "Accept: text/event-stream" -v https://your-domain.com/sse# Check container logs
docker-compose logs mcp-server
# Check container health
docker-compose ps
# Restart services
docker-compose restart# Check Caddy logs
docker-compose logs caddy
# Test certificate
curl -I https://your-domain.comAdjust timeouts in .env:
PROVIDER_TIMEOUT=2 # Reduce for faster responsesConsider adding Redis for response caching:
services:
redis:
image: redis:alpine
mcp-server:
environment:
- REDIS_URL=redis://redis:6379- Use Alpine images
- Implement connection pooling
- Add response caching
- Set appropriate resource limits
- Use API keys for higher rate limits
- Implement intelligent provider selection
- Add retry logic with exponential backoff
To migrate existing local MCP configurations:
- Update client configuration: Change from stdio to SSE transport
- Update URLs: Point to remote server
- Add authentication: If required
- Test connectivity: Verify SSE connection works
- Monitor performance: Check latency vs local setup
- Set up monitoring and alerting
- Implement authentication if needed
- Configure backup and disaster recovery
- Set up CI/CD for automated deployments
- Consider adding rate limiting for public deployments