OAuth connect fails for internal MCP servers on private/reserved IPs (guard vs. resource-origin check conflict)
- Dominant language
- TypeScript
- Stars
- 2.2k
- Forks
- 283
- Avg merge
- 11h 47m
- Merged PRs (30d)
- 737
Description
## What I'm trying to do
Connect Inspector to an internal, OAuth-protected MCP server. I've independently verified the server itself is healthy and spec-compliant (raw HTTP checks confirm `/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server`, and Dynamic Client Registration via `/register` all return valid RFC 9728 / RFC 8414 metadata, with `Access-Control-Allow-Origin: *`). The blocker seems to be entirely on the Inspector side.
## The blocker
The server's hostname resolves to an IP in a private/reserved range (RFC 6598 CGNAT space, `100.64.0.0/10` — common for internal corporate VPNs/load balancers). When I try to connect (Auth = Auto), every OAuth metadata discovery call fails with:
```json
{"error":"OAuth metadata target resolves to a private/reserved IP address ()", ...}
```
...regardless of Registration Strategy (Automatic/Preregistration/CIMD/DCR all hit this same guard, since it's on the discovery fetch itself). The UI just shows a generic "could not find a usable CIMD or DCR flow" toast — the real reason only shows up in the Network tab.
## What I tried
I stood up a local reverse proxy on `127.0.0.1` (forwarding to the real host) so the configured Server URL would be loopback and pass that guard, rewriting only the minimum in the metadata responses (leaving `resource`, `authorization_endpoint`, `token_endpoint`, `registration_endpoint` pointed at the real host, since the real server needs the real `resource` value or it errors out).
That got further — discovery and CIMD/DCR succeeded, a real `/authorize` URL got built — but the interactive connect flow then rejected it:
```
Rejected OAuth resource indicator from protected resource metadata: Resource indicator
"" is on a different origin than the server URL "http://127.0.0.1:/mcp"
— honoring it would let the server steer tokens to an audience it doesn't own.
```
Which makes sense as a check in isolation, but it seems to directly conflict with the loopback requirement from the first guard — I can't find a configuration where both are satisfied at once for a server like this.
## What I'm asking
Is there an intended way to connect to a private/internal MCP server through local Inspector today that I'm missing (some flag, env var, or config)? I noticed a `MCPJAM_ALLOWED_HOSTS` env var in the shipped bundle but couldn't find anywhere it's actually consulted — is that meant to help here, or is it for something unrelated / not wired up yet?
Any pointers on the recommended path (or whether this is just a known gap right now) would be appreciated.
## Environment
- `@mcpjam/inspector` v2.47.0
- Local/self-hosted mode (`npx @mcpjam/inspector@latest`), macOS
- Node v26
Contributor guide
Research direction
Trace the metadata discovery guard for private/reserved IPs and the later OAuth resource-origin validation in the Inspector connection flow. Search the shipped bundle and source for MCPJAM_ALLOWED_HOSTS to determine whether it is consulted, then reproduce the loopback and internal-host cases. Done should mean a documented or implemented supported path for private MCP servers with tests covering both checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- api, authentication, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 45/100