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.appcertificate. 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
- Stand up an MCP server that requires Bearer token authentication.
- In Claude.ai, add the server as an MCP connector (Settings → Integrations → Add MCP server).
- Complete the OAuth PKCE authorization flow when prompted — Claude.ai navigates to the auth endpoint, user approves, code is exchanged for a token.
- Instruct Claude.ai to use a tool from the MCP server.
- 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:
GET /.well-known/oauth-protected-resource— discovered authorization serverGET /.well-known/oauth-authorization-server— fetched token/authorize endpointsGET /oauth/authorize/?...&resource=https://8d82-75-115-56-59.ngrok-free.app/mcp&...→ 302POST /oauth/token/→ 200, received Bearer token- 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