Skip to content

design(mcpx): error honesty, finished — peer errors relayed as peer errors, unadvertised methods refused, -32021 when a capability is truly required #2434

Open
1 of 3 issues completed

Description

@dezren39

Design from the design-architect pass 16 (2026-10-29). Full design doc: files/designs/code-mcpx-error-honesty.md in the keep-the-four-repos-healthy goal workspace. Origins: conformance gap errors-local-errors-not-mistaken-for-peer (ex-nix#206) and the accept-unadvertised + 32021-broker-fallback gap keys of #1957 (ex-nix#257 — split out under its own acceptance rule, same pattern as #2192). Audited against origin/main 5a34fa70.

Problem

mcpx sits between two peers and speaks for both, but its errors do not say whose they are:

  1. A peer's failure arrives dressed as mcpx's own. Pass-through relays an upstream JSON-RPC error whole (UpstreamError, internal/mcpserver/capability.go). The gateway path flattens the same failure to text at the daemon /v1 surface, then internal/cli/serve_ask.go (~L225-255) reconstructs it by guessing at the text — with exactly one structured special case (-32021 at serve_ask.go:241). Structured data is destroyed and heuristically rebuilt. Spec (2026-07-28, lowercase should): local errors surfaced in JSON-RPC-shaped structures "should ensure they cannot be mistaken for errors received from the peer."
  2. mcpx answers methods it did not advertise. capabilities() (server.go:2410) declares completions only where the revision defines it — with the comment that completion/complete "is answered for every revision" — so a 2024-11-05 peer is served a method that cannot be advertised to it; resources/subscribe is dispatched unconditionally (server.go:938) even when resources.subscribe was advertised false. The 2026 schema's MethodNotFoundError guidance asks for -32601 for a method "gated behind a server capability the server did not advertise". The Removed() check (server.go:785) is the precedent seam; modern HTTP already pairs -32601 with 404 (modernStatus).
  3. -32021 exists only in islands. Emitted correctly by tasks missingTasks (tasks.go:199) and subscriptions/listen (listen.go:138), relayed in pass-through — but when a gateway call cannot finish without a capability the host never declared, the question waits in the broker even when no broker audience can answer it. ERR-08 (pkgs/mcpx/docs/compare/register/errors.md) + conflict Bump build/buck/preludes/buck2-prelude from 3a02ea3 to 3e4d218 #8 (docs/spec/revision-conflicts.md) already record the resolution: mcpx never requires the capability while a broker audience can answer — the complement (no audience ⇒ required ⇒ -32021) is unimplemented.

Decisions recorded

  1. One structured error carriage through the gateway. The /v1 call reply gains a structured upstream-error field (code/message/data); the gateway relays it verbatim via UpstreamError; text-reconstruction heuristics are deleted wherever the structured field is present. mcpx's OWN errors are minted through one helper that stamps error.data with an origin marker ("mcpx": {"origin": "local"}) — peer errors never gain it, local errors always carry it. That asymmetry is the "cannot be mistaken" mechanism. No new codes, no message prefixes.
  2. Dispatch consults the same map advertisement used. One gate after the Removed() check: refuse -32601 any method whose gating capability is absent/false in the capabilities() map for that peer. Initial gated set is only the two proven disagreements: completion/complete ← completions (pre-completions revisions), resources/subscribe/unsubscribe ← resources.subscribe. Accept-liberally stands for everything else; the gate's table grows by evidence. Tasks keep their own gating.
  3. -32021 exactly when waiting is not an answer. At the ask seam: question needs a capability the peer did not declare AND the broker has no audience that can answer it ⇒ modern peer gets -32021 with data.requiredCapabilities (existing shape; HTTP 400 via modernStatus); legacy keeps existing behaviour. When any audience can answer, the broker fallback is unchanged.

Phases

Open questions stay in the design doc (client-side origin marker vs #1481's choke point; prompts-validate-io deliberately excluded — it neighbours #1481, not this seam; gated-set growth needs evidence; ERR-09/ERR-13 belong to #2209's classification, not absorbed here).

🤖 Design-architect pass 16 — agent note: this issue is the design of record; phases are its sub-issues. Do not re-derive the decisions above in triage; flag genuine blockers here instead.

Acceptance criteria

Activity

  1. added
    priority/normalOrdinary priority (the default)
    app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)
    epicMeta-issue grouping multiple items (org Project Type=Epic)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  3. added
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    on Oct 29, 2026
  4. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 agent-note:v1 — design-architect pass 16 (2026-10-29). Design of record for the error-honesty cluster: origins are the conformance gap errors-local-errors-not-mistaken-for-peer (ex-nix#206) and #1957's accept-unadvertised + 32021-broker-fallback keys, split out under #1957's own acceptance rule. Phases: #2435 (structured carriage + origin marker, pilot task 3646 queued), #2436 (advertised-capability gate, pilot task 3647 queued), #2438 (Phase 3: -32021 when no broker audience can answer + truth-up, gated on #2435, pilot task 3648 queued). All three linked as sub-issues and verified. No user/review: the spec rows and ERR-08/conflict #8 already record the ends; the judgment calls are the architect's, recorded in the design doc.

  5. added
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 30, 2026
  6. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    Intake review (fleet monitor, 2026-10-29 run):

  7. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  8. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: triaged (coverage pass)
    Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 40 (priority/normal). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
    Found via: coverage pass 2026-11-03

  9. added
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  10. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: queue worker (repo-worker-code), lane code-h
    • Session / ID: task 7917
    • Found via: queue claim
    • Task: Triage issue design(mcpx): error honesty, finished — peer errors relayed as peer errors, unadvertised methods refused, -32021 when a capability is truly required #2434 at current main — handed off to the opencode execution lane; the laptop claimant launches the bound opencode session, which drives task 7917 to its terminal state. Claim labels were already in sync on pickup (queue/claimed, work/assigned, status/in-progress).
    • Expectation: effort l; done = triaged at current main with evidence, fix/design path scoped or closed with evidence. Note: task 7917 is at attempt 6 of 6 (final) — the claimant should complete it on this pass.
    • Links: queue task 7917 (repo-health-code-pilot)
  11. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  12. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  13. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: triaged
    task: 7917
    worker: repo-worker-code (OpenChamber scheduled lane 8ac61616-3815-4e7b-b33d-840a3829154a)
    checked: 2026-11-04 at current main origin/main@85c0e0b89
    verdict: STILL LIVE; keep #2434 open under its own acceptance criteria.
    evidence:

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)effort/lLarge: multi-day or open-ended; expect staged check-insepicMeta-issue grouping multiple items (org Project Type=Epic)kind/designA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)source/design-architectFiled by the repo design architect jobtask/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