Skip to content

Commit 0ab53b5

Browse files
committed
docs: reconcile ADRs and compliance docs to implemented 2026 behavior
- ADR-029 §CI surface: replace the never-compilable workspace-wide matrices with the as-built lanes (default-members for 2026; per-crate matrix for 2025 — spec-pinned workspace members trip the ADR's own compile_error! mutex on any --workspace sweep). Revision-log entry records the supersession and the empirical re-confirmation (the enum-union slice's 2025-lane break was caught only by the per-crate matrix). - ADR-025: revision entry recording branch reality — the shim is still consumed by four non-frozen crates and carries an unpublishable 0.4.0 manifest version from the mechanical sweep; the framework-wide cut remains 0.4.0 release-prep work (owner + trigger recorded). - docs/plans/2026-07-28-schema-coverage-matrix.md: STALE banner — superseded by COMPLIANCE.md + the compliance harness as coverage truth. - COMPLIANCE.md: elicitation enum-union note (post-rework shape), extension crate name updated to the spec-neutral turul-mcp-ext-tasks (ADR-028 2026-06-07 amendment).
1 parent 997bd58 commit 0ab53b5

5 files changed

Lines changed: 27 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4444

4545
### Added (2026-06-10)
4646

47+
- **Schema pin re-vendored (content sha256 `1bf94a60…`, fixture pin `1304c8fe`).** One substantive upstream change: `ElicitationCompleteNotificationParams` extracted into a named interface extending `NotificationParams` (surface 159 → 160). The Rust binding already modeled the optional `_meta`, so the previously recorded deviation resolved upstream. ADR-027 revision log + COMPLIANCE.md/schema README hash records updated.
48+
- **Docs/ADR reconciliation to implemented behavior**: ADR-029 §CI surface rewritten to the as-built lanes (the prescribed `cargo test --workspace` matrices never compiled — spec-pinned workspace members trip the ADR's own mutex; per-crate matrix is the operative shape); ADR-025 revision entry recording the shim's branch reality (still consumed by four non-frozen crates; manifest carries an unpublishable 0.4.0 from the version sweep; the framework-wide cut remains 0.4.0 release-prep work); `docs/plans/2026-07-28-schema-coverage-matrix.md` marked STALE/superseded (authoritative coverage = COMPLIANCE.md + the compliance harness); COMPLIANCE.md elicitation-union and extension-crate-name notes refreshed.
4749
- **MRTR (SEP-2322): `InputRequiredResult` production (server) and consumption (client).** Server: a tool returning the new `McpError::InputRequired { input_requests, request_state }` sentinel (2026 protocol crate; NOT a wire error — the only return channel available to tool impls) is converted by the `tools/call` handler into a successful `InputRequiredResult` (`resultType: "input_required"`), after enforcing that every input request targets a capability the client declared in that request's `_meta` `clientCapabilities` — undeclared → `-32003 MissingRequiredClientCapability` at HTTP 400 (the 2026 JSON-framed response path now dispatches inline so the HTTP status can reflect the JSON-RPC outcome; SSE-framed responses inherently stay 200). On the retry leg, `CallToolRequestParams.inputResponses`/`requestState` are surfaced to tools via `SessionContext::input_responses()` / `mrtr_request_state()` (requestState documented as attacker-controlled). Client: `call_tool` surfaces `resultType: "input_required"` as the new `McpClientError::InputRequired` carrying `inputRequests`/`requestState`; the application gathers inputs and retries via `call_tool_with_input_responses(name, args, responses, request_state)` (fresh JSON-RPC id; 2026 connections only). New `ClientConfig.declared_capabilities` (elicitation/sampling/roots, all off by default) feeds both the 2026 per-request `_meta` and the 2025 `initialize` capabilities — previously hardcoded empty. Tests: `mrtr_2026.rs` (real-HTTP two-leg round trip + `-32003`/400 capability rejection; the suite does not compile against the pre-slice tree — the sentinel variant did not exist) and a full client-driven MRTR e2e in `e2e_2026_real_server.rs`. Limitations (tracked): MRTR production is wired for `tools/call` only (`resources/read`/`prompts/get` handlers have no input hooks yet); the framework does not generate or verify `requestState` integrity (HMAC is the tool author's concern).
4850
- **Unknown-method mapping on the 2026 path: HTTP 404 + JSON-RPC `-32601`.** A request for a method the server does not implement now returns `404 Not Found` with a `-32601` body (the body distinguishes this from a legacy HTTP+SSE server's 404), checked pre-dispatch against the dispatcher's registered methods (the streaming architecture commits the status before dispatch completes, so the check cannot ride the dispatch result). Methods absent from the pinned 2026-07-28 schema — `ping`, `initialize`, `tasks/*`, `logging/setLevel`, `resources/subscribe` — are never registered on a 2026 build and land here. The 2025-era sessionless-ping bypass is now `protocol-2025-11-25`-only (it previously let `ping` dodge header validation and answer 200/`-32601` on the 2026 path). Tests: `error_mapping_2026.rs` (3 real-HTTP cases incl. a sweep over the absent methods; failing pre-fix — revert-and-fail evidence), wired into the default CI lane. With this, the 2026 error/status contract is: 401/403 auth (middleware) → `-32004` unsupported version (400) → `-32001` header mismatch (400) → `-32602` missing/incomplete `_meta` (400) → 404/`-32601` unknown method → dispatch.
4951
- **SEP-2243 request-metadata headers enforced (server) and emitted (client) on the 2026 path.** Server (`turul-http-mcp-server`, §Server Validation): every POST must carry `MCP-Protocol-Version` (a 2026-only build supports no pre-2025-06-18 clients, so an absent header is rejected) and `Mcp-Method` matching the body method; `tools/call`/`prompts/get` (`params.name`) and `resources/read` (`params.uri`) additionally require a matching `Mcp-Name`. Failures → HTTP 400 + JSON-RPC `-32001 HeaderMismatch` (id-less for notifications). A requested version this build does not implement → HTTP 400 + `-32004 UnsupportedProtocolVersionError` with `data.supported`/`data.requested` (previously never emitted); the header/body `_meta` protocolVersion disagreement moved from `-32602` to `-32001` (it is a header-validation failure). The 2026 build now routes ALL requests to the streamable handler — a legacy version header can no longer detour into the 2025-era session handler around version validation (`server.rs`). Client (`turul-mcp-client`): 2026 connections mirror `method` into `Mcp-Method` and `params.name`/`params.uri` into `Mcp-Name`; the `server/discover` probe now advertises `MCP-Protocol-Version: 2026-07-28` (header must match its 2026 `_meta` — the old legacy-header probe is rejected by a validating 2026 server) and the fallback arm restores the 2025 header before `initialize`; a 400 whose body is a JSON-RPC error surfaces its code to the negotiation classifier, and `-32004` now triggers the 2025 fallback (structured negotiation signal; bare 4xx still aborts). `headers.rs` (protocol crate) rewritten to the live-draft wire shape: `x-mcp-header` is a schema annotation key, the wire header is `Mcp-Param-{name}` (+ `=?base64?…?=` sentinels), and `-32001` gets a named constant — replacing the incorrect `x-mcp-header-<name>` wire-prefix constant. Tests: `mcp_headers_2026.rs` (9 real-HTTP enforcement cases, 7 failing pre-fix), `e2e_2026_real_server.rs` (bilingual client ↔ real in-process 2026 server: negotiation + tools round-trip, failing pre-fix on the probe header), strengthened wiremock matchers (stubs now require the headers), `-32004` classifier unit test. Existing 2026 suites migrated to send the now-mandatory headers. Not yet done (tracked): `Mcp-Param-*` emission/validation (requires `x-mcp-header` inputSchema scanning), client-side `subscriptions/listen` API.

crates/turul-mcp-protocol-2026-07-28/COMPLIANCE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,10 @@ DRAFT-2026-v1 deprecates **Roots**, **Sampling**, and **Logging** per SEP-2577 (
190190

191191
## Known follow-ups (not blockers for current state)
192192

193+
- **Elicitation enum schemas (2026-06-10)**`EnumSchema` now binds the schema's union (`SingleSelectEnumSchema | MultiSelectEnumSchema | LegacyTitledEnumSchema`); the legacy `enumNames` shape is `LegacyTitledEnumSchema` (with its `default` field). `StringSchema`/`NumberSchema`/`BooleanSchema` and the untitled select shapes carry `deny_unknown_fields` so the untagged `PrimitiveSchemaDefinition` lands every payload on its precise variant instead of silently dropping `enum`/numeric constraints. Round-trip fidelity tests cover the union incl. through `ElicitationSchema.properties`.
193194
- **`ContentBlock` union split** — the schema declares two distinct unions: `ContentBlock = Text | Image | Audio | ResourceLink | EmbeddedResource` (5) and `SamplingMessageContentBlock = Text | Image | Audio | ToolUse | ToolResult` (5). The crate models them as a single 7-variant enum, allowing wire-impossible shapes (a `CallToolResult.content` carrying `ToolUseContent`, etc.). Wire-equivalent for the 3-variant overlap; documented in §Intentional deviations.
194195
- **`ResourceReference``Resource` duplication** — A4 closed the wire-shape gap (size/icons now present on `ResourceReference`), but the two structs remain parallel. Collapsing onto one canonical `Resource` (per schema's `ResourceLink extends Resource`) is the cleaner end-state and queued for a follow-up.
195-
- **`turul-mcp-ext-tasks-2026-07-28` extension crate** — SEP-2663 moved tasks out of the core protocol; the new extension crate is not yet scaffolded (planned per ADR-028). Tasks types are correctly absent from the protocol crate.
196+
- **`turul-mcp-ext-tasks` extension crate** (spec-neutral name per ADR-028's 2026-06-07 amendment) — SEP-2663 moved tasks out of the core protocol; the extension crate is not yet scaffolded. Tasks types are correctly absent from the protocol crate.
196197

197198
## Intentional deviations from strict schema
198199

docs/adr/025-extract-turul-rpc.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,5 @@ The 0.3.39 release passed:
189189
- **2026-05-31**: Cross-reference with ADR-027 §"Status update (2026-05-31)". ADR-025's framework-wide cutover from `turul-mcp-json-rpc-server` shim to direct `turul-rpc` dependency is still scheduled for framework 0.4.0, and that release will also be the DRAFT-2026-v1 default cutover per ADR-027. The two are scoped to the same release for release-engineering convenience, but they are independent decisions — the turul-rpc shim cleanup is orthogonal to MCP spec version. No content change to this ADR.
190190

191191
- **2026-06-07** — **Branch standardized on published `turul-rpc` 0.2 (crates.io); `turul-rpc` 0.1 removed from this branch.** The framework-wide shim→direct cutover this ADR scoped to 0.4.0 landed early at the workspace level: the workspace `turul-rpc` pin moved `0.1 → 0.2`, and `turul-http-mcp-server` was ported to the 0.2 `JsonRpcResponse` Success/Error union (inbound `JsonRpcMessage` is now parse-only). The shim `turul-mcp-json-rpc-server` re-exports 0.2 and remains in the tree only for the frozen `2025-06-18` / `2025-11-25` protocol crates, which compile unchanged against 0.2 (freeze intact). `Cargo.lock` resolves only `turul-rpc 0.2.2`. The only remaining reason to mention `turul-rpc 0.1` is historical: it was maintained for the 0.3 framework, which lives on `main`. Any §Context / §Consequences prose describing a live `0.1 ↔ 0.2` workspace split is superseded by this entry. `cargo build --workspace` clean; http-server 92 unit + 11 doc tests pass; `clippy -D warnings` clean; zero test expectations changed (wire format byte-identical).
192+
193+
- **2026-06-10 (branch reality check)** — On `feat/turul-mcp-protocol-2026-07-28` the shim is still consumed by the non-frozen framework crates (`turul-mcp-server`, `turul-http-mcp-server`, `turul-mcp-builders`, `turul-mcp-client`) and its manifest carries `version = "0.4.0"` from the per-crate-versioning sweep — both at odds with this ADR's "terminal shim release is 0.3.39; framework 0.4.0 drops the dep entirely" decision. Disposition: the decision stands; the framework-wide shim cut is REMAINING 0.4.0 work, not done. The 0.4.0 version string in the shim manifest is an artifact of the mechanical version sweep and must not be published; the publishable outcome remains "no 0.4 of the shim." Owner: the 0.4.0 release-prep slice; trigger: before any `cargo publish` of 0.4.0 crates.

docs/adr/029-spec-coexistence-via-cargo-features.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,26 +134,24 @@ The atomic cutover slice (the "flip" of ADR-027 Phase 9.4) ships:
134134

135135
A separate plan document (`docs/plans/2026-07-28-feature-gating-rollout.md`, to be authored as part of the cutover slice) enumerates the per-crate `#[cfg(feature = "...")]` gates needed for downstream sources to compile under both feature configurations. Initial estimate: ~400–600 `#[cfg]` gates across `turul-mcp-server`, `turul-http-mcp-server`, `turul-mcp-builders`, derive macros, and the example fleet. The plan document is the verification artifact; the CI matrix (two configurations: default and `--no-default-features --features protocol-2025-11-25`) is the gate.
136136

137-
### CI surface
137+
### CI surface (as built — see `.github/workflows/ci.yml` / `scripts/ci-gates.sh`)
138138

139-
CI will run two matrices for each PR touching the protocol surface:
139+
Two lanes per push:
140140

141-
1. **Default (`protocol-2026-07-28`):** `cargo test --workspace`, `cargo check --workspace`, `cargo doc --no-deps --workspace`.
142-
2. **Legacy (`protocol-2025-11-25`):** the legacy feature is declared on every consumer crate (per the §"Cascade rule" above), so the CI command activates each leaf's own forwarding feature, NOT the alias-crate feature directly. The correct invocations are:
141+
1. **Default (`protocol-2026-07-28`):** `cargo build` / `cargo clippy --all-targets -- -D warnings` / `cargo test` over the workspace **default-members** (NOT `--workspace`), plus explicit real-HTTP acceptance suites (`discover_stateless_2026`, `stateless_2026_http_surface`, `subscriptions_listen_2026`, `mcp_headers_2026`, `error_mapping_2026`, `mrtr_2026`), the protocol-crate compliance + upstream-fixture gate, and the bilingual client suite.
142+
2. **Legacy (`protocol-2025-11-25`):** a **per-crate build/test matrix**, never a workspace-wide flag sweep:
143143

144144
```bash
145-
# Workspace-wide: every member crate that has a protocol-2025-11-25 feature
146-
# forwards through to the alias. Cargo unification activates the chain.
147-
cargo test --workspace --no-default-features --features protocol-2025-11-25
148-
149-
# Or target a specific leaf:
150-
cargo test -p turul-mcp-server --no-default-features --features protocol-2025-11-25
151-
cargo test -p turul-mcp-aws-lambda --no-default-features --features protocol-2025-11-25
145+
cargo test -p turul-mcp-server --no-default-features --features http,sse,protocol-2025-11-25
146+
cargo test -p mcp-tools-tests # 2025-pinned e2e crates select their own features
147+
# ... (full list in scripts/ci-gates.sh gate_opt_in_2025)
152148
```
153149

150+
**`cargo test --workspace --no-default-features --features protocol-2025-11-25` does not compile** — and neither does the default-lane `--workspace` form. The workspace contains members hard-pinned to each spec (the 2025-pinned e2e crates; the 2026-only examples), so a workspace-wide build unifies BOTH alias protocol features and trips this ADR's own `compile_error!` mutex. The mutex working as designed is exactly why the per-crate matrix is the only valid CI shape. (Earlier revisions of this section prescribed the workspace-wide commands; that was never executable — superseded 2026-06-10.)
151+
154152
**Do not use** `--features turul-mcp-protocol/protocol-2025-11-25` (the alias-crate feature only). That activates the alias's gate but does NOT activate each consumer crate's own `#[cfg(feature = "protocol-2025-11-25")]` blocks — those gates live in the leaf crate's source and require the leaf crate's feature to be enabled, not just the alias's.
155153

156-
Without the legacy matrix, the legacy feature rots silently (called out in the architecture review at `docs/plans/2026-07-28-architecture-review.md` as a high-severity hidden risk). The matrix doubles CI time on the protocol surface; this is the accepted cost.
154+
Without the legacy matrix, the legacy feature rots silently (called out in the architecture review at `docs/plans/2026-07-28-architecture-review.md` as a high-severity hidden risk; empirically re-confirmed when the elicitation enum-union slice broke the 2025 lane and only the matrix caught it). The matrix roughly doubles CI time on the protocol surface; this is the accepted cost.
157155

158156
## Alternatives considered
159157

@@ -224,3 +222,5 @@ The user-locked steelman from the decision phase (`docs/plans/2026-07-28-archite
224222
6. **Consumer fleet migrated.** 43 examples migrated to the 2026 default; 8 redundant duplicate examples removed (builders-showcase, comprehensive-server, sampling-with-tools-showcase, task-types-showcase, client-task-lifecycle, dynamic-tools-test-client, performance-testing, lambda-mcp-server-streaming); a small 2025-11-25 regression suite pinned (tasks-e2e pair + logging/sampling/elicitation/client/lambda examples held at the 2025 opt-in), and the integration-test crates pinned to the 2025-11-25 opt-in.
225223
Default-members build is green at 0 warnings; the framework `compile_error!` mutex fires correctly under both configurations. Publication to crates.io remains gated per ADR-027 (upstream final-spec publication + maintainer go-ahead).
226224
- **2026-06-08 (doc reconciliation + 2025 regression coverage)** — Current-state prose in this ADR that still named the 2026 spec `DRAFT-2026-v1` (Context status line, the §"Why coexistence" cleavage points, the §Constraints "Server default" line, the §Decision "0.4.0 defaults to…" line, the §Consequences release-notes wording) was reconciled to the finalized wire literal `2026-07-28`. Remaining `DRAFT-2026-v1` mentions are deliberate history: ADR-027's title/subject references and the dated RC-instability rationale. Separately, `roots-server` was found pinned to the 2026 default (its `Root` type resolved to the deprecated 2026 binding), so the 2025 `mcp-roots-tests` e2e suite could not handshake it — all 14 tests failed at server start. Pinning `roots-server` to the 2025 opt-in (matching `sampling-server`/`elicitation-server`) turns the suite green; the roots/sampling/elicitation suites and a `client-initialise-server` build were added to the opt-in-2025 CI lane (they had been absent). `client-initialise-server` itself, an inherently-stateful `initialize`/`Mcp-Session-Id` demo, was moved out of `default-members` and pinned to the 2025 opt-in.
225+
226+
- **2026-06-10** — §"CI surface" rewritten to the as-built lanes. The prescribed `cargo test --workspace [--no-default-features --features protocol-2025-11-25]` commands never compiled: spec-pinned workspace members (2025-pinned e2e crates, 2026 default examples) make any workspace-wide build unify both alias protocol features and trip this ADR's `compile_error!` mutex. The operative CI shape is default-members for the 2026 lane plus a per-crate matrix for the 2025 lane (`.github/workflows/ci.yml`, `scripts/ci-gates.sh`).

docs/plans/2026-07-28-schema-coverage-matrix.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
> **STALE — superseded as a coverage source of truth (2026-06-10).** This walk
2+
> predates the Slice A'/A'' fidelity corrections, the 2026-06-07 and 2026-06-10
3+
> re-vendors, and the elicitation enum-union rework; several rows now
4+
> misdescribe the crate (e.g. CacheableResult optionality, SEP-2577 markers,
5+
> the EnumSchema shape). Authoritative coverage lives in
6+
> `crates/turul-mcp-protocol-2026-07-28/COMPLIANCE.md` plus the compliance
7+
> harness (`cargo test -p turul-mcp-protocol-2026-07-28 --features compliance`).
8+
> Kept for the historical walk methodology only; do not cite rows as current.
9+
110
# Schema Coverage Matrix — `DRAFT-2026-v1` schema.ts ↔ `turul-mcp-protocol-2026-07-28`
211

312
**Source**: `crates/turul-mcp-protocol-2026-07-28/schema/draft-schema.ts` (2983 lines, ETag `8bdd4ae5…`)

0 commit comments

Comments
 (0)