Repository navigation
Replies: 4 comments
|
Independent corroboration from a separate reproduction, plus three data points that may help whoever implements this. 1. The newest published build does not contain a fix. So the gap is present in the newest published build, not only the rc. 2. A discriminator confirming the client retains a dead id rather than omitting it. Against the same endpoint after the server restarted — for the server I tested:
Relevant to the detector: on this server a 3. An asymmetry in the supervisor worth stating explicitly in whatever lands. A failed connect attempt does reach the supervisor — 4. Impact note. The MCP bridge is process-scoped — one plugin instance per Host, shared by every conversation — so starting a new conversation does not recover; only restarting the Host does. Worth reflecting in the final error text, since a per-call failure currently reads as "the server is broken" when the server is healthy. Thanks for the write-up — the proposal is more complete than anything I had. If a ported patch lands, I am happy to exercise it against a long-running Host with a server that restarts underneath it. |
|
Following up with a working implementation, published as an out-of-tree patch since this repo does not take PRs: https://github.com/MaxHou-infinity/dsh-mcp-session-recovery Two things that may be worth having before this lands natively. An A/B control, rather than a test that merely passes. The harness mounts the real
The control is what turns it into evidence: the pristine run reproduces the exact symptom, and the patch is the only variable. The fix is a missing subscription, not missing machinery. I added no retry logic — generation.onerror = (error) => {
if (attemptSettled && isSessionLostError(error)) generationDown(generation);
};with One point for the design, since it changes what "fixing this" has to cover: a failed connect attempt already reaches the supervisor ( The patch applies cleanly to both |
|
A correction to my earlier comments, plus the end-to-end result — posting because it changes who actually hits this. The trigger is not necessarily a server restart. I framed this as "a stateful server restarts and drops its in-memory sessions". Measuring against a real server (Nowledge Mem's local backend) shows that is not the general case:
So the practical trigger is "DSH was left running for >12h without calling that server" — no crash, no restart, nothing visible. That is far easier to hit than a server restart, which means more people are affected than the restart framing suggests. The End-to-end confirmation on a real host. With the patch installed and DSH left running, I invalidated the session the way that server actually does — aged the store entry past
No DSH restart in between. A new session id appearing on its own is decent evidence: only a fresh Two notes that follow from this:
Corrected framing and the harness are in the repo: https://github.com/MaxHou-infinity/dsh-mcp-session-recovery |
|
Proposing we consolidate into one implementation. Two independent patches for the same gap make the first question "which one" rather than "should this be fixed", and that is time better spent on the fix itself. @gkinter — your design is the more complete base and I would rather not see it split. The 400 arm, retry-once semantics, concurrent-failure recycle sharing and bounded wait are all things mine does not do. Mine is deliberately narrower (a 404, or a 400 whose body names the session → the existing What I would rather contribute than compete with: An A/B harness. The corrected trigger, which widens the scope. Sessions are not necessarily dropped on restart: the server I measured persists them and survives restarts intact, but prunes them on a Production confirmation. On a real host, invalidating a session the way that server actually does: the first call 404s, a new Offered as: pick whichever base is easier to port into |
Uh oh!
There was an error while loading. Please reload this page.
Symptom
After a stateful streamable-HTTP MCP server restarts, every tool call from a DSH instance that was connected before the restart fails until the DSH instance itself restarts:
Cause
The restarted server does not know the client's
mcp-session-id. The MCP spec answers with HTTP 404 and requires the client to start a new session; common servers answer 400 "No valid session ID provided". Neither closes the client transport: the SDK throws a generic non-ok-POST error for the request and reports the dead GET stream throughonerror.dsh-mcp-clientreconnects only fromgeneration.onclose, so no reconnect ever runs and every tool closure stays bound to the dead generation.Proposal
Detect session loss narrowly (HTTP 404 when the request carried a session id, or a 400 whose body says the session id is missing, invalid, unknown or expired; any other 400 is the request's own fault and surfaces unchanged). On a hit, close the current generation through the supervisor's own close so the existing onclose to reconnect path builds the next one, wait (bounded) for the next connected and re-synced generation, and retry the failed request once. Concurrent failures share one recycle, aborts are never retried, and the final error names the lost session and the reconnect outcome.
We run this as an out-of-tree patch against the built
lib/index.jswith a pure detector plus a test harness around the supervisor; happy to port it topackages/mcp/mcp-client/srcwith specs in your style if the design is acceptable. I have not opened a branch yet because it touches the connection supervisor and I would like to confirm the direction first.All reactions