Skip to content

Commit 0564397

Browse files
committed
feat: transport deprecation markers — SEP-2596 + 2026-lane enable_get_sse
- Client SseTransport (HTTP+SSE, protocol ≤ 2024-11-05) now carries #[deprecated]: the transport is deprecated upstream (SEP-2596, 2025-03-26 — "new implementations SHOULD NOT adopt it; existing implementations SHOULD migrate to Streamable HTTP"). Crate docs and README carry migration notes; the type stays functional for unmigrated servers (factory/fallback sites carry scoped allows). - The server's legacy session_handler module documents the same SEP-2596 status (it serves ≤ 2024-11-05 clients on the 2025 lane only; the 2026 lane never routes there). - ServerConfig.enable_get_sse and HttpMcpServerBuilder::get_sse() are deprecated on the 2026-07-28 lane only (cfg_attr): the stateless endpoint is POST-only (GET = 405) and the long-lived notification stream is subscriptions/listen. No warning on the protocol-2025-11-25 opt-in, where stateful GET SSE remains first-class. Internal 2025-lane plumbing carries scoped allows. Gates: scripts/ci-gates.sh all → ALL GATES PASSED. Closes spec-compliance driver gap DEP-GAP-1 (P2).
1 parent 982667f commit 0564397

11 files changed

Lines changed: 77 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
2121
### Added
2222

23+
- **Transport deprecation markers (2026-06-11, SEP-2596 + 2026 lane).** The client's `SseTransport` (HTTP+SSE, ≤ 2024-11-05) now carries `#[deprecated]` with migration notes in the crate docs and README — the transport is deprecated upstream (SEP-2596, 2025-03-26: "new implementations SHOULD NOT adopt it"); it remains functional for unmigrated servers. The server's legacy `session_handler` module documents the same. `ServerConfig.enable_get_sse` and the `get_sse()` builder setter are deprecated on the 2026-07-28 lane only (`cfg_attr`): the stateless endpoint is POST-only (GET = 405) and the long-lived stream is `subscriptions/listen`; stateful GET SSE remains first-class on the `protocol-2025-11-25` opt-in. Closes spec-compliance driver gap **DEP-GAP-1 (P2)**.
2324
- **Client disconnect now cancels the in-flight request (2026-06-11).** Streamable HTTP §Cancellation: "Closing the SSE response stream MUST be treated by the server as cancellation of that request. The server SHOULD stop work … and MUST NOT send any further messages for it." The streaming dispatch task previously ran detached to completion after a disconnect; it now races the dispatch future against the response channel's `closed()` signal — on disconnect the future is dropped (the handler stops at its next await point), the progress task is shut down, and nothing further is sent. Wire test: a slow tool's completion flag stays unset when the client drops mid-execution (`cancellation_2026.rs`; control test pins the connected path). Closes spec-compliance driver gaps **PAT/G1 + TX/GAP-2 (both P1)** — the final open P1s.
2425
- **Request-scoped progress on the 2026 path (2026-06-11).** Tools and resources can now emit spec-compliant `notifications/progress`: the request's `_meta.progressToken` is surfaced through the session extensions (`SessionContext::progress_token()`), and the new `notify_request_progress(progress, total)` references exactly that token — no-op when the request declared none ("Progress notifications MUST only reference tokens that were provided in an active request"). Numeric tokens now round-trip as JSON numbers end-to-end; the session→StreamManager bridge previously dropped non-string tokens (`as_str()`) and stringified the rest. Wire tests in `progress_2026.rs` (string echo, numeric round-trip, no-token-no-notifications); revert-and-fail recorded. Closes spec-compliance driver gap **PAT/G2 (P1)**.
2526
- **Real-HTTP OAuth acceptance on the 2026 default transport (2026-06-11, tests + manifest).** New `crates/turul-mcp-server/tests/oauth_2026.rs`: missing/garbage bearers → 401 with the RFC 9728 `WWW-Authenticate` challenge (`resource_metadata=`, `error="invalid_token"`), 401 outranks the missing-`_meta` 400 (auth before validation), and both RFC 9728 well-known routes (root + path form) serve the metadata unauthenticated — all through Builder → `server.run()` → wire. To ride `turul-mcp-server`'s dev-deps without tripping the ADR-029 spec mutex, `turul-mcp-oauth` is now spec-neutral: its transport/storage deps drop default features and it gains its own `protocol-2025-11-25`/`protocol-2026-07-28` forwarding features (default 2026 standalone; unification supplies the spec when used with `default-features = false`). Closes spec-compliance driver gap **AUTH-1 (P1)**.

crates/turul-http-mcp-server/src/server.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,19 @@ pub struct ServerConfig {
3232
pub enable_cors: bool,
3333
/// Maximum request body size
3434
pub max_body_size: usize,
35-
/// Enable GET SSE support (persistent event streams)
35+
/// Enable GET SSE support (persistent event streams).
36+
///
37+
/// 2025-11-25 stateful lane only. On the 2026-07-28 stateless lane this
38+
/// flag has no effect: the MCP endpoint is POST-only (GET returns 405
39+
/// Method Not Allowed) and the long-lived notification stream is the
40+
/// `subscriptions/listen` POST stream instead.
41+
#[cfg_attr(
42+
feature = "protocol-2026-07-28",
43+
deprecated(
44+
since = "0.4.0",
45+
note = "no effect on the 2026-07-28 stateless lane (POST-only endpoint; GET = 405) — use subscriptions/listen for the long-lived stream; stateful GET SSE remains on the protocol-2025-11-25 opt-in"
46+
)
47+
)]
3648
pub enable_get_sse: bool,
3749
/// Enable POST SSE support (streaming tool call responses) - disabled by default for compatibility
3850
pub enable_post_sse: bool,
@@ -54,6 +66,7 @@ pub struct ServerConfig {
5466
}
5567

5668
impl Default for ServerConfig {
69+
#[allow(deprecated)] // enable_get_sse is 2026-lane-deprecated but must default
5770
fn default() -> Self {
5871
Self {
5972
bind_address: "127.0.0.1:8000".parse().unwrap(),
@@ -172,7 +185,17 @@ impl HttpMcpServerBuilder {
172185
self
173186
}
174187

175-
/// Enable or disable GET SSE for persistent event streams
188+
/// Enable or disable GET SSE for persistent event streams.
189+
///
190+
/// 2025-11-25 stateful lane only — see [`ServerConfig::enable_get_sse`].
191+
#[cfg_attr(
192+
feature = "protocol-2026-07-28",
193+
deprecated(
194+
since = "0.4.0",
195+
note = "no effect on the 2026-07-28 stateless lane (POST-only endpoint; GET = 405)"
196+
)
197+
)]
198+
#[allow(deprecated)]
176199
pub fn get_sse(mut self, enable: bool) -> Self {
177200
self.config.enable_get_sse = enable;
178201
self
@@ -184,7 +207,10 @@ impl HttpMcpServerBuilder {
184207
self
185208
}
186209

187-
/// Enable or disable both GET and POST SSE (convenience method)
210+
/// Enable or disable both GET and POST SSE (convenience method).
211+
///
212+
/// The GET half is 2025-11-25-lane-only — see [`ServerConfig::enable_get_sse`].
213+
#[allow(deprecated)]
188214
pub fn sse(mut self, enable: bool) -> Self {
189215
self.config.enable_get_sse = enable;
190216
self.config.enable_post_sse = enable;

crates/turul-http-mcp-server/src/session_handler.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
//! JSON-RPC 2.0 over HTTP handler for MCP requests with SessionStorage integration
22
//!
3+
//! **Deprecated transport (SEP-2596).** This is the legacy HTTP+SSE path
4+
//! (protocol ≤ 2024-11-05), deprecated upstream on 2025-03-26: "new
5+
//! implementations SHOULD NOT adopt it; existing implementations SHOULD
6+
//! migrate to Streamable HTTP." It remains wired for backward compatibility
7+
//! on the 2025-11-25 lane only; the 2026-07-28 lane never routes here.
8+
//!
39
//! This handler implements proper JSON-RPC 2.0 server over HTTP transport with
410
//! MCP 2025-11-25 compliance, including:
511
//! - SessionStorage trait integration (defaults to InMemory)
@@ -850,6 +856,7 @@ impl SessionMcpHandler {
850856
}
851857

852858
// Check if GET SSE is enabled on the server
859+
#[allow(deprecated)] // 2026-lane deprecation; this is the legacy GET SSE path itself
853860
if !self.config.enable_get_sse {
854861
warn!("GET SSE request received but GET SSE is disabled on server");
855862
let error = JsonRpcError::new(

crates/turul-mcp-aws-lambda/src/builder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,9 @@ impl LambdaMcpServerBuilder {
795795
self.enable_sse = enable;
796796

797797
// Update SSE endpoints in ServerConfig based on enable flag
798+
// (enable_get_sse is 2026-lane-deprecated; the setter remains the
799+
// 2025-lane control and a harmless no-op on 2026)
800+
#[allow(deprecated)]
798801
if enable {
799802
self.server_config.enable_get_sse = true;
800803
self.server_config.enable_post_sse = true;
@@ -1661,6 +1664,7 @@ mod tests {
16611664
}
16621665

16631666
#[tokio::test]
1667+
#[allow(deprecated)] // asserts the 2025-lane enable_get_sse plumbing
16641668
async fn test_sse_toggle_functionality() {
16651669
// Test that SSE can be toggled on/off/on correctly
16661670
let mut builder =

crates/turul-mcp-client/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,15 @@ let client = McpClientBuilder::new()
7979
.build();
8080
```
8181

82-
### SSE Transport (HTTP+SSE)
82+
### SSE Transport (HTTP+SSE, deprecated — SEP-2596)
8383

84-
For servers supporting server-sent events:
84+
> **Deprecated upstream** (SEP-2596, 2025-03-26): "new implementations
85+
> SHOULD NOT adopt it; existing implementations SHOULD migrate to
86+
> Streamable HTTP." Use `HttpTransport` unless you must talk to an
87+
> unmigrated ≤ 2024-11-05 server.
8588
8689
```rust
90+
#![allow(deprecated)]
8791
use turul_mcp_client::transport::SseTransport;
8892

8993
let transport = SseTransport::new("http://localhost:8080/mcp")?;
@@ -456,6 +460,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
456460
### Transport Comparison
457461

458462
```rust
463+
#![allow(deprecated)] // SseTransport: HTTP+SSE is deprecated upstream (SEP-2596)
459464
use turul_mcp_client::transport::{HttpTransport, SseTransport, TransportCapabilities};
460465

461466
// Compare transport capabilities

crates/turul-mcp-client/src/client.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,6 +1893,7 @@ impl McpClientBuilder {
18931893
crate::transport::TransportType::Sse => {
18941894
// SSE is a legacy transport — ConnectionConfig not wired (no with_config)
18951895
Box::new(
1896+
#[allow(deprecated)] // ≤2024-11-05 fallback (SEP-2596)
18961897
crate::transport::sse::SseTransport::new(url)
18971898
.expect("URL was validated in with_url() but SSE construction failed"),
18981899
)

crates/turul-mcp-client/src/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,18 @@
7878
//! # }
7979
//! ```
8080
//!
81-
//! ### SSE Transport (HTTP+SSE, legacy)
81+
//! ### SSE Transport (HTTP+SSE, deprecated — SEP-2596)
8282
//!
83-
//! For servers using the pre-2025-03-26 SSE-based protocol. Like the HTTP
84-
//! transport, `connect()` is a no-op marker — the SSE subscription is
85-
//! established lazily during message exchange.
83+
//! For servers using the pre-2025-03-26 SSE-based protocol. The HTTP+SSE
84+
//! transport is deprecated upstream ("new implementations SHOULD NOT adopt
85+
//! it") — use [`HttpTransport`](transport::HttpTransport) unless you must
86+
//! talk to an unmigrated ≤ 2024-11-05 server. Like the HTTP transport,
87+
//! `connect()` is a no-op marker — the SSE subscription is established
88+
//! lazily during message exchange.
8689
//!
8790
//! ```rust,no_run
8891
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
92+
//! #![allow(deprecated)]
8993
//! use turul_mcp_client::transport::SseTransport;
9094
//!
9195
//! let transport = SseTransport::new("http://localhost:8080/mcp")?;

crates/turul-mcp-client/src/transport.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod sse;
1717

1818
// Re-export transport implementations
1919
pub use http::HttpTransport;
20+
#[allow(deprecated)] // re-exported for unmigrated ≤2024-11-05 servers (SEP-2596)
2021
pub use sse::SseTransport;
2122

2223
// Re-exports for future transport implementations
@@ -327,6 +328,7 @@ impl TransportFactory {
327328

328329
match transport_type {
329330
TransportType::Http => Ok(Box::new(HttpTransport::new(url)?)),
331+
#[allow(deprecated)] // factory keeps serving ≤2024-11-05 servers (SEP-2596)
330332
TransportType::Sse => Ok(Box::new(SseTransport::new(url)?)),
331333
}
332334
}
@@ -338,6 +340,7 @@ impl TransportFactory {
338340
) -> McpClientResult<BoxedTransport> {
339341
match transport_type {
340342
TransportType::Http => Ok(Box::new(HttpTransport::new(endpoint)?)),
343+
#[allow(deprecated)] // factory keeps serving ≤2024-11-05 servers (SEP-2596)
341344
TransportType::Sse => Ok(Box::new(SseTransport::new(endpoint)?)),
342345
}
343346
}

crates/turul-mcp-client/src/transport/sse.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@ use crate::transport::{
1717
TransportStatistics, TransportType,
1818
};
1919

20-
/// SSE transport for MCP client (HTTP+SSE 2024-11-05)
20+
/// SSE transport for MCP client (HTTP+SSE, protocol 2024-11-05).
21+
///
22+
/// The HTTP+SSE transport is deprecated upstream (SEP-2596, deprecated
23+
/// 2025-03-26): "new implementations SHOULD NOT adopt it; existing
24+
/// implementations SHOULD migrate to Streamable HTTP." Use
25+
/// [`HttpTransport`](crate::transport::HttpTransport) instead; this type
26+
/// remains for talking to unmigrated ≤ 2024-11-05 servers.
27+
#[deprecated(
28+
since = "0.4.0",
29+
note = "HTTP+SSE transport is deprecated upstream (SEP-2596) — use HttpTransport (Streamable HTTP)"
30+
)]
2131
#[derive(Debug)]
2232
pub struct SseTransport {
2333
/// HTTP client
@@ -40,6 +50,7 @@ pub struct SseTransport {
4050
session_id: parking_lot::Mutex<Option<String>>,
4151
}
4252

53+
#[allow(deprecated)]
4354
impl SseTransport {
4455
/// Create a new SSE transport
4556
pub fn new(endpoint: &str) -> McpClientResult<Self> {
@@ -298,6 +309,7 @@ impl SseTransport {
298309
}
299310

300311
#[async_trait]
312+
#[allow(deprecated)]
301313
impl Transport for SseTransport {
302314
fn transport_type(&self) -> TransportType {
303315
TransportType::Sse
@@ -609,6 +621,7 @@ impl Transport for SseTransport {
609621
}
610622
}
611623

624+
#[allow(deprecated)]
612625
impl Drop for SseTransport {
613626
fn drop(&mut self) {
614627
debug!("🔥 DROP: SseTransport (CLIENT) - SSE transport being cleaned up");

crates/turul-mcp-server/src/server.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ impl McpServer {
471471
// Build HTTP server with shared session storage from SessionManager
472472
let session_storage = self.session_manager.get_storage();
473473
debug!("Configuring HTTP MCP server with session storage backend");
474+
#[allow(deprecated)] // get_sse: 2026-lane-deprecated; pass-through stays for the 2025 lane
474475
let mut builder =
475476
turul_http_mcp_server::HttpMcpServer::builder_with_storage(session_storage)
476477
.bind_address(self.bind_address)
@@ -748,6 +749,7 @@ impl McpServer {
748749
// Build HTTP server with shared session storage from SessionManager
749750
let session_storage = self.session_manager.get_storage();
750751
debug!("Configuring HTTP MCP server with session storage backend");
752+
#[allow(deprecated)] // get_sse: 2026-lane-deprecated; pass-through stays for the 2025 lane
751753
let mut builder =
752754
turul_http_mcp_server::HttpMcpServer::builder_with_storage(session_storage)
753755
.bind_address(self.bind_address)

0 commit comments

Comments
 (0)