You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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).
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -44,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
44
44
45
45
### Added (2026-06-10)
46
46
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.
47
49
- **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).
48
50
- **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.
49
51
- **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.
Copy file name to clipboardExpand all lines: crates/turul-mcp-protocol-2026-07-28/COMPLIANCE.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -190,9 +190,10 @@ DRAFT-2026-v1 deprecates **Roots**, **Sampling**, and **Logging** per SEP-2577 (
190
190
191
191
## Known follow-ups (not blockers for current state)
192
192
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`.
193
194
-**`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.
194
195
-**`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.
Copy file name to clipboardExpand all lines: docs/adr/025-extract-turul-rpc.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -189,3 +189,5 @@ The 0.3.39 release passed:
189
189
-**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.
190
190
191
191
- **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.
Copy file name to clipboardExpand all lines: docs/adr/029-spec-coexistence-via-cargo-features.md
+12-12Lines changed: 12 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -134,26 +134,24 @@ The atomic cutover slice (the "flip" of ADR-027 Phase 9.4) ships:
134
134
135
135
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.
136
136
137
-
### CI surface
137
+
### CI surface (as built — see `.github/workflows/ci.yml` / `scripts/ci-gates.sh`)
138
138
139
-
CI will run two matrices for each PR touching the protocol surface:
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:
143
143
144
144
```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)
152
148
```
153
149
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
+
154
152
❌ **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.
155
153
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.
157
155
158
156
## Alternatives considered
159
157
@@ -224,3 +222,5 @@ The user-locked steelman from the decision phase (`docs/plans/2026-07-28-archite
224
222
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.
225
223
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).
226
224
- **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`).
0 commit comments