Repository navigation
Description
Description
OpenCode can no longer complete OAuth authentication against a local MCP server at http://mcp.localhost:18259. This setup worked before.
The token exchange fails with:
Refusing to send credentials to non-https token endpoint 'http://mcp.localhost:18259/token'. OAuth token requests MUST use TLS (localhost / 127.0.0.1 / ::1 are exempt).
Expected behavior: HTTP token endpoints on .localhost subdomains receive the same exception as bare localhost. Non-loopback HTTP endpoints remain blocked.
RFC 6761 §6.3 reserves .localhost subdomains for loopback use. Replacing mcp.localhost with localhost is not an equivalent workaround: the application selects its MCP router through the request’s Host header.
Steps to reproduce
- Run an OAuth-enabled MCP server at
http://mcp.localhost:18259. - Have its OAuth discovery document advertise
http://mcp.localhost:18259/token. - Configure OpenCode:
{
"mcp": {
"servers": {
"local": {
"type": "remote",
"url": "http://mcp.localhost:18259"
}
}
}
}- Authenticate through
/mcps. - The token exchange fails with the error above.
Regression and cause
OpenCode PR #48937, first included in 2.0.4, replaced @modelcontextprotocol/[email protected] with @modelcontextprotocol/[email protected].
The previous SDK did not enforce this token-endpoint TLS check. Client 2.0.0 added the check but exempted only these exact hostnames:
hostname === "localhost" ||
hostname === "127.0.0.1" ||
hostname === "[::1]" ||
hostname === "::1"Consequently, it rejects mcp.localhost.
OpenCode 2.0.26 still pins MCP client 2.0.0. Its compiled binary also contains this exact-host check.
The last working OpenCode version in this setup was not recorded. The dependency change between 2.0.3 and 2.0.4 was verified.
Suggested fix
Update the bundled MCP client to a release containing support for .localhost subdomains, or apply the same hostname change locally.
The MCP SDK already fixed this behavior in PR #2597, merged September 23, 2026. It adds hostname.endsWith(".localhost") to the loopback check while preserving the HTTPS requirement for non-loopback hosts.
The matching SDK report is issue #2591.
OpenCode version
- Installed CLI:
2.0.26 - Running background service:
2.0.24 - Both releases pin MCP client
2.0.0
Operating System
Darwin 25.6.0, arm64.
Terminal
Herdr, TERM=xterm-256color, COLORTERM=truecolor.
Shell: fish.
Activity
opencode-agent commentedon Oct 10, 2026
Thanks for the detailed report. I can confirm this.
I ran a mock OAuth-protected MCP server at
http://mcp.localhost:18259, with its metadata givinghttp://mcp.localhost:18259/tokenas the token endpoint, and configured it as a remote MCP server. Then I ranopencode mcp auth localand completed the authorize redirect. On 2.0.26, discovery, client registration and authorization all succeed. The code exchange then fails with the same error you saw:It still happens on the latest
v2(55dde88). That branch still pins@modelcontextprotocol/[email protected], and that version's loopback check only allows the exact nameslocalhost,127.0.0.1and::1. The script below reproduces it. It needsmcp.localhostto resolve to 127.0.0.1, for example through/etc/hosts.Reproduction script (repro.sh)
Run it:
bash repro.shargszero commentedon Oct 10, 2026
I'd like to work on this.
The pinned
@modelcontextprotocol/[email protected]is already patched in this repo for OAuth behavior, so the smallest fix is to extend that patch with the rule typescript-sdk#2597 shipped: treat a hostname ending in.localhostas loopback, while still refusing non-loopbackhttp:token endpoints. I have that change plus a regression test covering both halves running locally.Happy to bump the bundled client instead if you would rather take a newer release.
argszero commentedon Oct 10, 2026
Implemented in #54345.
The existing
@modelcontextprotocol/[email protected]OAuth patch now accepts the whole.localhostnamespace as loopback, per RFC 6761 §6.3 — the same predicate upstream added intypescript-sdk#2597(shipped in client 2.3.1). The patch was regenerated mechanically rather than hand-edited, so the two pre-existing hunks (url.search = issuer.search,prompt=consent) are byte-identical and the header blob hashes remain the real 40-character ones.