Table of contents

How do I deploy an MCP server in production?

apiVersion: v1 kind: Service metadata: name: mcp-server-service spec: selector: app: mcp-server ports: - protocol: TCP port: 80 targetPort: 3000 type: LoadBalancer ```

Deploying to Kubernetes

# Apply the deployment
kubectl apply -f mcp-deployment.yaml

# Check deployment status
kubectl get deployments

# View pods
kubectl get pods

# Scale deployment
kubectl scale deployment mcp-server --replicas=5

# View logs
kubectl logs -f deployment/mcp-server

# Update deployment
kubectl set image deployment/mcp-server mcp-server=your-registry/mcp-server:v2

Python-Based MCP Server Deployment

For Python MCP servers using FastAPI or similar frameworks:

Gunicorn with Nginx

# gunicorn.conf.py
import multiprocessing

bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "uvicorn.workers.UvicornWorker"
keepalive = 5
timeout = 30
accesslog = "/var/log/mcp/access.log"
errorlog = "/var/log/mcp/error.log"
loglevel = "info"
# Install Gunicorn
pip install gunicorn uvicorn

# Run with Gunicorn
gunicorn main:app -c gunicorn.conf.py

Nginx Configuration

Create /etc/nginx/sites-available/mcp-server:

upstream mcp_server {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name mcp.yourdomain.com;

    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name mcp.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/mcp.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mcp.yourdomain.com/privkey.pem;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    location / {
        proxy_pass http://mcp_server;
        proxy_set_header Host $host;
        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;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Security Best Practices

1. Environment Variables and Secrets Management

Never hardcode sensitive data. Use environment variables or secret management tools:

# Using environment files
echo "MCP_API_KEY=your_secret_key" > .env
echo "DATABASE_URL=postgresql://user:pass@localhost/db" >> .env

# Or use cloud secret managers
# AWS Secrets Manager
aws secretsmanager get-secret-value --secret-id mcp-server-credentials

# Google Cloud Secret Manager
gcloud secrets versions access latest --secret="mcp-api-key"

2. Rate Limiting

Implement rate limiting to prevent abuse:

// Using express-rate-limit
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP'
});

app.use('/api/', limiter);

3. Authentication and Authorization

// API key authentication middleware
const authenticateApiKey = (req, res, next) => {
  const apiKey = req.headers['x-api-key'];

  if (!apiKey || !isValidApiKey(apiKey)) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  next();
};

app.use(authenticateApiKey);

4. CORS Configuration

const cors = require('cors');

app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || [],
  methods: ['GET', 'POST'],
  credentials: true
}));

Monitoring and Logging

Application Monitoring

// Using Prometheus metrics
const promClient = require('prom-client');

const register = new promClient.Registry();
promClient.collectDefaultMetrics({ register });

const httpRequestDuration = new promClient.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  registers: [register]
});

app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});

Structured Logging

const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.json(),
  defaultMeta: { service: 'mcp-server' },
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console({
      format: winston.format.simple()
    })
  ]
});

Cloud Platform Deployments

AWS Elastic Container Service (ECS)

# Create ECR repository
aws ecr create-repository --repository-name mcp-server

# Build and push Docker image
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin your-account.dkr.ecr.us-east-1.amazonaws.com
docker build -t mcp-server .
docker tag mcp-server:latest your-account.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest
docker push your-account.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest

# Deploy to ECS (using AWS CLI or Terraform)

Google Cloud Run

# Build and deploy
gcloud builds submit --tag gcr.io/PROJECT_ID/mcp-server
gcloud run deploy mcp-server \
  --image gcr.io/PROJECT_ID/mcp-server \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --max-instances 10

Azure Container Instances

# Create container registry
az acr create --resource-group myResourceGroup --name mcpRegistry --sku Basic

# Build and push
az acr build --registry mcpRegistry --image mcp-server:latest .

# Deploy
az container create \
  --resource-group myResourceGroup \
  --name mcp-server \
  --image mcpRegistry.azurecr.io/mcp-server:latest \
  --cpu 2 --memory 4 \
  --registry-login-server mcpRegistry.azurecr.io \
  --ports 3000

Performance Optimization

Caching Strategies

Implement caching to reduce load on your MCP server:

const Redis = require('redis');
const client = Redis.createClient({
  url: process.env.REDIS_URL
});

async function getCachedResponse(key, fetchFunction, ttl = 3600) {
  const cached = await client.get(key);
  if (cached) {
    return JSON.parse(cached);
  }

  const result = await fetchFunction();
  await client.setEx(key, ttl, JSON.stringify(result));
  return result;
}

Load Balancing

Use load balancers to distribute traffic across multiple MCP server instances, similar to how you might handle browser sessions in Puppeteer across multiple browser instances.

Backup and Disaster Recovery

#!/bin/bash
# Backup script

BACKUP_DIR="/backup/mcp-server"
DATE=$(date +%Y%m%d_%H%M%S)

# Database backup
pg_dump $DATABASE_URL > "$BACKUP_DIR/db_$DATE.sql"

# Configuration backup
tar -czf "$BACKUP_DIR/config_$DATE.tar.gz" /opt/mcp-server/config

# Upload to S3
aws s3 cp "$BACKUP_DIR/db_$DATE.sql" s3://your-backup-bucket/
aws s3 cp "$BACKUP_DIR/config_$DATE.tar.gz" s3://your-backup-bucket/

# Cleanup old backups (keep last 30 days)
find $BACKUP_DIR -name "*.sql" -mtime +30 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete

Health Checks and Readiness Probes

Implement robust health check endpoints:

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});

app.get('/ready', async (req, res) => {
  try {
    // Check database connection
    await db.ping();

    // Check Redis connection
    await redis.ping();

    res.status(200).json({
      status: 'ready',
      checks: {
        database: 'connected',
        redis: 'connected'
      }
    });
  } catch (error) {
    res.status(503).json({
      status: 'not ready',
      error: error.message
    });
  }
});

Continuous Deployment

GitHub Actions Workflow

name: Deploy MCP Server

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Build Docker image
      run: docker build -t mcp-server:${{ github.sha }} .

    - name: Push to registry
      run: |
        echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
        docker tag mcp-server:${{ github.sha }} your-registry/mcp-server:latest
        docker push your-registry/mcp-server:latest

    - name: Deploy to production
      run: |
        kubectl set image deployment/mcp-server mcp-server=your-registry/mcp-server:${{ github.sha }}
        kubectl rollout status deployment/mcp-server

Troubleshooting Common Issues

Memory Leaks

Monitor memory usage and implement automatic restarts:

// Memory monitoring
setInterval(() => {
  const usage = process.memoryUsage();
  logger.info('Memory usage', {
    rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
    heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`
  });

  // Restart if memory exceeds threshold
  if (usage.heapUsed > 900 * 1024 * 1024) { // 900MB
    logger.error('Memory threshold exceeded, graceful shutdown');
    gracefulShutdown();
  }
}, 30000);

Connection Timeouts

Configure appropriate timeout settings for different scenarios, much like handling timeouts in Puppeteer when scraping dynamic content:

// Server timeouts
server.keepAliveTimeout = 65000; // slightly higher than ALB timeout
server.headersTimeout = 66000;

// Request timeouts
const axios = require('axios');
const client = axios.create({
  timeout: 30000,
  maxRedirects: 5
});

Conclusion

Deploying an MCP server in production requires attention to security, scalability, and reliability. Whether you choose Docker, PM2, systemd, or Kubernetes, ensure you implement proper monitoring, logging, and backup procedures. Start with a simple deployment method and scale up as your requirements grow. Regular testing, monitoring, and maintenance are key to keeping your MCP server running smoothly in production.

For web scraping workloads that need to handle browser automation in Docker, consider the additional resource requirements and isolation benefits that containerization provides for your MCP server deployment.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon