Files
gh-jezweb-claude-skills-ski…/references/deployment-guide.md
2025-11-30 08:25:43 +08:00

6.3 KiB

Deployment Guide for TypeScript MCP Servers

Complete guide to deploying MCP servers on Cloudflare Workers.


Quick Deployment

# Build
npm run build

# Deploy
wrangler deploy

Environment Setup

Development (.dev.vars)

Create .dev.vars for local secrets:

WEATHER_API_KEY=abc123
DATABASE_URL=http://localhost:3306

Never commit .dev.vars to git!

Production Secrets

# Set secrets
wrangler secret put WEATHER_API_KEY
wrangler secret put DATABASE_URL

# List secrets
wrangler secret list

# Delete secret
wrangler secret delete OLD_KEY

Multiple Environments

wrangler.jsonc:

{
  "name": "mcp-server",
  "main": "src/index.ts",

  "env": {
    "staging": {
      "name": "mcp-server-staging",
      "vars": {
        "ENVIRONMENT": "staging"
      },
      "d1_databases": [
        { "binding": "DB", "database_id": "staging-db-id" }
      ]
    },
    "production": {
      "name": "mcp-server-production",
      "vars": {
        "ENVIRONMENT": "production"
      },
      "d1_databases": [
        { "binding": "DB", "database_id": "prod-db-id" }
      ]
    }
  }
}

Deploy to specific environment:

wrangler deploy --env staging
wrangler deploy --env production

Custom Domains

Setup

  1. Add domain in Cloudflare dashboard:

    • Workers & Pages → your worker → Settings → Domains & Routes
    • Add custom domain: mcp.example.com
  2. Or via wrangler.jsonc:

{
  "routes": [
    {
      "pattern": "mcp.example.com/*",
      "custom_domain": true
    }
  ]
}

SSL/TLS

Cloudflare provides automatic SSL certificates for custom domains.


CI/CD with GitHub Actions

.github/workflows/deploy.yml:

name: Deploy MCP Server

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build

      - name: Deploy to Cloudflare Workers
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: deploy --env production

Setup Secrets

  1. Get Cloudflare API token:

    • Dashboard → My Profile → API Tokens
    • Create token with "Edit Cloudflare Workers" permissions
  2. Add to GitHub:

    • Repository → Settings → Secrets → Actions
    • Add CLOUDFLARE_API_TOKEN
    • Add CLOUDFLARE_ACCOUNT_ID

Database Migrations

D1 Migrations

Create migration:

wrangler d1 migrations create my-db add-users-table

migrations/0001_add_users_table.sql:

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  email TEXT UNIQUE NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Apply migrations:

# Local
wrangler d1 migrations apply my-db --local

# Production
wrangler d1 migrations apply my-db --remote

In CI/CD:

- name: Run D1 migrations
  run: wrangler d1 migrations apply my-db --remote
  env:
    CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

Monitoring & Logs

Real-time Logs

# Tail logs
wrangler tail

# Filter by status
wrangler tail --status error

# Filter by method
wrangler tail --method POST

Workers Analytics

View in dashboard:

  • Requests per second
  • Error rate
  • CPU time
  • Bandwidth

Custom Logging

app.post('/mcp', async (c) => {
  console.log('MCP request:', {
    method: c.req.method,
    path: c.req.path,
    timestamp: new Date().toISOString()
  });

  // ... handle request

  console.log('MCP response:', { status: 200, duration: '15ms' });
});

Rollback Strategy

Quick Rollback

# List deployments
wrangler deployments list

# Rollback to specific deployment
wrangler rollback --deployment-id abc123

Git-based Rollback

# Revert to previous commit
git revert HEAD
git push

# CI/CD will auto-deploy reverted version

Performance Optimization

1. Enable Compression

Cloudflare automatically compresses responses. No configuration needed.

2. Caching

app.get('/mcp-schema', async (c) => {
  const schema = { ... };

  return c.json(schema, 200, {
    'Cache-Control': 'public, max-age=3600',
    'CDN-Cache-Control': 'max-age=86400'
  });
});

3. Edge Caching with KV

async function getCachedOrFetch(key: string, fetcher: () => Promise<string>, env: Env) {
  const cached = await env.CACHE.get(key);
  if (cached) return cached;

  const fresh = await fetcher();
  await env.CACHE.put(key, fresh, { expirationTtl: 3600 });
  return fresh;
}

Health Checks

app.get('/health', (c) => {
  return c.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    version: '1.0.0'
  });
});

Monitor with UptimeRobot, Pingdom, etc.


Cost Optimization

Workers Pricing

  • Free: 100,000 requests/day
  • Paid: $5/month + $0.50/million requests

Tips

  1. Use KV for caching (reduces computation)
  2. Optimize D1 queries (use indexes)
  3. Batch operations where possible
  4. Set reasonable rate limits
  5. Monitor usage in dashboard

Security Checklist

Before production:

  • Authentication implemented
  • Rate limiting enabled
  • CORS configured correctly
  • Secrets in Wrangler secrets (not code)
  • Error messages don't leak data
  • HTTPS only (enforced by CF)
  • Input validation on all tools
  • SQL injection protection
  • API keys rotated regularly

Troubleshooting Deployments

Deployment Fails

# Check syntax
npm run build

# Validate wrangler.jsonc
wrangler deploy --dry-run

# View detailed logs
wrangler deploy --verbose

Worker Not Responding

# Check logs
wrangler tail

# Test locally first
wrangler dev

# Verify bindings
wrangler d1 list
wrangler kv namespace list

Performance Issues

# Check CPU time
wrangler tail --status ok | grep "CPU time"

# Profile with Analytics
# Dashboard → Workers → Analytics

Last Updated: 2025-10-28