Using ngrok Locally (Free)

Overview

Claude.ai may fail to send the Bearer token when connecting to a local free-tier ngrok instance with frisian-mcp, even after the OAuth flow completes successfully. The connection establishes, but tool calls arrive with no Authorization header.

The behavior is reproducible, but the root cause is not yet confirmed and may not be specific to ngrok. Candidate explanations under investigation:

  • Wildcard SSL certificate — the free tier serves a shared *.ngrok-free.app certificate. Claude.ai may treat OAuth servers behind such certs differently, though this does not fully explain the observed per-server and date-dependent variation.
  • Claude.ai connector behavior — the same tunnel works for some MCP servers and not others, and previously-working servers began failing on a specific date, which is more consistent with a client-side change than a certificate-trust policy.

Workarounds

Re-save the MCP connector configuration in the AI client. This forces a fresh connection that correctly attaches the Bearer token.

  • Claude.ai: Settings → Integrations → your connector → Save
Full Report May, 2026

Bug Report: Claude.ai MCP Connector Omits Authorization Header on Tool Calls

Date: 2026-05-10
Severity: High — renders token-based auth non-functional for all Claude.ai MCP users
Component: Claude.ai web application — MCP connector / OAuth token forwarding
Reporter: Jeremy Friese


Summary

Claude.ai successfully completes the OAuth 2.0 PKCE flow and obtains a Bearer token, but does not include the Authorization: Bearer <token> header on any subsequent MCP POST requests. Every tool call — including initialize, tools/list, and individual tool invocations — arrives at the server with no Authorization header at all.


Environment

Item Value
Client Claude.ai web application
Client User-Agent Claude-User
MCP server friese-mcp gateway (Django/DRF)
Transport HTTPS via ngrok tunnel (*.ngrok-free.app)
Auth protocol OAuth 2.0 PKCE (RFC 7636)
MCP protocol version 2025-11-25
Server IPs (Anthropic egress) 160.79.106.36, 160.79.106.37
Test date 2026-05-10

Steps to Reproduce

  1. Stand up an MCP server that requires Bearer token authentication.
  2. In Claude.ai, add the server as an MCP connector (Settings → Integrations → Add MCP server).
  3. Complete the OAuth PKCE authorization flow when prompted — Claude.ai navigates to the auth endpoint, user approves, code is exchanged for a token.
  4. Instruct Claude.ai to use a tool from the MCP server.
  5. Observe the raw HTTP headers arriving at the server.

Expected Behavior

Every POST to /mcp should include:

Authorization: Bearer <token>

Actual Behavior

Every POST to /mcp arrives with no Authorization header:

[MCP_AUTH] path=/mcp | has_bearer=False | Authorization=<ABSENT> | User-Agent=Claude-User | Origin=<none> | X-Forwarded-For=160.79.106.36
[MCP_AUTH] path=/mcp | has_bearer=False | Authorization=<ABSENT> | User-Agent=Claude-User | Origin=<none> | X-Forwarded-For=160.79.106.37
[MCP_AUTH] path=/mcp | has_bearer=False | Authorization=<ABSENT> | User-Agent=Claude-User | Origin=<none> | X-Forwarded-For=160.79.106.37

These log lines were captured by a middleware that fires before any authentication processing, logging the raw HTTP_AUTHORIZATION value from Django's request.META. <ABSENT> means the key was not present in the request at all — not an empty string, not malformed.


Testing Timeline

Date MCP Server Claude.ai sends Bearer? Notes
2026-05-06 Netbox Yes Working — confirmed by file creation timestamps
2026-05-09 Paperless-ngx No Broken — first failure observed
2026-05-10 Open edX LMS No Broken — confirmed today
2026-05-10 Open edX LMS GPT Yes GPT control test — same server, token present

The 3-day gap between the last working test (May 6) and the first failure (May 9) is consistent with a Claude.ai deployment or configuration change that broke Bearer token forwarding. If this is a regression rather than a design decision, the change window is narrow: sometime between May 6 and May 9, 2026.


Evidence That This Is Not a Network/Proxy Issue

This was tested against multiple MCP servers all running behind ngrok tunnels on the same machine:

MCP Server Test Date Tunnel Claude.ai sends Bearer?
Netbox 2026-05-06 ngrok (HTTPS) Yes
Paperless-ngx 2026-05-09 ngrok (HTTPS) No
Open edX LMS 2026-05-10 ngrok (HTTPS) No

If ngrok or any other network layer were stripping the Authorization header, it would affect all services equally. The selective nature (works for Netbox, fails for Paperless and Open edX) indicates the bug is in Claude.ai's MCP connector, conditionally triggered by something in the OAuth server's response or metadata — or a recent Claude.ai deployment broke token forwarding for a class of OAuth servers while leaving others intact.

Other clients tested against the same servers:

Client User-Agent Sends Bearer on tool calls?
Claude Code CLI claude-code/* Yes
GPT MCP connector openai-mcp/1.0.0 (ChatGPT) Yes ✓ (confirmed 2026-05-10)
Claude.ai web Claude-User No

Confirmed: GPT Sends Bearer Token Correctly

On 2026-05-10 at 15:35 UTC, GPT connected to the same MCP server over the same ngrok tunnel and sent a valid Bearer token on every MCP POST:

[MCP_AUTH] path=/mcp | has_bearer=True | Authorization=Bearer abb3ed53a82b695a67d78f9a9045a26fb0900602808dc352e9f5fe7e5ca3d4d5 | User-Agent=openai-mcp/1.0.0 (ChatGPT) | Origin=<none> | X-Forwarded-For=20.169.72.103
[MCP_AUTH] path=/mcp | has_bearer=True | Authorization=Bearer abb3ed53a82b695a67d78f9a9045a26fb0900602808dc352e9f5fe7e5ca3d4d5 | User-Agent=openai-mcp/1.0.0 (ChatGPT) | Origin=<none> | X-Forwarded-For=20.169.72.104
[MCP_AUTH] path=/mcp | has_bearer=True | Authorization=Bearer abb3ed53a82b695a67d78f9a9045a26fb0900602808dc352e9f5fe7e5ca3d4d5 | User-Agent=openai-mcp/1.0.0 (ChatGPT) | Origin=<none> | X-Forwarded-For=20.169.72.109

GPT's OAuth PKCE flow completed in full:

  1. GET /.well-known/oauth-protected-resource — discovered authorization server
  2. GET /.well-known/oauth-authorization-server — fetched token/authorize endpoints
  3. GET /oauth/authorize/?...&resource=https://8d82-75-115-56-59.ngrok-free.app/mcp&... → 302
  4. POST /oauth/token/ → 200, received Bearer token
  5. All subsequent MCP POSTs: Authorization: Bearer <token> present

This definitively rules out ngrok, the server implementation, and the network path as causes. The bug is Claude.ai-specific.


Contact

Jeremy Friese — jeremy@thefriese.com