Skip to content

Mixing up Streaming HTTP protocol versions results in connection failures #5619

Open

Description

@gitcnd

What version of Codex is running?

codex-cli 0.47.0

What subscription do you have?

Plus

Which model were you using?

n/a

What platform is your computer?

Ubuntu WSL1

What issue are you seeing?

codex is not authenticating with some non-stdio MCP servers.

Take the rest with a grain of salt (Claude analysis of the server logs, and your github code):-

The Codex client is sending 2025-06-18 but appears to be expecting 2024-11-05 behavior (immediate close after endpoint event).

Based on the documentation from Context7 about the official rmcp SDK, and examining the Codex code:

🎯 Root Cause Analysis: Client Bug Confirmed

What I Found:

  1. The Official rmcp SDK Documentation shows that for Streamable HTTP transport, the server should:

    • Send SSE headers
    • Send the endpoint event
    • Then continue processing the initialize request synchronously and send the response via SSE
  2. Your Server is Correct: The logs show you're doing exactly that:

    18:58:39 Streaming HTTP response... (SSE headers + endpoint event)
    18:58:39 Processing initial request for session...
    18:58:39 JSONRPC Request... method=initialize
    18:58:39 Session FIN detected: SSL client closed connection gracefully
    
  3. The Codex Client Bug: Looking at the client code structure:

    • auth_status.rs line 21 uses "2024-11-05" for OAuth discovery
    • But the logs show the client sent "protocolVersion":"2025-06-18" in the initialize request
    • This is the problem! The client is mixing protocol versions and may be expecting immediate JSON response instead of SSE-based response

The Protocol Version Mismatch:

From the MCP spec documentation you showed me earlier and the Context7 docs:

  • 2024-11-05: Original spec where initialize response could be synchronous
  • 2025-06-18: New spec where Streamable HTTP uses SSE for all messages including initialize response

The Codex client is sending 2025-06-18 but appears to be expecting 2024-11-05 behavior (immediate close after endpoint event).

✅ Conclusion: This is 100% a Client Bug

Your server implementation is completely correct. The Codex client (codex-mcp-client v0.47.0) has a bug where it:

  1. Sends protocol version 2025-06-18
  2. Receives the SSE endpoint event
  3. Immediately closes the connection without waiting for the initialize response via SSE
  4. This causes the "connection closed: initialize response" error

Recommended Action:

Report this as a bug to the Codex team at https://github.com/openai/codex/issues with:

  • Your server logs
  • The fact that your server works perfectly with Cursor IDE
  • Reference to the MCP 2025-06-18 spec requirement that responses come via SSE

Your OAuth implementation and server are working perfectly. ✅

Logs:-

Discovery:-

18:58:39 Connect from ('127.0.0.1', 63924)
18:58:39 Request from 127.0.0.1:63924 < {'Method': 'GET', 'Path': '/.well-known/oauth-authorization-server/sse', 'Headers': {'mcp-protocol-version': '2024-11-05', 'accept': '/', 'host': '127-0-0-1.local.aurafriday.com:31173'}, 'Body length': 0} Body:
18:58:39 RESponse to 127.0.0.1:63924 > 1174 bytes\nb'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nCache-Control: public, max-age=3600\r\nContent-Length: 1045\r\nConnection: close\r\n\r\n{\n "issuer": "https://127-0-0-1.local.aurafriday.com:31173",\n "authorization_endpoint": "https://127-0-0-1.local.aurafriday.com:31173/oauth2/authorize",\n "token_endpoint": "https://127-0-0-1.local.aurafriday.com:31173/oauth2/token",\n "registration_endpoint": "https://127-0-0-1.local.aurafriday.com:31173/oauth2/register",\n "introspection_endpoint": "https://127-0-0-1.local.aurafriday.com:31173/oauth2/introspect",\n "revocation_endpoint": "https://127-0-0-1.local.aurafriday.com:31173/oauth2/revoke",\n "grant_types_supported": [\n "authorization_code",\n "refresh_token"\n ],\n "response_types_supported": [\n "code"\n ],\n "response_modes_supported": [\n "query",\n "form_post"\n ],\n "code_challenge_methods_supported": [\n "S256"\n ],\n "token_endpoint_auth_methods_supported": [\n "client_secret_basic",\n "client_secret_post",\n "none"\n ],\n "scopes_supported": [\n "offline_access"\n ],\n "claims_parameter_supported": false,\n "request_parameter_supported": false,\n "request_uri_parameter_supported": false\n}'
18:58:39 Session Initiated graceful socket shutdown (FIN sent)
18:58:39 Session Client socket closed
18:58:39 Connect from ('127.0.0.1', 63925)

Auth:-

18:58:39 Request from 127.0.0.1:63925 < {'Method': 'POST', 'Path': '/sse', 'Headers': {'accept': 'text/event-stream, application/json', 'authorization': 'Bearer ', 'content-type': 'application/json', 'host': '127-0-0-1.local.aurafriday.com:31173', 'content-length': '198'}, 'Body length': 198} Body: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{"elicitation":{}},"clientInfo":{"name":"codex-mcp-client","title":"Codex","version":"0.47.0"}}} 18:58:39 Auth Attempting Bearer OAuth for OAuth client: Codex from 127.0.0.1:63925 18:58:39 Auth Successful Bearer OAuth authentication for OAuth client: Codex from 127.0.0.1:63925 18:58:39 Streaming HTTP Parsed initial request: {'jsonrpc': '2.0', 'id': 0, 'method': 'initialize', 'params': {'protocolVersion': '2025-06-18', 'capabilities': {'elicitation': {}}, 'clientInfo': {'name': 'codex-mcp-client', 'title': 'Codex', 'version': '0.47.0'}}} 18:58:39 New streaming HTTP connection from 127.0.0.1:63925, session_id=f9c5483294394a9ca75e32ea1870f67d 18:58:39 Streaming HTTP response to 127.0.0.1:63925 > b'HTTP/1.1 200 OK\r\nContent-Type: text/event-stream; charset=utf-8\r\nConnection: keep-alive\r\nCache-Control: no-store\r\nX-Accel-Buffering: no\r\nAccess-Control-Allow-Origin: null\r\nAccess-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Allow-Credentials: true\r\nAccess-Control-Max-Age: 86400\r\n\r\nevent: endpoint\r\ndata: /messages/?session_id=f9c5483294394a9ca75e32ea1870f67d\r\n\r\n' 18:58:39 Streaming HTTP Processing initial request for session f9c5483294394a9ca75e32ea1870f67d 18:58:39 JSONRPC Request session=f9c5483294394a9ca75e32ea1870f67d, method=initialize, id=0 18:58:39 Session FIN detected: SSL client 127.0.0.1:63925 closed connection gracefully 18:58:39 Session Disconnect detected for client 127.0.0.1:63925 before sending

What steps can reproduce the bug?

Maybe work out which protocol(s) you want to support, and check if the flow is correct?
Claude 4.5 (Above) is not always right.

What is the expected behavior?

OAuth should work.

Additional information

See also: 5588
Attempts OAuth even when the MCP SSE server does not support oauth

Activity

  1. added
    mcpIssues related to the use of model context protocol (MCP) servers
    on Oct 24, 2025
  2. gpeal commented on Nov 3, 2025

    @gpeal
    Contributor

    Is there a public MCP server I can test against? I don't totally trust the AI report attached but can take a look if there is a public repro.

  3. eirenik0 commented on Dec 23, 2025

    @eirenik0

    @gpeal I see that it's a little bit different but might be related. You could test it with this server, for example, https://docs.z.ai/devpack/mcp/reader-mcp-server.

    I see the error:

    MCP client for `web-reader` failed to start: MCP startup failed: handshaking with MCP
      server failed: Send message error Transport
      [rmcp::transport::worker::WorkerTransport<rmcp::transport::streamable_http_client::Stre
      amableHttpClientWorker<reqwest::async_impl::client::Client>>] error: Transport channel
      closed, when send initialized notification
    

    And feature experimental_use_rmcp_client = true is doesn't help.

  4. guyinwonder168 commented on Mar 15, 2026

    @guyinwonder168

    Confirming this with a fresh repro on 2026-03-15 against the Z.AI public MCP endpoints.

    Setup:

    • web-search-prime: https://api.z.ai/api/mcp/web_search_prime/mcp
    • web-reader: https://api.z.ai/api/mcp/web_reader/mcp
    • zread: https://api.z.ai/api/mcp/zread/mcp
    • Codex config uses the documented streamable HTTP fields:
      • bearer_token_env_var = "ZAI_API_KEY"
      • http_headers = { Accept = "application/json, text/event-stream" }

    Observed in Codex startup:

    MCP client for `web-search-prime` failed to start: MCP startup failed: handshaking with MCP server failed: Send message error Transport [...] error: Transport channel closed, when send initialized notification
    MCP client for `web-reader` failed to start: MCP startup failed: handshaking with MCP server failed: Send message error Transport [...] error: Transport channel closed, when send initialized notification
    MCP client for `zread` failed to start: MCP startup failed: handshaking with MCP server failed: Send message error Transport [...] error: Transport channel closed, when send initialized notification
    

    What I verified manually outside Codex:

    1. initialize succeeds on all three endpoints when I send:
      • Authorization: Bearer $ZAI_API_KEY
      • Accept: application/json, text/event-stream
      • JSON-RPC initialize
    2. The server returns 200, content-type: text/event-stream, and an mcp-session-id.
    3. The initialize result advertises protocolVersion: "2024-11-05" even when the client asks for 2025-03-26.
    4. Sending notifications/initialized manually with the returned Mcp-Session-Id succeeds with 200.
    5. Sending tools/list manually after that also succeeds.

    So this does not look like:

    • bad auth
    • missing Accept: application/json, text/event-stream
    • dead/unreachable endpoint
    • wrong Codex config keys

    It looks like a compatibility problem in the Codex streamable HTTP MCP client during/after the initialized step, likely related to protocol negotiation or how this older server implementation is handled.

    If useful, I can provide exact curl repro commands, but the short version is: the same session works manually and fails only in Codex with Transport channel closed, when send initialized notification.

  5. guyinwonder168 commented on Mar 15, 2026

    @guyinwonder168

    This looks closely related to #5208.

    Why I think so:

    • same failure point: Transport channel closed, when send initialized notification
    • same transport family: streamable HTTP MCP
    • same pattern: works in manual/client alternatives, fails specifically in Codex

    The difference is that this is still reproducible now with public Z.AI MCP servers, despite #5208 having been closed as fixed via the earlier rust-sdk work.

    So this may be either:

    • a regression of the old bug class, or
    • a remaining compatibility gap for servers that respond successfully to initialize but advertise protocolVersion: "2024-11-05" while Codex is initiating with 2025-03-26.

    If helpful, I can post exact curl repro commands for the full successful manual sequence (initialize -> notifications/initialized -> tools/list).

  6. guyinwonder168 commented on Mar 15, 2026

    @guyinwonder168

    Here is a minimal manual repro that succeeds outside Codex for web_search_prime. The same pattern also works for web_reader and zread by swapping the URL.

    export URL='https://api.z.ai/api/mcp/web_search_prime/mcp'
    export AUTH="Authorization: Bearer $ZAI_API_KEY"
    
    # 1) initialize
    curl -sS -D /tmp/mcp.init.headers -o /tmp/mcp.init.body \
      -X POST "$URL" \
      -H "$AUTH" \
      -H 'Accept: application/json, text/event-stream' \
      -H 'Content-Type: application/json' \
      --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl-repro","version":"0.0.1"}}}'
    
    SESSION_ID=$(awk 'BEGIN{IGNORECASE=1} /^mcp-session-id:/ {print $2}' /tmp/mcp.init.headers | tr -d '\r')
    
    echo '--- initialize headers ---'
    sed -n '1,20p' /tmp/mcp.init.headers
    echo '--- initialize body ---'
    sed -n '1,20p' /tmp/mcp.init.body
    
    # 2) notifications/initialized
    curl -sS -D /tmp/mcp.notif.headers -o /tmp/mcp.notif.body \
      -X POST "$URL" \
      -H "$AUTH" \
      -H 'Accept: application/json, text/event-stream' \
      -H 'Content-Type: application/json' \
      -H "Mcp-Session-Id: $SESSION_ID" \
      --data '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
    
    echo '--- initialized headers ---'
    sed -n '1,20p' /tmp/mcp.notif.headers
    echo '--- initialized body ---'
    sed -n '1,20p' /tmp/mcp.notif.body
    
    # 3) tools/list
    curl -sS -D /tmp/mcp.tools.headers -o /tmp/mcp.tools.body \
      -X POST "$URL" \
      -H "$AUTH" \
      -H 'Accept: application/json, text/event-stream' \
      -H 'Content-Type: application/json' \
      -H "Mcp-Session-Id: $SESSION_ID" \
      --data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
    
    echo '--- tools/list headers ---'
    sed -n '1,20p' /tmp/mcp.tools.headers
    echo '--- tools/list body ---'
    sed -n '1,40p' /tmp/mcp.tools.body

    Expected successful manual behavior:

    • step 1 returns 200, content-type: text/event-stream, and an mcp-session-id
    • step 2 returns 200
    • step 3 returns 200 plus the tool list

    In my environment, this full manual sequence succeeds, while Codex startup still fails earlier with:

    Transport channel closed, when send initialized notification
    
  7. caibirdme commented on Mar 23, 2026

    @caibirdme

    I find a workaround for this

    use mcp-proxy, you should install it first

    here's the example config.toml

    [mcp_servers.zread]
    command = "mcp-proxy"
    args = ["--headers", "Authorization", "Bearer YOURS", "--transport", "streamablehttp", "https://open.bigmodel.cn/api/mcp/zread/mcp"]
    
    [mcp_servers.web-reader]
    command = "mcp-proxy"
    args = ["--headers", "Authorization", "Bearer YOURS", "--transport", "streamablehttp", "https://open.bigmodel.cn/api/mcp/web_reader/mcp"]
    
    [mcp_servers.web-search-prime]
    command = "mcp-proxy"
    args = ["--headers", "Authorization", "Bearer YOURS", "--transport", "streamablehttp", "https://open.bigmodel.cn/api/mcp/web_search_prime/mcp"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmcpIssues related to the use of model context protocol (MCP) servers

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions