Skip to content

mcpx: Phase 2 of #1634 — re-issue a broken modern request once (tools/call gated on annotations) #1638

Closed

Description

@dezren39

Phase 2 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. Independent of Phase 1 (different era, different trigger) — either may land first.

Scope

Re-issue a modern (2026-07-28) request once when its response stream breaks — the 2026 spec's client MUST — without ever silently double-executing a tool call.

  • Give the stream-lost error a stable, checkable identity. Today lost() (internal/mcpclient/http.go ~L378-389) synthesises code -32000 (codeStreamLost), which the receive loop's "connection closed" failure shares; matching on message text is not acceptable. A marker in the error's data or an equivalent in-package sentinel — triage settles the shape.
  • Re-issue at the request layer (Client.roundTrip, internal/mcpclient/client.go ~L953), so each MRTR input_required round is its own request: same method and params, once only, with a NEW request id. No re-issue when the caller's context is done. A second stream-lost surfaces the error.
  • tools/call gate (decision recorded on Design: mcpx client session & stream recovery — re-initialize on a legacy session 404, re-issue a broken modern request once #1634, from PM-06's own hazard note): every other method (lists, reads, ping/server/discover, completions, prompt gets — reads by protocol nature) is re-issued automatically. tools/call is re-issued automatically only when the tool's annotations declare readOnlyHint or idempotentHint; otherwise the stream-lost error surfaces unchanged and the host decides. Annotations are retained at list time for call time — Tool.Annotations (client.go ~L159-170) carries them raw, and the client's per-tool toolHeaders retention (modern.go) is the precedent.
  • Legacy era is excluded: legacy's broken-stream mechanism is resumability (resume(), http.go ~L338); when resumption is exhausted the error surfaces, as today.
  • Retire conformance gap streamable-http-client-reissue-broken-request (internal/conformance/coverage_transport.go).

Evidence (main ed8e591, 2026-10-28)

Acceptance

  • Fake modern upstream that kills the response stream: a list call succeeds after exactly one re-issue, and the two attempts carried distinct request ids.
  • A tools/call against a tool annotated readOnlyHint (and one annotated idempotentHint) is re-issued once and its result returned.
  • A tools/call against an unannotated or destructive-annotated tool surfaces the stream-lost error, and the upstream observed exactly one execution.
  • A stream that breaks on the re-issue as well → the error surfaces; no third attempt.
  • Caller-cancelled context → no re-issue.
  • Legacy broken-stream behaviour unchanged (resume path untouched).
  • Gap streamable-http-client-reissue-broken-request retired; go test ./... for pkgs/mcpx passes in CI.

Out of scope

Legacy session-404 recovery (Phase 1), register truth-up (Phase 3), a configurable re-issue count (open question on #1634 — one is the recorded default), re-issuing server-initiated requests from a dead stream.

Activity

  1. added
    kind/featureNew feature or capability request
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)
    on Oct 28, 2026
  2. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  3. 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
  4. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done (triaged)

    • Agent: queue worker (repo-worker-code), Lane B
    • Session / ID: queue task 1771 (triage)
    • Found via: queue claim
    • Task: Triage verdict — scope CONFIRMED against current main (ed8e5910); both shapes settled; implementation packet enqueued as queue task 1778. Phase 2 is independently landable (modern-era only; no Phase 1 / mcpx: Phase 1 of #1634 — re-initialize on a legacy session 404 and retry the request once #1637 dependency).
    • Stream-lost identity — settled: a data marker, never message matching. lost() (internal/mcpclient/http.go:378) synthesises codeStreamLost (-32000, const :388) with no marker today, while the receive loop (client.go:870) synthesises its own -32000 "connection closed" — same code, different meaning (transport death; re-issuing into it is wrong), and a server can send -32000 too. Task 1778: lost() adds data: {"mcpxStreamLost": true}; only marked errors trigger re-issue.
    • Annotation retention — settled: the toolHeaders precedent, minus its gate. A per-Client hints map parsed from Tool.Annotations (kept raw today, client.go ~:159) at list time under c.mu, refreshed on ListTools — following checkToolHeaders (modern.go:499) in shape, but NOT gated on mirrorsHeaders(), since the tools/call gate must work whether header mirroring is on or not. Coordination note in the packet: queue task 1641 (mcpx: Phase 2 of #1554 — client task execution for taskSupport "required" tools (internal/mcpclient) #1556) is adding a per-Client cached tool lookup in the same CallTool/ListTools area — one shared cached snapshot beats two.
    • Re-issue placement: the attempt loop belongs in roundTrip (client.go:953 — single-shot today; fresh id per attempt from nextID; each MRTR round is its own roundTrip so rounds re-issue independently; no re-issue when ctx.Err() != nil — the transport already suppresses lost() on cancellation); policy comes from the caller via call/CallTool (:902/:1164): automatic for all methods except tools/call, which requires retained readOnlyHint or idempotentHint. A second marked failure surfaces. The conformance gap (coverage_transport.go:114) is retired in the same PR.
    • Links: follow-up packet hidden_files/packets/task1771-followup-1638.json (goal workspace) · design Design: mcpx client session & stream recovery — re-initialize on a legacy session 404, re-issue a broken modern request once #1634
  5. 86 remaining items

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

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  8. 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
  9. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  10. added a commit that references this issue on Oct 28, 2026
  11. 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