This release includes 1 security fix for security teams reviewing exposed deployments.
Topics
Affected surfaces
Summary
AI summaryAdded MCP 2025-11-25 streamable HTTP transport fully compliant with the spec.
Full changelog
[3.0.13] - 2026-04-14
Added
-
MCP 2025-11-25 streamable HTTP transport, full lifecycle —
crates/turbomcp-server/src/transport/http.rsnow implements the complete spec shape:POST /+POST /mcpfor JSON-RPC requests,GET /+GET /mcp+GET /ssefor Server-Sent Events, andDELETE /+DELETE /mcpfor explicit session termination.handle_json_rpcemits202 Acceptedfor client JSON-RPC responses and notifications per §4, validates theMCP-Protocol-Versionheader against the per-session negotiated version, rejectsinitializerequests that already carry a session id with400, and returns404for terminated sessions. A newbuild_router()helper backs both the standalonehttp::run{,_with_config}entry points andServerBuilder::into_axum_router/into_service, so BYO-Axum deployments pick up the same spec coverage. -
JSON Schema draft-2020-12 support in
ToolInputSchema/ToolOutputSchema— Acrossturbomcp-types,turbomcp-core, andturbomcp-protocol,schema_typeis nowOption<Value>(accepts"object"or["object", "null"]),additional_propertiesis nowOption<Value>(acceptsfalseor a sub-schema like{"type": "string"}), and a new#[serde(flatten)] extra_keywords: HashMap<String, Value>field preserves arbitrary JSON Schema keywords (oneOf,$schema,$defs,description, ...) losslessly through round-trip. Covered bytest_tool_schema_preserves_arbitrary_json_schema_keywords. Macros (turbomcp-macros/src/tool.rs),turbomcp-openapihandler,turbomcp-wasmregistrations,turbomcp-server/examples/manual_server.rs, andturbomcp-protocoltest helpers all updated to the new shape. -
HTTP origin validation — New
OriginValidationConfigstruct onturbomcp-server::configwithallowed_origins: HashSet<String>,allow_localhost: bool(defaulttrue), andallow_any: bool(defaultfalse). Exposed onServerConfigBuilder(origin_validation,allow_origin,allow_origins,allow_localhost_origins,allow_any_origin) and onServerBuilder(with_origin_validation,with_allowed_origin,allow_localhost_origins,allow_any_origin). The HTTP transport threads the config into every POST / GET / DELETE handler viavalidate_origin, which also extracts the client IP fromConnectInfoor forwarded headers. Covers the MCP "Servers MUST validate the Origin header" DNS-rebinding mitigation. -
Request ID deduplication per session — New shared
InitializedSessionStateinturbomcp-server/src/transport/mod.rstracksseen_request_ids: HashSet<String>, enforcing the MCP spec requirement that "The request ID MUST NOT have been previously used by the requestor within the same session." HTTP, channel, line (stdio/tcp/unix), and WebSocket transports all wire duplicate requests to a-32600 Invalid Requesterror. Notifications (id=None) bypass the dedup check. -
Channel transport initialization lifecycle —
turbomcp-server/src/transport/channel.rsnow enforces the sameSessionState/InitializedSessionStatelifecycle already present in line and WebSocket transports: rejects non-lifecycle requests beforeinitialize, rejects duplicateinitialize, and routes post-init requests throughroute_request_versionedwith the negotiatedProtocolVersion. -
SSE primer event for resumability —
handle_ssenow yields an initialEvent::default().id("<session>-0").data("")before draining the per-subscriber channel, satisfying the MCP spec's "server SHOULD immediately send an SSE event consisting of an event ID and an empty data field in order to prime the client to reconnect (using that event ID as Last-Event-ID)." -
RESERVED_METHOD_NAMEvalidation error —ProtocolValidator::validate_requestandvalidate_notificationnow emit the dedicatedRESERVED_METHOD_NAMEerror code for methods starting withrpc., per JSON-RPC 2.0 §6 ("Method names that begin with the word rpc followed by a period character are reserved for rpc-internal methods and extensions"). Both request and notification paths covered by new tests. -
Comprehensive HTTP spec-compliance integration tests — New
crates/turbomcp-server/tests/http_transport_spec.rscovers session-id handshake,notifications/initialized→202, client JSON-RPC response POST →202, GET/DELETE session termination on the same endpoint, untrusted origin rejection, configured origin allowance, duplicate request-id rejection,413 Payload Too Largefor oversized bodies (raw-TCP test, race-free), and SSE primer event emission. Nine tests, all green. -
Channel transport duplicate request-id and silent-notification tests (
test_channel_transport_rejects_duplicate_request_ids,test_channel_transport_silent_on_notification_before_init) plus line transport silent-notification test (test_line_transport_silent_on_notification_before_init).
Changed
-
SessionManagerroutes SSE messages to a single subscriber per session — Previously backed bytokio::sync::broadcast, which delivered every outbound message to every active subscriber for a session. That violated MCP Streamable HTTP §Multiple Connections ("The server MUST send each of its JSON-RPC messages on only one of the connected streams; that is, it MUST NOT broadcast the same message across multiple streams").SessionDatanow carriessubscribers: Vec<mpsc::UnboundedSender<String>>, andsend_to_session/broadcastroute each message to exactly one live subscriber per session, dropping dead senders as they go.subscribe_sessionreturns a dedicatedmpsc::UnboundedReceiver<String>. Verified by the newsend_to_session_routes_to_single_subscriberunit test. -
ServerBuilder::into_axum_router/into_servicedelegate totransport::http::build_router— Removed 180+ lines of duplicatedhandle_json_rpclogic fromcrates/turbomcp-server/src/builder.rs. BYO-Axum integrations now automatically inherit the full streamable HTTP spec coverage (session management, origin validation, request-id dedup, 413/413 body limits, SSE primer) rather than running a reduced fork. -
Protocol method-name regex relaxed — From
^[a-zA-Z][a-zA-Z0-9_/]*$to^[^\s\x00-\x1F]+$, matching MCP's "just a string" rule and allowing extension-friendly names likenamespace.v1/tool-name. The dedicatedRESERVED_METHOD_NAMEcheck still catchesrpc.*. -
HTTP client treats
405 Method Not Allowedon GET/sseas "no standalone SSE" —crates/turbomcp-http/src/transport.rsSSE connection task now logs and breaks its reconnection loop when the server replies 405, matching MCP spec-compliant servers that do not offer a standalone SSE stream. -
Oversized HTTP bodies return
413 Payload Too Large—build_routernow adds atower_http::limit::RequestBodyLimitLayerat the middleware layer, andhandle_json_rpcalso performs an early Content-Length check and inspects theto_byteserror chain forhttp_body_util::LengthLimitErroras a fallback. Previously mapped to400 Bad Request.
Fixed
-
JSON-RPC 2.0 §4.1: transports no longer respond to notifications with errors — Line (stdio/tcp/unix), channel, and WebSocket transports previously emitted a JSON-RPC error back to the peer when a notification was rejected (uninitialized session, duplicate request id, ...). Per spec, "Notifications are not confirmable by definition, since they do not have a Response object to be returned." The rejection paths now gate on
request.id.is_some()for line and channel transports and useJsonRpcOutgoing::notification_ack()(dropped byshould_send) for WebSocket, leaving the wire silent for notifications. -
turbomcp-wasm wasm_server/server.rscompile failure under--features wasm-server— TwoToolregistrations intool()/tool_with_ctx()initializedextra_keywords: std::collections::HashMap::new(), but the underlyingturbomcp-core::types::tools::ToolInputSchema::extra_keywordsfield is typedhashbrown::HashMap<String, Value>(because the crate isno_std + alloc). Replaced withHashMap::new()so the already-importedhashbrownalias applies. Caught bycargo check --workspace --all-features. -
turbomcp-proxy::proxy::backend::convert_toolscompile failure — The introspectionToolSpec'sToolInputSchemacarries a plainschema_type: Stringand flattenedadditional: HashMap<String, Value>, whileturbomcp-protocol::types::tools::ToolInputSchemamigrated toOption<Value>for bothschema_typeandadditional_properties.convert_toolsnow extracts the schema-type string with an"object"fallback, copiesadditional_propertiesstraight intoadditional, and propagatesextra_keywordsso arbitrary JSON Schema keywords survive proxy introspection.
Full Changelog: https://github.com/Epistates/turbomcp/compare/v3.0.12...v3.0.13
Security Fixes
- Origin header validation prevents DNS rebinding attacks per MCP spec
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
Related context
Beta — feedback welcome: [email protected]