Skip to content

fix(client): wait for slow HTTP discovery in Auto mode - #1435

Open
charliermarsh wants to merge 1 commit into
modelcontextprotocol:mainfrom
charliermarsh:charlie/http-discovery-fallback
Open

charliermarsh wants to merge 1 commit into
modelcontextprotocol:mainfrom
charliermarsh:charlie/http-discovery-fallback

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Nov 6, 2026 •

Copy link
Copy Markdown

Summary

At present, a Streamable HTTP server that takes 12 seconds to answer server/discover can fail to connect in Auto mode. After 10 seconds, the client queues a legacy initialize request with ID 1. The HTTP worker is still reading the discovery POST, so when the server replies with ID 0, the client rejects the correct reply with expected 1, got 0 (see: openai/codex#57631).

In this PR, Auto still gives discovery 10 seconds, including time spent sending the probe and negotiating a version. Then, when the HTTP transport accepts the probe, it marks that request before the send future is polled. If the 10 seconds expire, Auto keeps waiting for that HTTP handshake within the caller’s startup deadline. A JSON or SSE reply can finish discovery, and an explicit legacy rejection still triggers initialize immediately.

@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-service Service layer changes T-transport Transport layer changes labels Nov 6, 2026
@github-actions
github-actions Bot requested a review from jamadeo November 6, 2026 17:09
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

@jamadeo is on triage rotation this week and is the reviewer for this one.

Labels come from the PR labeler. Please review the change.

Automated triage. Labels are a first pass and the assigned maintainer has the final say.

@charliermarsh
charliermarsh force-pushed the charlie/http-discovery-fallback branch 3 times, most recently from 2cfa51f to cec7d60 Compare November 6, 2026 18:14
@charliermarsh charliermarsh changed the title fix(client): handle late discovery replies during startup fix(client): wait for slow HTTP discovery in Auto mode Nov 6, 2026
@charliermarsh
charliermarsh force-pushed the charlie/http-discovery-fallback branch from cec7d60 to 0fff88e Compare November 6, 2026 18:35
@charliermarsh
charliermarsh force-pushed the charlie/http-discovery-fallback branch from 0fff88e to 74d1079 Compare November 6, 2026 18:49
@charliermarsh
charliermarsh marked this pull request as ready for review November 6, 2026 19:02
@charliermarsh
charliermarsh requested a review from a team as a code owner November 6, 2026 19:02
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-06T19:05:23.959702Z 74d1079 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@jokemanfire jokemanfire left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at head 74d1079 (code read at the head SHA; tests run locally).

Verified

  • cargo test -p rmcp --lib --test test_discover_http_client_startup --features "client server reqwest transport-streamable-http-client-reqwest transport-streamable-http-server" → 462 passed, 0 failed (lib) and 7 passed, 0 failed (HTTP startup), including the new case_4_queued_http_send and the auto_falls_back_while_discovery_send_awaits_acknowledgment worker test.
  • cargo fmt --check clean at this head.
  • The mechanism works for the reported case: case_4_queued_http_send defers the probe send by 12 s and Auto still reaches initialize instead of falling back at 10 s.

Asks

  1. What bounds the wait after the probe is submitted? For streamable HTTP this removes the only deadline on discovery:

    • is_control_message returns false for every Request (streamable_http_client.rs:1127-1136), so control_request_timeout (default 5 s) does not apply to the discover POST.
    • the default reqwest client is built without a request timeout (transport/common/reqwest, Client::builder() at ~:489), and StreamableHttpClientTransportConfig has no per-request deadline.

    So once submitted_to_http is set, discovery.await is bounded only by the caller's cancellation token. A peer that accepts the POST and never sends response headers (the case #1149's bounded wait was added for) now never falls back, where main falls back after 10 s and sends initialize. That is a behavioural regression for silently-hanging HTTP servers, not just slow ones. Can you keep a bounded grace period after submission (e.g. control_request_timeout-style, or auto_discover_timeout plus a margin), or state explicitly that callers must supply their own deadline?

  2. submitted_to_http means "queued", not "accepted". on_request_submitted runs synchronously in WorkerTransport::send (transport/worker.rs, before the message reaches the worker channel), while the POST is only handed to the HTTP client later in process_startup_message. A backlogged worker therefore sets the flag without the server having seen anything. If the intent is "the server accepted the probe", the flag should be set where post_message_with_max_sse_event_size is invoked, not at enqueue time. The doc comment ("when the HTTP transport accepts the probe") currently overstates it.

  3. Version-negotiation retries inherit the unbounded wait. In discover_startup's retry loop, the probe is inserted into every attempt; once the flag is set, later attempts are also no longer bounded by auto_discover_timeout, so a server that rejects version 1 quickly but hangs on version 2 stalls Auto indefinitely. If you add the grace period from (1), please apply it to the whole discovery phase rather than to the first probe only.

  4. Coordination with #1436. That draft skips late probe replies generically (stdio and others); this PR avoids the late reply on HTTP by waiting. They do not conflict as written, and I would expect both to be useful — but the deadline question in (1) applies to both, and neither PR's tests cover the combination.

Minor: the unconditional sleep(12s) added inside version_negotiating_http_handler only affects paused tests today, but it makes that shared handler wall-clock-sensitive for any future non-paused caller.

No approval from me: (1) is an open behaviour question, not a style nit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-service Service layer changes T-test Testing related changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants