.do

Debugging & Monitoring

Debug, monitor, and troubleshoot MCP.do server

Comprehensive guide to debugging, monitoring, and troubleshooting MCP.do server and integrations.

Debugging Tools

Debugging Workflow

flowchart TD ISSUE[Issue Detected] --> TYPE{Issue Type?} TYPE --> |Connection| CONN[Connection Issues] TYPE --> |Auth| AUTH[Authentication Issues] TYPE --> |Rate Limit| RATE[Rate Limit Issues] TYPE --> |Execution| EXEC[Execution Issues] TYPE --> |Performance| PERF[Performance Issues] CONN --> CONN_CHECK[Check Health Endpoint<br/>Verify DNS<br/>Test Network] AUTH --> AUTH_CHECK[Validate Token<br/>Check Expiry<br/>Test with Curl] RATE --> RATE_CHECK[Check Rate Limit Headers<br/>Wait for Reset<br/>Implement Backoff] EXEC --> EXEC_CHECK[Check Script Syntax<br/>Add Error Handling<br/>Review Logs] PERF --> PERF_CHECK[Profile Response Times<br/>Check Analytics<br/>Optimize Queries] CONN_CHECK --> LOGS[Review Logs<br/>wrangler tail] AUTH_CHECK --> LOGS RATE_CHECK --> LOGS EXEC_CHECK --> LOGS PERF_CHECK --> LOGS LOGS --> FIX{Fixed?} FIX --> |Yes| MONITOR[Monitor & Document] FIX --> |No| ESCALATE[Escalate to Support<br/>Create GitHub Issue] MONITOR --> END([Resolved]) ESCALATE --> END

Health Check

# Check server health
curl https://mcp.do/health

# Response
{
  "status": "ok",
  "timestamp": "2024-01-15T10:30:00Z"
}

Server Info

# Get server information
curl https://mcp.do/ \
  -H "Accept: application/json"

# Response
{
  "name": "mcp.do",
  "version": "1.0.0",
  "description": "Revolutionary MCP server...",
  "authentication": {
    "anonymous": { "rateLimit": "10 req/min", ... },
    "oauth": { "rateLimit": "100 req/min", ... },
    "apikey": { "rateLimit": "100 req/min", ... }
  },
  "endpoints": {
    "health": "/health - Health check",
    "mcp": "/ - MCP endpoint (SSE or HTTP JSON-RPC)"
  }
}

List Tools

# List available tools
curl https://mcp.do \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_live_xxxxx" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
  }'

Client-Side Debugging

Enable Debug Logging

import { MCPClient } from '@modelcontextprotocol/sdk'

const client = new MCPClient({
  serverUrl: 'https://mcp.do',
  apiKey: process.env.DO_API_KEY,
  debug: true, // Enable debug logging
})

// Logs will show:
// - Connection events
// - Request/response details
// - Error details

Log Tool Calls

async function debugExecute(script: string) {
  console.log('Executing script:', script)
  const startTime = Date.now()

  try {
    const result = await client.useTool('do', { script })
    const duration = Date.now() - startTime

    console.log('Success:', {
      duration,
      result: result.content[0].text.substring(0, 100),
    })

    return result
  } catch (error) {
    const duration = Date.now() - startTime

    console.error('Error:', {
      duration,
      message: error.message,
      status: error.status,
      stack: error.stack,
    })

    throw error
  }
}

Inspect Responses

const result = await client.useTool('do', { script })

console.log('Full response:', JSON.stringify(result, null, 2))

// Check for errors
if (result.isError) {
  console.error('Tool returned error:', result.content[0].text)
}

// Inspect content
console.log('Content type:', result.content[0].type)
console.log('Content text:', result.content[0].text)

Server-Side Debugging

Wrangler Tail

View real-time logs:

# View all logs
wrangler tail --format=pretty

# Filter by status
wrangler tail --format=pretty --status=error

# Search for specific text
wrangler tail --format=pretty --search="tools/call"

# Filter by method
wrangler tail --format=pretty --search="do"

Enable Debug Mode

Add debug logging in worker:

// In wrangler.jsonc
{
  "vars": {
    "DEBUG": "true"
  }
}

// In code
if (env.DEBUG === 'true') {
  console.log('Debug: Tool call', { tool, args })
}

Trace Requests

// Add request tracing
const requestId = crypto.randomUUID()
console.log(`[${requestId}] Starting request`, { method, tool })

try {
  const result = await executeTypeScript(script, options, env)
  console.log(`[${requestId}] Success`, { duration })
  return result
} catch (error) {
  console.error(`[${requestId}] Error`, { error: error.message })
  throw error
}

Common Issues

1. Rate Limit Exceeded

Symptom:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 10
X-RateLimit-Window: 60

Debug:

# Check rate limiter state
wrangler tail --format=pretty --search="rate limit"

# View user's request history
curl https://mcp.do/admin/rate-limits/usr_123 \
  -H "Authorization: Bearer sk_admin_xxxxx"

Solutions:

  • Wait for rate limit window to reset
  • Use authenticated requests (100 req/min instead of 10)
  • Implement exponential backoff:
async function executeWithRetry(script: string, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await client.useTool('do', { script })
    } catch (error) {
      if (error.status === 429 && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000 // Exponential backoff
        console.log(`Rate limited, retrying in ${delay}ms`)
        await new Promise((resolve) => setTimeout(resolve, delay))
      } else {
        throw error
      }
    }
  }
}

2. Execution Timeout

Symptom:

{
  "content": [
    {
      "type": "text",
      "text": "Error: Execution timeout after 10000ms"
    }
  ],
  "isError": true
}

Debug:

// Add timing logs
console.time('db-query')
const result = await db.list('Business')
console.timeEnd('db-query')

Solutions:

  • Optimize queries: await db.list('Order', { limit: 10 })
  • Use parallel operations: Promise.all([...])
  • Split into multiple tool calls
  • Use authenticated mode (30s timeout instead of 10s)

3. Authentication Failures

Symptom:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="https://mcp.do"

Debug:

# Test token
curl https://ooauth.do/oauth/token/info \
  -H "Authorization: Bearer oauth_xxxxx"

# Check token expiry
jwt decode oauth_xxxxx

Solutions:

  • Verify token is not expired
  • Check token format (oauth_ or sk_ prefix)
  • Ensure correct Authorization header format
  • Implement token refresh:
async function ensureValidToken() {
  if (tokenExpiresAt < Date.now() + 300000) {
    // 5 min buffer
    const newToken = await refreshToken(currentToken)
    client.setToken(newToken)
  }
}

4. Readonly Violations

Symptom:

{
  "content": [
    {
      "type": "text",
      "text": "Error: Write operation not allowed in readonly mode"
    }
  ],
  "isError": true
}

Debug:

// Check auth type
console.log('Auth type:', auth.type) // Should show 'anon'

// Test with authenticated request
const result = await client.useTool('do', {
  script: 'await db.create("Order", {...})',
})

Solutions:

  • Add authentication header
  • Use OAuth or API key
  • Limit anonymous users to read operations

5. TypeScript Errors

Symptom:

{
  "content": [
    {
      "type": "text",
      "text": "Error: Cannot read property 'name' of undefined\n  at line 3:15"
    }
  ],
  "isError": true
}

Debug:

// Add error handling in script
const script = `
  try {
    const order = await db.get('Order', 'ord_123')
    console.log('Order:', order)  // Debug output
    return order.customer.name
  } catch (error) {
    console.error('Error:', error.message)
    throw error
  }
`

Solutions:

  • Add null checks: order?.customer?.name
  • Validate data exists before accessing
  • Use TypeScript optional chaining

Performance Monitoring

Response Time

// Track response times
const times: number[] = []

for (let i = 0; i < 100; i++) {
  const start = Date.now()
  await client.useTool('do', { script: 'await db.list("Business")' })
  times.push(Date.now() - start)
}

// Calculate percentiles
times.sort((a, b) => a - b)
console.log('p50:', times[Math.floor(times.length * 0.5)])
console.log('p95:', times[Math.floor(times.length * 0.95)])
console.log('p99:', times[Math.floor(times.length * 0.99)])

Request Tracing

import { trace } from '@opentelemetry/api'

const tracer = trace.getTracer('mcp-client')

async function tracedExecute(script: string) {
  return tracer.startActiveSpan('mcp.execute', async (span) => {
    span.setAttribute('script.length', script.length)

    try {
      const result = await client.useTool('do', { script })
      span.setAttribute('result.success', true)
      return result
    } catch (error) {
      span.setAttribute('result.success', false)
      span.recordException(error)
      throw error
    } finally {
      span.end()
    }
  })
}

Cloudflare Analytics

View Dashboard

  1. Go to Cloudflare dashboard
  2. Select Workers & Pages
  3. Select mcp worker
  4. View analytics:
    • Requests per second
    • Error rate
    • CPU time
    • Duration (p50, p95, p99)

Custom Metrics

// In worker
const startTime = Date.now()

try {
  const result = await executeTypeScript(script, options, env)

  // Log success metric
  console.log(
    JSON.stringify({
      metric: 'tool.execution',
      tool: 'do',
      success: true,
      duration: Date.now() - startTime,
      scriptLength: script.length,
    })
  )

  return result
} catch (error) {
  // Log error metric
  console.log(
    JSON.stringify({
      metric: 'tool.execution',
      tool: 'do',
      success: false,
      duration: Date.now() - startTime,
      error: error.message,
    })
  )

  throw error
}

Query Logs

# Using GraphQL API
curl https://api.cloudflare.com/client/v4/graphql \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -d '{
    "query": "{ viewer { zones(filter: { zoneTag: \"...\" }) { httpRequestsAdaptiveGroups { ... } } } }"
  }'

Testing & Validation

Integration Tests

import { describe, it, expect } from 'vitest'
import { MCPClient } from '@modelcontextprotocol/sdk'

describe('MCP Integration', () => {
  const client = new MCPClient({
    serverUrl: 'https://mcp.do',
    apiKey: process.env.DO_API_KEY,
  })

  beforeAll(async () => {
    await client.connect()
  })

  afterAll(async () => {
    await client.close()
  })

  it('should list businesses', async () => {
    const result = await client.useTool('do', {
      script: 'await db.list("Business")',
    })

    expect(result.isError).toBe(false)
    const businesses = JSON.parse(result.content[0].text)
    expect(Array.isArray(businesses)).toBe(true)
  })

  it('should handle errors', async () => {
    const result = await client.useTool('do', {
      script: 'await db.get("Order", "invalid")',
    })

    expect(result.isError).toBe(true)
  })
})

Load Testing

import { check } from 'k6'
import http from 'k6/http'

export const options = {
  stages: [
    { duration: '1m', target: 10 }, // Ramp up
    { duration: '3m', target: 10 }, // Stay at 10
    { duration: '1m', target: 0 }, // Ramp down
  ],
}

export default function () {
  const payload = JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'tools/call',
    params: {
      name: 'do',
      arguments: {
        script: 'await db.list("Business")',
      },
    },
  })

  const res = http.post('https://mcp.do', payload, {
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${__ENV.DO_API_KEY}`,
    },
  })

  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  })
}

Troubleshooting Checklist

Connection Issues

  • Check server health: curl https://mcp.do/health
  • Verify DNS resolution: nslookup mcp.do
  • Test network connectivity: ping mcp.do
  • Check firewall rules
  • Verify SSL certificate

Authentication Issues

  • Check token format (oauth_ or sk_ prefix)
  • Verify token is not expired
  • Test with curl to isolate client issues
  • Check OAuth server health
  • Review environment variables

Execution Issues

  • Check script syntax
  • Verify timeout settings
  • Review rate limit status
  • Test with simpler script
  • Check Durable Object health

Performance Issues

  • Profile response times
  • Check Cloudflare analytics
  • Review execution logs
  • Optimize queries (add limits)
  • Consider caching

Getting Help

Support Channels

Bug Reports

Include:

  • MCP client version
  • Server URL and environment
  • Full error message
  • Minimal reproduction script
  • Expected vs actual behavior

Feature Requests

Use GitHub Discussions:

  • Describe the use case
  • Explain why existing tools don't work
  • Provide example usage
  • Consider implementation complexity

Next Steps