Skip to content

mcpx: Phase 1 of #1634 — re-initialize on a legacy session 404 and retry the request once #1637

Closed

Description

@dezren39

Phase 1 of design #1634 (mcpx client session & stream recovery). Design doc: files/designs/code-mcpx-client-session-stream-recovery.md in the keep-the-four-repos-healthy goal workspace.

Scope

Recover transparently when a legacy HTTP upstream forgets the session (restarts) mid-life: forget the dead session, re-initialize once, retry the request once.

  • Trigger, narrowly: a request (never a notification, never initialize itself) fails with HTTPStatusError 404, the client era is legacy, and the failed request actually carried a session id. Any other 404 behaves exactly as today.
  • internal/mcpclient/http.go: add a small transport seam the client can interface-assert (the interface{ setNegotiated(string) } pattern in client.go): atomically report-and-clear the held session id (working name forgetSession() bool). The fresh initialize must go out with no session id, and no later request may echo the dead one. Stdio transports do not implement the seam; recovery is HTTP-only.
  • internal/mcpclient/client.go: recovery in the request path (roundTrip/call layer) reusing the existing initializeLegacy/finishLegacy unchanged. Re-initialize is singleflight across concurrent calls — a client-level mutex plus a session generation, so N concurrent 404s from one upstream restart produce exactly one initialize; calls whose generation was already replaced just retry on the new session.
  • Retry the original request exactly once through roundTrip (fresh pending entry, new request id). A second 404, or a failed re-initialize, surfaces the error as today. No retry loop.
  • After a successful re-initialize, fire the existing catalogue-invalidation signal (invalidate path → Notifications.OnListChanged) for tools, resources and prompts — a restarted upstream's catalogue is not the cached one. finishLegacy already refreshes ServerInfo/Capabilities and restarts the GET listen stream for ≥2025-03-26.
  • Modern era is excluded: 2026 has no sessions (TR-30); a modern 404 stays an ordinary error.
  • Retire conformance gap streamable-http-client-reinit-on-404 (internal/conformance/coverage_transport.go).

Evidence (main ed8e591, 2026-10-28)

Acceptance

  • Fake legacy HTTP upstream test: session dropped upstream → the call succeeds after exactly one re-initialize, and the retried request carries the new session id.
  • N concurrent calls 404ing from the same restart → exactly one initialize observed upstream.
  • 404 with no session held (stateless legacy server) → error as today, no initialize sent.
  • A second 404 after re-initialize → error surfaces; no further re-initialize for that call.
  • Modern-era 404 → no recovery attempt.
  • Gap streamable-http-client-reinit-on-404 retired; go test ./... for pkgs/mcpx passes in CI.

Out of scope

Modern broken-stream re-issue (Phase 2), register truth-up (Phase 3), persisting the new session into #1414's store (open question on #1634), notification-triggered lazy re-initialize (open question on #1634).

Activity

  1. added
    kind/featureNew feature or capability request
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    on Oct 28, 2026
  2. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  3. added
    queue/queuedHas a queued task in the Supabase agent queue
    app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)
    on Oct 28, 2026
  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 28, 2026
  5. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — triage verdict: CONFIRMED, scope as-built verified (queue task 1770, lane A, 2026-10-28)

    Verified fresh on main ed8e591 (laptop primary clone, matching the issue's evidence base):

    Shapes settled by this triage:

    • Seam: forgetSession() bool on HTTPTransport — atomically reports whether a session id was held and clears it (plus the negotiated state tied to it), so the fresh initialize goes out with no session id and no later request echoes the dead one.
    • Generation: a client-side uint64 counter incremented on each successful legacy initialize; a call captures the generation before its request, and on a qualifying 404 takes the recovery mutex — if the generation has already advanced, it skips re-initialize and just retries once on the new session; otherwise it forgets, re-initializes (advancing the generation), invalidates tools/resources/prompts, and retries once.
    • Trigger stays exactly as scoped: request only (not notification, not initialize), HTTPStatusError 404, legacy era, and the failed request carried a session id.

    Follow-up: implementation packet enqueued as a separate queue task (small_fix) with #1637's acceptance criteria intact. This triage does not implement.

  6. 137 remaining items

  7. added
    status/parkedDeliberately paused with a recorded resume condition
    on Oct 28, 2026
  8. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  9. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 28, 2026
  10. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  11. added a commit that references this issue on Oct 28, 2026
  12. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

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

Metadata

Metadata

Assignees

Labels

app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)kind/featureNew feature or capability requestpriority/normalOrdinary priority (the default)source/design-architectFiled by the repo design architect jobstatus:donetask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions