Merge pull request #2326 from modelcontextprotocol/v2/fix/2305-cimd-loopback-http

fix: exempt loopback hosts from the CIMD metadata URL HTTPS requirement
This commit is contained in:
Cliff Hall 2026-09-11 10:22:31 -04:00 • committed by GitHub
commit 336e045210
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -560,16 +560,40 @@ from **Client settings**, not from a server's own OAuth settings. A `clientMetad
catalog entry's `oauth` block is silently ignored — and with `supportDCR: true` the connection then
succeeds *via DCR*, which looks like CIMD working until you read the client id.
⚠️ **The Inspector requires that URL to be HTTPS, and there is no loopback exemption**
**Use this server's own document as the `clientMetadataUrl`** — `/client-metadata.json` on the origin
the server announced on startup (`http://127.0.0.1:8092/client-metadata.json` when it got its
configured port; read the announced URL, since this fixture walks upward on `EADDRINUSE` like every
other one here). The Inspector requires HTTPS *except* on the three
loopback literals `localhost`, `127.0.0.1` and `[::1]`
(`getCimdClientMetadataUrlError` in `core/client/config-parse.ts`, applied to `client.json` on disk as
well as to the settings form). So this server's own `http://` document is **not** usable as a
`clientMetadataUrl`: it exists for the authorization-server side of the flow and for tests that drive
the AS directly. To drive the Inspector end to end you need the document served over HTTPS —
`https://127.0.0.1:8443/client-metadata.json` from a throwaway self-signed listener works, with
well as to the settings form), which is the same exemption the SDK applies to token endpoints and what
the runtime already tolerated for an already-stored `client_id`. Give the URL the host the server is
actually listening on; a host outside those three — `localhost.` and `tenant.app.localhost` included,
even though both resolve to loopback — is still rejected, deliberately, so this allow-list and the
SDK's cannot disagree about one URL.
⚠️ **Before [#2305](https://github.com/modelcontextprotocol/inspector/issues/2305) the config and
form validator had no loopback exemption** — the *runtime* already tolerated an `http://` URL as an
already-stored `client_id`, which is precisely the asymmetry that issue is about; there was simply no
way to get such a value past validation and into `client.json`. So driving this flow meant standing
up a throwaway self-signed HTTPS listener to hold the document and setting
`NODE_TLS_REJECT_UNAUTHORIZED=0` in the *test server's* environment so its own fetch of that document
succeeds. That asymmetry is tracked in
[#2305](https://github.com/modelcontextprotocol/inspector/issues/2305); it is the same over-narrow
allow-list shape as the token-endpoint exemption above.
would succeed. That workaround is no longer needed — if you find it in a script or an older note,
delete it.
⚠️ **Use the canonical spelling of the host.** `http://127.1/…` and `http://2130706433/…` are accepted
by the validator (and by the SDK) because both canonicalize to `127.0.0.1` — but a CIMD `client_id`
is compared as a **string** by the authorization server against the URL it dereferenced, so an
exotic spelling can fail that comparison on a server that normalizes differently than the one here.
Paste the origin the fixture announced rather than an equivalent you typed yourself.
⚠️ **Clear OAuth state before the run if you have connected to this fixture before.** Tokens and the
registered client persist in `~/.mcp-inspector/storage/oauth.json` independently of the install-wide
CIMD toggle, and the Inspector reuses valid stored tokens before prompting — so a leftover grant can
carry a run that the registration path never actually completed, which is the same
"it connected, therefore CIMD worked" trap `supportDCR: false` exists to close. Use **Clear OAuth
state and disconnect** (Server Settings → Authorization), or point `MCP_STORAGE_DIR` at a throwaway
directory, which additionally survives a restarted fixture having forgotten a client it once issued.
With that in place: set the metadata URL in Client settings, connect, and open **Connection Info**.
It should read `Client registration — Client ID Metadata (CIMD)` with the **client id equal to the