Repository navigation
Description
Phase 2 of #1753 — client-side sampling enforcement
Design: #1753 (mcpx sub-capability gating). Design doc: files/designs/code-mcpx-subcapability-gating.md in the keep-the-four-repos-healthy goal workspace. Origin: dezren39/nix#210. Independent of Phase 1 (different package, different boundary).
Problem (verified on main f6760a69)
Client.answer (pkgs/mcpx/internal/mcpclient/modern.go ~L277-296) refuses an elicitation whose mode was not declared, with -32602 ("Answering anyway would teach the server that undeclared works"). The adjacent sampling/createMessage case has no equivalent check: mcpx declares sampling as a bare {} (capabilities() ~L85-87 — no tools, no context), yet an upstream request carrying tools/toolChoice or includeContext is handed to the handler untouched. Conformance gap (internal/conformance/coverage_clientfeatures.go, tagged #210): sampling-client-error-on-tools-without-capability. Register SMP-03 records the spec's asymmetry: the schema gate says the client MUST error on tools it did not declare.
Scope (decisions recorded on #1753)
In pkgs/mcpx/internal/mcpclient, beside elicitModeDeclared:
- A sampling shape check runs in
Client.answerbefore the handler is invoked (shared by both eras, asansweralready is). toolsortoolChoicepresent and nosampling.toolsdeclared (today: always — the declaration is bare{}) → refuse with-32602naming the undeclared sub-capability, mirroring the elicitation-mode refusal's shape and wording style. The handler is never invoked.includeContextpresent and nosampling.contextdeclared → drop the field, then proceed to the handler (context inclusion is expressly ignorable — conformance overridesampling-client-may-ignore-includecontext; dropping preserves the plain completion the upstream asked for).- Do NOT start declaring
sampling.toolsorsampling.contextupstream: the broker answers plain messages, and declaring more would recreate the original lie in the other direction.
Out of scope
- Server-side relay gating in
internal/mcpserver(Phase 1 of mcpx: design — sub-capability gating (gate on sampling.tools / sampling.context / elicitation.form, not parent presence) #1753). - Elicitation form gate and register truth-up (Phase 3 of mcpx: design — sub-capability gating (gate on sampling.tools / sampling.context / elicitation.form, not parent presence) #1753).
- Sampling result validation / review UI / broader error mapping (mcpx: sampling relay -- human review, validation of results, error mapping (8 conformance rows) dezren39/darwin-nix#256 — separate design space).
- Implementing a sampling tool loop anywhere.
Acceptance criteria
- Tools-carrying upstream sampling request is refused with
-32602namingsampling.tools, and the handler is never invoked — in both eras (legacy wire request and moderninput_required, sinceansweris shared). - Context-carrying request is answered with
includeContextremoved before the handler sees it. - Plain sampling request reaches the handler untouched; existing elicitation-mode tests still pass.
- Conformance gap
sampling-client-error-on-tools-without-capabilityretired fromcoverage_clientfeatures.gowith a passing conformance row. - CI green (GitHub Actions; per fleet rule, no builds on the sandbox).
Activity
developing-today commentedon Oct 28, 2026
CODE-711
dezren39 commentedon Oct 28, 2026
🤖 Agent note · filed
internal/mcpclient(independent of Phase 1). Triage packet enqueued directly to therepo-health-code-pilotqueue as task 1979 (verified: queue=repo-health-code-pilot, status=queued, last_error=null). Packet:hidden_files/queue-packets/packet-1756.jsonin the keep-the-four-repos-healthy goal workspace.-32602namingsampling.tools(handler never invoked, both eras),includeContextdropped whensampling.contextundeclared, gapsampling-client-error-on-tools-without-capabilityretired.files/designs/code-mcpx-subcapability-gating.md; origin mcpx: capabilities declared to upstreams that nothing answers, and host gates that ignore sub-capabilities dezren39/darwin-nix#21061 remaining items
dezren39 commentedon Oct 29, 2026
🤖 Agent note · parked
runandcompare with the records— are still QUEUED after a 5-minute poll (runner congestion; zero failures anywhere). Re-parked for 20 minutes per the park-don't-wait rule.queue/2000-1756-sampling-client-enforcement@ 7d0e39edezren39 commentedon Oct 29, 2026
🤖 Agent note · picked up
7d0e39ef(full test suite, all conformance client/server rows, spec parity, treefmt, Nix flake evaluation, andcompare with the records— the two that were queued at the last park have both passed). The only non-pass is therunjob of the Auto Assign workflow, queued since 2026-10-29T01:38Z — an unrelated assignment automation stuck in runner congestion, not a code check. Merging under the standing merge directive.Merge pull request #2081 from developing-today/queue/2000-1756-sampli…
dezren39 commentedon Oct 29, 2026
🤖 Agent note · done
82b019a06217459c11418ff922d261ccb2fd1f89; this issue closed by the merge. Fresh CI at head7d0e39ef: 15 checks pass (full test suite, all conformance client/server rows, spec parity, treefmt, Nix flake evaluation, compare with the records); the only non-pass was the Auto Assign workflow'srunjob, queued since 01:38Z (unrelated runner congestion). Task 2000 completed asfixed_merged.