MCP app-server 0.147 strips trailing slash from expected authorization-server issuer

Open 💬 6 comments Opened Aug 7, 2026 by Charpup

What version of Codex is running?

codex-cli 0.147.0-alpha.6.5, bundled with ChatGPT Desktop 26.803.41515 (build 6321).

Platform

macOS arm64.

What issue are you seeing?

The 0.147 app-server rejects an already-authenticated Streamable HTTP MCP server while initializing/calling its tools because the expected authorization-server issuer has had its trailing slash removed, but the issuer returned by authorization-server metadata retains that slash.

Sanitized form of the error:

issuer mismatch:
expected https://auth.example.invalid/oauth2
received https://auth.example.invalid/oauth2/

The MCP URL-only configuration has no static bearer token or headers. mcpServerStatus/list reports authStatus=oAuth but exposes an empty tool catalog; a direct read-only mcpServer/tool/call then returns the issuer mismatch.

Reproduction

  1. Configure a Streamable HTTP MCP server using only a URL such as https://mcp.example.invalid/mcp.
  2. Complete OAuth once. The authorization-server metadata advertises an issuer ending in /, such as https://auth.example.invalid/oauth2/.
  3. Start the bundled 0.147 app-server over stdio.
  4. Send initialize with experimentalApi=true, then initialized.
  5. Start an ephemeral read-only thread.
  6. Call mcpServerStatus/list, followed by a read-only mcpServer/tool/call.

Control evidence

On the same machine, against the same unchanged MCP endpoint and stored OAuth account:

  • pinned @openai/codex@0.146.1 app-server succeeds;
  • its allowlisted read-only catalog contains exactly three tools;
  • a fresh serial traversal completed 42/42 tool calls (3 metadata and 39 tree calls) with zero OAuth or issuer errors;
  • bundled 0.147.0-alpha.6.5 fails before the first tool result with the mismatch above.

No PAT, copied bearer token, custom OAuth client secret, browser scraping, or direct REST fallback is involved.

Expected behavior

Codex should preserve and compare the same canonical issuer value on both sides. If authorization-server metadata declares https://auth.example.invalid/oauth2/, the client must not strip the slash only from its expected value and then reject the metadata value it originally discovered.

Relationship to existing issues

This appears distinct from:

  • #31573, where the OAuth callback handler drops the RFC 9207 iss parameter;
  • #35716, where an OAuth resource parameter gains a trailing slash.

This failure happens when an already-authenticated MCP server is initialized/called through app-server, and concerns authorization-server issuer canonicalization rather than callback parsing or the protected-resource parameter.

View original on GitHub ↗

6 Comments

Rigos0 · 18 days ago
  • same
xinix909 · 17 days ago

seme

zousandian · 17 days ago

same problem

Silentpartnercoding · 17 days ago

I traced this to the RMCP dependency rather than the app-server wrapper. Current Codex main pins rmcp 3.0.0. In that release, protected-resource discovery reconstructs the expected issuer from the well-known URL; for a non-root issuer it uses issuer.set_path("/{issuer_path}"), which turns /oauth2/ into /oauth2. Its equality helper only tolerates a trailing slash at the origin root, so the mismatch here is deterministic.

RMCP 3.1.2 already carries the narrow fix: it preserves the exact authorization_servers identifier through discovery and adds protected_resource_metadata_preserves_non_root_issuer_trailing_slash.

That suggests the smallest Codex path is upgrading RMCP and retaining an integration fixture where protected-resource metadata advertises https://auth.example.invalid/oauth2/ and authorization-server metadata returns that exact issuer. I have not run the Codex suite with 3.1.2, so I cannot yet rule out unrelated dependency-upgrade fallout.

jjader-nps · 14 days ago

I can reproduce a related issuer-validation failure with a GitLab Self-Managed Streamable HTTP MCP server on Windows.

Environment:

  • codex-cli 0.147.0-alpha.6.6
  • Current ChatGPT/Codex Desktop app
  • GitLab MCP endpoint: https://gitlab.example.org/api/v4/mcp

Configuration:

codex mcp add gitlab_mcp --url "https://gitlab.example.org/api/v4/mcp"
codex mcp login gitlab_mcp --scopes mcp

codex mcp login fails before opening the browser authorization flow:

Error: Authorization server issuer mismatch:
expected https://gitlab.example.org/api/v4/mcp
received https://gitlab.example.org

The server's OAuth discovery chain appears internally consistent:

  1. An unauthenticated request to /api/v4/mcp returns 401 with:

``text
WWW-Authenticate: Bearer resource_metadata="https://gitlab.example.org/.well-known/oauth-protected-resource/api/v4/mcp"
``

  1. That protected-resource metadata returns:

``json
{
"authorization_servers": [
"https://gitlab.example.org"
]
}
``

  1. https://gitlab.example.org/.well-known/oauth-authorization-server returns:

``json
{
"issuer": "https://gitlab.example.org"
}
``

So this is not a trailing-slash mismatch, but Codex is still expecting the protected MCP resource URL as the authorization-server issuer rather than preserving the authorization_servers identifier from protected-resource metadata.

Silentpartnercoding's RMCP discovery analysis seems relevant. Would updating RMCP to the version that preserves the exact discovered authorization-server identifier also address this root-issuer versus MCP-resource mismatch?

stefanoamorelli · 7 days ago

I can also reproduce!

TL;DR: since 0.147.0-alpha.6.5, Codex never mints or refreshes an OAuth token against my MCP server whose endpoint lives on a subpath (like https://<host>/mcp). Users with an existing token keep working until it expires, after that the client silently retries the dead token forever instead of re-authenticating, and the UI still shows the server as authenticated.

I run a production MCP server with this setup:

  • MCP endpoint: https://<host>/mcp (a subpath, not the root)
  • OAuth authorization server and issuer: https://<host> (the origin, no path)
  • /.well-known/oauth-protected-resource and /.well-known/oauth-protected-resource/mcp: both return 200 and point at the origin as the authorization server
  • /.well-known/oauth-authorization-server: returns 200 with the origin as issuer
  • The path-inserted variants (/.well-known/oauth-authorization-server/mcp, /.well-known/openid-configuration/mcp): return 404

unlike the GitLab report above, there is no wildcard metadata involved (discovery either 404s or returns consistent origin metadata).

This is what I'm observing (grouping a week of traffic by codex-mcp-client user agent):

| version | outcome against a subpath-resource server |
| -------------------------------------------------------------- | ----------------------------------------- |
| 0.146.0 / 0.146.0-alpha.9.2 / 0.146.1 | completes OAuth, successful tool calls |
| 0.147.0-alpha.1.2 | completes OAuth, successful tool calls |
| 0.147.0-alpha.6.5 and later (0.147.0, 0.148.0-alphas, 0.148.0) | zero successful requests |

2 things:

  1. User agents from 0.147.0-alpha.6.5 onward never appear on /token or any /.well-known path (caveat: some discovery flows send an empty user agent, which we cannot attribute per version) (versions up to 0.147.0-alpha.1.2 reach both)
  2. After the token dies there is no backoff and heavy parallelism. I noticed dozens of concurrent rejected requests within the same second from a single client for multiple days. Combined with #39054 (the UI keeps reporting the server as authenticated), users have no signal anything is wrong while the client hammers the server. This is pretty bad :(

We serve everything rmcp's root-fallback discovery needs, and 0.147.0-alpha.1.2 (already on rmcp 3.0.0) completes the flow successfully, hence the change that broke subpath-resource servers appears to land in codex between 0.147.0-alpha.1.2 and 0.147.0-alpha.6.5...

lmk if you have any further details 🙏