← Files CloudflareARCHIVED FILE
skills/building-mcp-server-on-cloudflare/references/troubleshooting.md
6.02 KB · Sep 30, 2026 · 23:18 UTC
# MCP Server Troubleshooting
Common errors and solutions for MCP servers on Cloudflare.
## Connection Issues
### "Failed to connect to MCP server"
**Symptoms:** Client cannot establish connection to deployed server.
**Causes & Solutions:**
1. **Wrong URL path**
```
# Wrong
https://my-server.workers.dev/
# Correct
https://my-server.workers.dev/mcp
```
2. **Worker not deployed**
```bash
wrangler deployments list
# If empty, deploy first:
wrangler deploy
```
3. **Worker crashed on startup**
```bash
wrangler tail
# Check for initialization errors
```
### "WebSocket connection failed"
MCP uses SSE (Server-Sent Events), not WebSockets. Ensure your client is configured for SSE transport:
```json
{
"mcpServers": {
"my-server": {
"command": "npx",
"args": ["mcp-remote", "https://my-server.workers.dev/mcp"]
}
}
}
```
### CORS Errors in Browser
If calling from browser-based client:
```typescript
// Add CORS headers to your worker
export default {
async fetch(request: Request, env: Env) {
// Handle preflight
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
});
}
const response = await handleRequest(request, env);
// Add CORS headers to response
const headers = new Headers(response.headers);
headers.set("Access-Control-Allow-Origin", "*");
return new Response(response.body, {
status: response.status,
headers,
});
},
};
```
## Tool Errors
### "Tool not found: [tool_name]"
**Causes:**
1. Tool not registered in `init()`
2. Tool name mismatch (case-sensitive)
3. `init()` threw an error before registering tool
**Debug:**
```typescript
async init() {
console.log("Registering tools...");
this.server.tool("my_tool", { ... }, async () => { ... });
console.log("Tools registered:", this.server.listTools());
}
```
Check logs: `wrangler tail`
### "Invalid parameters for tool"
Zod validation failed. Check parameter schema:
```typescript
// Schema expects number, client sent string
this.server.tool(
"calculate",
{ value: z.number() }, // Client must send number, not "123"
async ({ value }) => { ... }
);
// Fix: Coerce string to number
this.server.tool(
"calculate",
{ value: z.coerce.number() }, // "123" → 123
async ({ value }) => { ... }
);
```
### Tool Timeout
Workers have CPU time limits (10-30ms for free, longer for paid). For long operations:
```typescript
this.server.tool(
"long_operation",
{ ... },
async (params) => {
// Break into smaller chunks
// Or use Queues/Durable Objects for background work
// Don't do this:
// await sleep(5000); // Will timeout
return { content: [{ type: "text", text: "Queued for processing" }] };
}
);
```
## Authentication Errors
### "401 Unauthorized"
OAuth token missing or expired.
1. **Check client is handling OAuth flow**
2. **Verify secrets are set:**
```bash
wrangler secret list
# Should show GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
```
3. **Check KV namespace exists:**
```bash
wrangler kv namespace list
# Should show OAUTH_KV
```
### "Invalid redirect_uri"
OAuth callback URL doesn't match app configuration.
**Local development:**
- OAuth app callback: `http://localhost:8788/callback`
**Production:**
- OAuth app callback: `https://[worker-name].[account].workers.dev/callback`
Must match EXACTLY (including trailing slash or lack thereof).
### "State mismatch" / CSRF Error
State parameter validation failed.
1. **Clear browser cookies and retry**
2. **Check KV is storing state:**
```typescript
// In your auth handler
console.log("Storing state:", state);
await env.OAUTH_KV.put(`state:${state}`, "1", { expirationTtl: 600 });
```
3. **Verify same domain for all requests**
## Binding Errors
### "Binding not found: [BINDING_NAME]"
Binding not in `wrangler.toml` or not deployed.
```toml
# wrangler.toml
[[d1_databases]]
binding = "DB" # Must match env.DB in code
database_name = "mydb"
database_id = "xxx-xxx"
```
After adding bindings: `wrangler deploy`
### "D1_ERROR: no such table"
Migrations not applied.
```bash
# Local
wrangler d1 migrations apply DB_NAME --local
# Production
wrangler d1 migrations apply DB_NAME
```
### Durable Object Not Found
```toml
# wrangler.toml must have:
[durable_objects]
bindings = [{ name = "MCP", class_name = "MyMCP" }]
[[migrations]]
tag = "v1"
new_classes = ["MyMCP"]
```
And class must be exported:
```typescript
export { MyMCP }; // Don't forget this!
```
## Deployment Errors
### "Class MyMCP is not exported"
```typescript
// src/index.ts - Must export the class
export { MyMCP } from "./mcp";
// OR in same file
export class MyMCP extends McpAgent { ... }
```
### "Migration required"
New Durable Object class needs migration:
```toml
# Add to wrangler.toml
[[migrations]]
tag = "v2" # Increment version
new_classes = ["NewClassName"]
# Or for renames:
# renamed_classes = [{ from = "OldName", to = "NewName" }]
```
### Build Errors
```bash
# Clear cache and rebuild
rm -rf node_modules .wrangler
npm install
wrangler deploy
```
## Debugging Tips
### Enable Verbose Logging
```typescript
export class MyMCP extends McpAgent {
async init() {
console.log("MCP Server initializing...");
console.log("Environment:", Object.keys(this.env));
this.server.tool("test", {}, async () => {
console.log("Test tool called");
return { content: [{ type: "text", text: "OK" }] };
});
console.log("Tools registered");
}
}
```
View logs:
```bash
wrangler tail --format pretty
```
### Test Locally First
```bash
npm start
npx @modelcontextprotocol/inspector@latest
```
Always verify tools work locally before deploying.
### Check Worker Health
```bash
# List deployments
wrangler deployments list
# View recent logs
wrangler tail
# Check worker status
curl -I https://your-worker.workers.dev/mcp
```
SHA-256: 45dbff8cb48f25368b9397aa05e23bc262ccfb53d9d7cb90d85ea7d1d9105910