Skip to content

Opt-in OAuth HTTP exception for trusted local development hosts #1911

Closed as not planned

Description

@sudarsunperu

Inspector v2 rejects OAuth token endpoints using http://host.docker.int, even when the endpoint is reachable only from a local Docker development environment.

Please consider an explicit opt-in mechanism, such as:

MCP_INSPECTOR_ALLOW_INSECURE_OAUTH_HOSTS=host.docker.internal

or a per-server configuration field. The override should:

  • be disabled by default;
  • require exact hostname matching;
  • never allow arbitrary hosts or CIDRs;
  • display a prominent warning;
  • apply only to development/local environments.

Activity

  1. added this to the v2.3.0 milestone on Aug 5, 2026
  2. added
    authIssues and PRs related to authorization
    containerizationRelated to the docker image or containerized operation in general
    on Aug 5, 2026
  3. cliffhall commented on Aug 17, 2026

    @cliffhall
    Member

    Thanks for the detailed writeup — the requirements you listed (off by default, exact hostname, no CIDRs, prominent warning, dev-only) are exactly the right ones to ask for. I dug into why the refusal happens, and the short answer is that the Inspector cannot implement this: the rejection is not ours, and the SDK offers no seam to relax it.

    Where the refusal comes from

    It's an unconditional assert inside @modelcontextprotocol/client:

    /** Loopback hosts exempt from the in-transit `https:` requirement (RFC 8252 §7.3). */
    function isLoopbackHost(hostname) {
      return hostname === "localhost" || hostname === "127.0.0.1"
          || hostname === "[::1]" || hostname === "::1";
    }
    /** SEP-2207: refuse to send credentials to a non-TLS, non-loopback token endpoint. */
    function assertSecureTokenEndpoint(tokenEndpoint) {
      const url = new URL(String(tokenEndpoint));
      if (url.protocol !== "https:" && !isLoopbackHost(url.hostname))
        throw new InsecureTokenEndpointError(url.href);
      return url;
    }

    host.docker.internal misses that four-value list — it isn't a loopback address, it resolves to a routable host-gateway address (typically 192.168.65.254 on Docker Desktop). The message you're seeing is:

    Refusing to send credentials to non-https token endpoint '…'. OAuth token requests MUST use TLS (localhost / 127.0.0.1 / ::1 are exempt).

    Three things about that assert matter here:

    1. It takes no options. No flag, no env var, no provider hook. It runs inside executeTokenRequest — the single funnel for authorization-code exchange and refresh — before any fetch the Inspector could wrap. There is no configuration we could expose that would reach it.
    2. The SDK goes out of its way to make it terminal. auth() special-cases this one error to rethrow rather than fall through to a fresh /authorize redirect. That's a deliberate hard stop, not an incidental validation.
    3. The spec is behind it. MCP's authorization spec says "All authorization server endpoints MUST be served over HTTPS," incorporating OAuth 2.1 §1.5: "All the OAuth protocol URLs … MUST use the https scheme except for loopback interface redirect URIs." The single carve-out is the redirect URI, not the token endpoint.

    To be precise about the last point: I'm reading intent from the implementation, not from a recorded decision — I'm not aware of an upstream proposal that was formally considered and declined. But as things stand the SDK provides no way to opt out, and the Inspector adding one would mean deliberately defeating that check from outside it, which we're not going to do.

    Narrower than it may appear

    Only the token endpoint is gated. The authorization endpoint has no scheme check at all, so http://host.docker.internal/authorize already works today. It's specifically the credential-carrying request that's refused.

    What works right now

    Make the endpoint genuinely loopback from the Inspector's point of view, rather than asking for an exception. Inside the container:

    socat TCP-LISTEN:8080,fork TCP:host.docker.internal:8080
    

    then point the token endpoint at http://127.0.0.1:8080/token. The SDK's loopback exemption then applies legitimately rather than by override, and the credential travels the same wire either way. This mirrors the approach the CLI/TUI OAuth callback docs already recommend for reaching a listener across a host boundary.

    If you need to override the discovered endpoint to do that, v2 now has per-server Authorization URL and Token URL overrides in Server Settings → Authorization (#1906), which replace whatever the authorization server's metadata advertises.

    Closing this

    I'm closing this as not planned, because the change would have to land in the SDK before the Inspector could expose anything — there's no version of it we can build on our side.

    If you'd still like to push for it, the place to file is modelcontextprotocol/typescript-sdk. The most likely-to-succeed framing, for what it's worth, is asking for assertSecureTokenEndpoint to become caller-relaxable — e.g. an allowInsecureTokenEndpointHosts?: string[] on AuthOptions — rather than asking to broaden the loopback list, since the former keeps the decision opt-in and per-caller while the latter would loosen the default for every client. Please do link back to this issue; if such an option ever lands, wiring it to a per-server field in Server Settings is a small change on our side and I'd be glad to pick it up.

  4. self-assigned this
    on Aug 17, 2026
  5. removed this from the v2.3.0 milestone on Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

authIssues and PRs related to authorizationcontainerizationRelated to the docker image or containerized operation in generalenhancementNew feature requestv2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions