Repository navigation
fix(client): wait for slow HTTP discovery in Auto mode - #1435
charliermarsh wants to merge 1 commit into
Conversation
|
@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. |
2cfa51f to
cec7d60
Compare
cec7d60 to
0fff88e
Compare
0fff88e to
74d1079
Compare
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
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 newcase_4_queued_http_sendand theauto_falls_back_while_discovery_send_awaits_acknowledgmentworker test.cargo fmt --checkclean at this head.- The mechanism works for the reported case:
case_4_queued_http_senddefers the probe send by 12 s and Auto still reachesinitializeinstead of falling back at 10 s.
Asks
-
What bounds the wait after the probe is submitted? For streamable HTTP this removes the only deadline on discovery:
is_control_messagereturnsfalsefor everyRequest(streamable_http_client.rs:1127-1136), socontrol_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), andStreamableHttpClientTransportConfighas no per-request deadline.
So once
submitted_to_httpis set,discovery.awaitis 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, wheremainfalls back after 10 s and sendsinitialize. 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, orauto_discover_timeoutplus a margin), or state explicitly that callers must supply their own deadline? -
submitted_to_httpmeans "queued", not "accepted".on_request_submittedruns synchronously inWorkerTransport::send(transport/worker.rs, before the message reaches the worker channel), while the POST is only handed to the HTTP client later inprocess_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 wherepost_message_with_max_sse_event_sizeis invoked, not at enqueue time. The doc comment ("when the HTTP transport accepts the probe") currently overstates it. -
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 byauto_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. -
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.
Summary
At present, a Streamable HTTP server that takes 12 seconds to answer
server/discovercan fail to connect inAutomode. After 10 seconds, the client queues a legacyinitializerequest 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 withexpected 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
initializeimmediately.