394 lines
6.8 KiB
Markdown
394 lines
6.8 KiB
Markdown
# Common Issues and Troubleshooting
|
|
|
|
Detailed troubleshooting for the 15 most common Cloudflare MCP server errors.
|
|
|
|
---
|
|
|
|
## 1. McpAgent Class Not Exported
|
|
|
|
**Error**: `TypeError: Cannot read properties of undefined (reading 'serve')`
|
|
|
|
**Diagnosis**: Check if your McpAgent class is exported
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// ✅ CORRECT
|
|
export class MyMCP extends McpAgent { ... }
|
|
|
|
// ❌ WRONG
|
|
class MyMCP extends McpAgent { ... } // Missing export!
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Transport Mismatch
|
|
|
|
**Error**: `Connection failed: Unexpected response format`
|
|
|
|
**Diagnosis**: Client and server transport don't match
|
|
|
|
**Debug**:
|
|
```bash
|
|
# Check what your server supports
|
|
curl https://my-mcp.workers.dev/sse
|
|
curl https://my-mcp.workers.dev/mcp
|
|
```
|
|
|
|
**Solution**: Serve both transports (see SKILL.md Transport section)
|
|
|
|
---
|
|
|
|
## 3. OAuth Redirect URI Mismatch
|
|
|
|
**Error**: `OAuth error: redirect_uri does not match`
|
|
|
|
**Diagnosis**: Check client configuration vs deployed URL
|
|
|
|
**Common causes**:
|
|
- Developed with localhost, deployed to workers.dev
|
|
- HTTP vs HTTPS
|
|
- Missing `/oauth/callback` path
|
|
- Typo in domain
|
|
|
|
**Solution**:
|
|
```json
|
|
// Update after deployment
|
|
{
|
|
"mcpServers": {
|
|
"my-mcp": {
|
|
"url": "https://my-mcp.YOUR_ACCOUNT.workers.dev/sse",
|
|
"auth": {
|
|
"authorizationUrl": "https://my-mcp.YOUR_ACCOUNT.workers.dev/authorize",
|
|
"tokenUrl": "https://my-mcp.YOUR_ACCOUNT.workers.dev/token"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. WebSocket Hibernation State Loss
|
|
|
|
**Error**: State not found after WebSocket reconnect
|
|
|
|
**Diagnosis**: Using in-memory state instead of storage
|
|
|
|
**Wrong**:
|
|
```typescript
|
|
class MyMCP extends McpAgent {
|
|
userId: string; // ❌ Lost on hibernation!
|
|
|
|
async init() {
|
|
this.userId = "123";
|
|
}
|
|
}
|
|
```
|
|
|
|
**Correct**:
|
|
```typescript
|
|
class MyMCP extends McpAgent {
|
|
async init() {
|
|
await this.state.storage.put("userId", "123"); // ✅ Persisted
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Durable Objects Binding Missing
|
|
|
|
**Error**: `Cannot read properties of undefined (reading 'idFromName')`
|
|
|
|
**Diagnosis**: Check wrangler.jsonc
|
|
|
|
**Solution**:
|
|
```jsonc
|
|
{
|
|
"durable_objects": {
|
|
"bindings": [
|
|
{
|
|
"name": "MY_MCP",
|
|
"class_name": "MyMCP",
|
|
"script_name": "my-mcp-server"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Migration Not Defined
|
|
|
|
**Error**: `Durable Object class MyMCP has no migration defined`
|
|
|
|
**Diagnosis**: First DO deployment needs migration
|
|
|
|
**Solution**:
|
|
```jsonc
|
|
{
|
|
"migrations": [
|
|
{
|
|
"tag": "v1",
|
|
"new_classes": ["MyMCP"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**After first deployment**, migrations are locked. Subsequent changes require new migration tags (v2, v3, etc.).
|
|
|
|
---
|
|
|
|
## 7. CORS Errors
|
|
|
|
**Error**: `Access blocked by CORS policy`
|
|
|
|
**Diagnosis**: Remote MCP server needs CORS headers
|
|
|
|
**Solution**: Use OAuthProvider (handles CORS automatically) or add headers:
|
|
```typescript
|
|
return new Response(body, {
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
}
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Client Configuration Format Error
|
|
|
|
**Error**: Claude Desktop doesn't see MCP server
|
|
|
|
**Diagnosis**: Check JSON format
|
|
|
|
**Wrong**:
|
|
```json
|
|
{
|
|
"mcpServers": [ // ❌ Array instead of object!
|
|
{
|
|
"name": "my-mcp",
|
|
"url": "..."
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Correct**:
|
|
```json
|
|
{
|
|
"mcpServers": { // ✅ Object with named servers
|
|
"my-mcp": {
|
|
"url": "..."
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Location**:
|
|
- Mac: `~/.config/claude/claude_desktop_config.json`
|
|
- Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
- Linux: `~/.config/claude/claude_desktop_config.json`
|
|
|
|
---
|
|
|
|
## 9. serializeAttachment() Not Used
|
|
|
|
**Error**: WebSocket metadata lost on hibernation
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// Store metadata on WebSocket
|
|
webSocket.serializeAttachment({
|
|
userId: "123",
|
|
sessionId: "abc",
|
|
connectedAt: Date.now()
|
|
});
|
|
|
|
// Retrieve on wake
|
|
const metadata = webSocket.deserializeAttachment();
|
|
console.log(metadata.userId); // "123"
|
|
```
|
|
|
|
---
|
|
|
|
## 10. OAuth Consent Screen Disabled
|
|
|
|
**Security risk**: Users don't know what they're authorizing
|
|
|
|
**Wrong**:
|
|
```typescript
|
|
allowConsentScreen: false // ❌ Never in production!
|
|
```
|
|
|
|
**Correct**:
|
|
```typescript
|
|
allowConsentScreen: true // ✅ Always in production
|
|
```
|
|
|
|
---
|
|
|
|
## 11. JWT Signing Key Missing
|
|
|
|
**Error**: `JWT_SIGNING_KEY environment variable not set`
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Generate secure key
|
|
openssl rand -base64 32
|
|
|
|
# Add to secrets
|
|
npx wrangler secret put JWT_SIGNING_KEY
|
|
|
|
# Or add to wrangler.jsonc (less secure)
|
|
"vars": {
|
|
"JWT_SIGNING_KEY": "generated-key-here"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Environment Variables Not Configured
|
|
|
|
**Error**: `env.MY_VAR is undefined`
|
|
|
|
**Diagnosis**: Variables only in `.dev.vars`, not in wrangler.jsonc
|
|
|
|
**Wrong**:
|
|
```bash
|
|
# .dev.vars only (works locally, fails in production)
|
|
MY_VAR=value
|
|
```
|
|
|
|
**Correct**:
|
|
```jsonc
|
|
// wrangler.jsonc
|
|
{
|
|
"vars": {
|
|
"MY_VAR": "production-value"
|
|
}
|
|
}
|
|
```
|
|
|
|
**For secrets**:
|
|
```bash
|
|
npx wrangler secret put MY_SECRET
|
|
```
|
|
|
|
---
|
|
|
|
## 13. Tool Schema Validation Error
|
|
|
|
**Error**: `ZodError: Invalid input type`
|
|
|
|
**Diagnosis**: Client sends different type than schema expects
|
|
|
|
**Solution**: Use Zod transforms or coerce
|
|
```typescript
|
|
// Client sends string "123", but you need number
|
|
{
|
|
count: z.string().transform(val => parseInt(val, 10))
|
|
}
|
|
|
|
// Or use coerce
|
|
{
|
|
count: z.coerce.number()
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 14. Multiple Transport Endpoints Conflicting
|
|
|
|
**Error**: `/sse` returns 404 after adding `/mcp`
|
|
|
|
**Diagnosis**: Path matching issue
|
|
|
|
**Wrong**:
|
|
```typescript
|
|
if (pathname === "/sse") { // ❌ Misses /sse/message
|
|
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
|
|
}
|
|
```
|
|
|
|
**Correct**:
|
|
```typescript
|
|
if (pathname === "/sse" || pathname.startsWith("/sse/")) { // ✅
|
|
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 15. Local Testing Limitations
|
|
|
|
**Error**: OAuth flow fails in `npm run dev`
|
|
|
|
**Diagnosis**: Miniflare doesn't support all DO features
|
|
|
|
**Solutions**:
|
|
|
|
**Option 1**: Use remote dev
|
|
```bash
|
|
npx wrangler dev --remote
|
|
```
|
|
|
|
**Option 2**: Test OAuth on deployed Worker
|
|
```bash
|
|
npx wrangler deploy
|
|
# Test at https://my-mcp.workers.dev
|
|
```
|
|
|
|
**Option 3**: Mock OAuth for local testing
|
|
```typescript
|
|
if (env.ENVIRONMENT === "development") {
|
|
// Skip OAuth, use mock user
|
|
return {
|
|
userId: "test-user",
|
|
email: "test@example.com"
|
|
};
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## General Debugging Tips
|
|
|
|
### Check Logs
|
|
```bash
|
|
npx wrangler tail
|
|
```
|
|
|
|
### Test with MCP Inspector
|
|
```bash
|
|
npx @modelcontextprotocol/inspector@latest
|
|
```
|
|
|
|
### Verify Bindings
|
|
```bash
|
|
npx wrangler kv:namespace list
|
|
npx wrangler d1 list
|
|
```
|
|
|
|
### Check Deployment
|
|
```bash
|
|
npx wrangler deployments list
|
|
```
|
|
|
|
### View Worker Code
|
|
```bash
|
|
npx wrangler whoami
|
|
# Visit dashboard: https://dash.cloudflare.com/
|
|
```
|
|
|
|
---
|
|
|
|
**Still stuck?** Check:
|
|
- Cloudflare Docs: https://developers.cloudflare.com/agents/
|
|
- MCP Spec: https://modelcontextprotocol.io/
|
|
- Community: https://community.cloudflare.com/
|