Skip to content

fix: Claude.ai connection fails behind reverse proxy (trust proxy + root MCP endpoint) #30

Description

@Kakuzen93

Please be gentle with me here, this is a .md that I had generated with Claude that documented the steps I had to take to get this project working. This is also my first GitHub issue I've ever made. Let me know if I did alright? lol

Note: Tested against the official Docker image ghcr.io/freema/openclaw-mcp:1.4.1 pulled from GHCR with no modifications prior to applying the fixes described below.


Description

Two bugs prevent openclaw-mcp from connecting to Claude.ai when running behind a reverse proxy (Cloudflare Tunnel, ngrok, etc.).

Note: MCP_ISSUER_URL was correctly set to the public HTTPS tunnel URL per the README. These bugs occur independently of that setting.


Bug 1: X-Forwarded-For validation error crashes OAuth token endpoint

Symptoms: docker compose logs shows a repeating ValidationError from express-rate-limit:

ValidationError: The 'X-Forwarded-For' header is set but the Express 'trust proxy'
setting is false (default). This could indicate a misconfiguration...
ERR_ERL_UNEXPECTED_X_FORWARDED_FOR

This causes the /token endpoint to fail, breaking the OAuth flow entirely.

Root cause: Express's trust proxy setting is false by default. When running behind a proxy that sets X-Forwarded-For, express-rate-limit throws a ValidationError.

Fix: Add app.set('trust proxy', 1) immediately after createMcpExpressApp() in src/server/sse.ts:

const app = createMcpExpressApp({ host: config.host });
app.set('trust proxy', 1); // ← add this

Suggested improvement: Support a TRUST_PROXY environment variable so users can configure this without patching the source.


Bug 2: Claude.ai connects to / (root) instead of /mcp

Symptoms: After OAuth completes successfully, Claude.ai hits POST / and GET / which return 404, and the connector shows as failed.

Root cause: Claude.ai uses the resource URL from /.well-known/oauth-protected-resource as the MCP endpoint. This resolves to the server root (/), but the MCP streamable HTTP handler is only registered at /mcp.

Fix: Register the streamable HTTP handler at / in addition to /mcp in src/server/sse.ts:

// Existing
app.get('/mcp', ...withAuth(handleStreamableRequest));
app.post('/mcp', ...withAuth(handleStreamableRequest));
app.delete('/mcp', ...withAuth(handleStreamableRequest));

// Add these for Claude.ai compatibility
app.get('/', ...withAuth(handleStreamableRequest));
app.post('/', ...withAuth(handleStreamableRequest));
app.delete('/', ...withAuth(handleStreamableRequest));

Environment

  • openclaw-mcp version: 1.4.1
  • Deployment: Docker behind Cloudflare Tunnel
  • Client: Claude.ai custom MCP connector
  • OS: Debian 13

Steps to Reproduce

  1. Deploy openclaw-mcp with AUTH_ENABLED=true behind Cloudflare Tunnel or ngrok
  2. Add a custom MCP connector in Claude.ai pointing to the tunnel URL
  3. Click Connect — OAuth flow fails with ValidationError (Bug 1), or succeeds but MCP connection fails with 404 (Bug 2)

Both fixes were verified working together. Happy to open a PR if helpful!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions