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 detailsLog 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: 60Debug:
# 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_xxxxxSolutions:
- Verify token is not expired
- Check token format (
oauth_orsk_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
- Go to Cloudflare dashboard
- Select Workers & Pages
- Select
mcpworker - 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_orsk_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
- Documentation: https://mcp.do/docs
- GitHub Issues: https://github.com/youraccount/platform/issues
- Discord: https://discord.gg/dotdo
- Email: [email protected]
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
- The
doTool - Tool reference - Authentication - Security configuration
- Configuration - Server setup
- Integration Guide - Client integration