Skip to content

fix(client): treat *.localhost as loopback for OAuth token endpoints - #2597

Merged
felixweinberger merged 4 commits into
modelcontextprotocol:mainfrom
arimu1:fix/2591-loopback-localhost-subdomains
Sep 23, 2026
Merged

felixweinberger merged 4 commits into
modelcontextprotocol:mainfrom
arimu1:fix/2591-loopback-localhost-subdomains

Conversation

@arimu1

@arimu1 arimu1 commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Extend isLoopbackHost so hostnames ending in .localhost are treated as loopback (RFC 6761 §6.3), in addition to localhost, 127.0.0.1, and ::1 / [::1].
  • Lets SEP-2207’s assertSecureTokenEndpoint allow plain-HTTP token endpoints on host-based multi-tenant local setups (e.g. http://tenant.example.localhost:3300/...) instead of throwing InsecureTokenEndpointError.
  • Same helper drives application_type inference for redirect URIs, so *.localhost redirects correctly default to native.

Fixes #2591

Motivation

.localhost is a reserved TLD: names under it resolve to loopback by specification (and by browsers / OS resolvers). The SDK’s https token-endpoint guard was stricter than that definition and blocked common local multi-tenant Host-header setups that cannot use bare http://localhost.

Test plan

  • Extended unit coverage in packages/client/test/client/auth.test.ts:
    • assertSecureTokenEndpoint accepts tenant.example.localhost
    • refreshAuthorization permits *.localhost token endpoints
    • resolveClientMetadata derives application_type: 'native' for *.localhost redirects
  • Changeset for @modelcontextprotocol/client patch
  • pnpm --filter @modelcontextprotocol/client test — 800/800 passed
  • Pre-push: pnpm build:all / typecheck:all / lint:all green

Notes

Straightforward bug fix + tests per CONTRIBUTING. Implementation assisted by tooling.

(Opened as draft due to API restriction converting to ready — please mark Ready for review if required.)

RFC 6761 §6.3 reserves names ending in .localhost as loopback. Exempt
them from the SEP-2207 https token-endpoint guard (same as localhost /
127.0.0.1 / ::1) so host-based multi-tenant local dev works.

Fixes modelcontextprotocol#2591
@changeset-bot

changeset-bot Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a24ce17

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/client Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/core-internal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2597

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@2597

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@2597

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2597

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@2597

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2597

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2597

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2597

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2597

commit: a24ce17

@arimu1 arimu1 closed this Aug 1, 2026
@arimu1 arimu1 reopened this Aug 1, 2026
@claude claude Bot added the v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes label Aug 18, 2026
@seebi

seebi commented Sep 1, 2026

Copy link
Copy Markdown

Thanks for the work on this!

Is there an update on the plan here, and any rough idea of when it might be merged?

@cliffhall

Copy link
Copy Markdown
Member

Requesting a review on this from the Inspector side, where the guard surfaces most often.

We have the same request open as modelcontextprotocol/inspector#1944, filed independently of #2591 — so that's two unrelated reporters here, plus the three confirmations on #2591 including a non-Inspector desktop client hitting it against Keycloak. That last one is the useful signal: it's isLoopbackHost itself, not anything client-specific.

For what it's worth, the argument in #2591 holds up against the layers below. RFC 6761 §6.3 reserves any name ending in .localhost. with loopback semantics and makes it non-registrable, so the suffix check can't be spoofed by acquiring a domain. W3C Secure Contexts already classifies http://*.localhost as potentially trustworthy, and Chrome and Firefox 84+ implement that — which means the SDK is presently stricter than the browser it runs inside. Vite, Django and Rails all default-allow .localhost in their own host allow-lists for the same reason.

We can't work around it downstream: assertSecureTokenEndpoint runs inside executeTokenRequest, takes no options, and auth() special-cases the error to rethrow rather than retry — so there's no hook the Inspector could reach. We closed our own request for a host-exception escape hatch (inspector#1911) as not planned on exactly that basis, and because broadening the exemption by configuration is the wrong shape. This PR is the right shape: it doesn't loosen the default, it corrects the exemption list to match the loopback definition the error message already cites.

Happy to test a prerelease against the Inspector's OAuth suites if that helps move it.

The migration guide's exempt-host list for the token endpoint TLS guard now includes *.localhost. The changeset says that the SDK does not resolve the name itself, so *.localhost reaches the local machine only if the system resolver follows RFC 6761.
@felixweinberger
felixweinberger marked this pull request as ready for review September 23, 2026 18:01
@felixweinberger
felixweinberger requested a review from a team as a code owner September 23, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAuth: isLoopbackHost rejects *.localhost subdomains, breaking host-based local dev (InsecureTokenEndpointError)

4 participants