@@ -17,7 +17,13 @@ This repository contains the following crates:
1717- [ rmcp] ( crates/rmcp ) : The core crate providing the RMCP protocol implementation - see [ rmcp] ( crates/rmcp/README.md )
1818- [ rmcp-macros] ( crates/rmcp-macros ) : A procedural macro crate for generating RMCP tool implementations - see [ rmcp-macros] ( crates/rmcp-macros/README.md )
1919
20- For the full MCP specification, see [ modelcontextprotocol.io] ( https://modelcontextprotocol.io/specification/2025-11-25 ) .
20+ This SDK tracks the MCP ** ` 2026-07-28 ` ** draft (the current development spec)
21+ while remaining fully compatible with the stable ** ` 2025-11-25 ` ** release and
22+ earlier versions. New ` 2026-07-28 ` features — server discovery & negotiation,
23+ transport-neutral subscriptions, long-running tasks, response caching,
24+ multi-round-trip requests, and standard HTTP routing headers — are documented
25+ below alongside the stable feature set. For the full MCP specification, see
26+ [ modelcontextprotocol.io] ( https://modelcontextprotocol.io/specification/draft ) .
2127
2228## Table of Contents
2329
@@ -31,8 +37,11 @@ For the full MCP specification, see [modelcontextprotocol.io](https://modelconte
3137- [ Completions] ( #completions )
3238- [ Notifications] ( #notifications )
3339- [ Subscriptions] ( #subscriptions )
40+ - [ Multi-Round-Trip Requests] ( #multi-round-trip-requests )
3441- [ Tasks] ( #tasks-long-running-tool-invocations )
3542- [ Caching] ( #caching )
43+ - [ Standard HTTP Headers] ( #standard-http-headers )
44+ - [ Stateless Streamable HTTP] ( #stateless-streamable-http )
3645- [ Examples] ( #examples )
3746- [ OAuth Support] ( #oauth-support )
3847- [ Related Resources] ( #related-resources )
@@ -44,7 +53,7 @@ For the full MCP specification, see [modelcontextprotocol.io](https://modelconte
4453### Import the crate
4554
4655``` toml
47- rmcp = { version = " 0.16 .0" , features = [" server" ] }
56+ rmcp = { version = " 2.2 .0" , features = [" server" ] }
4857# # or dev channel
4958rmcp = { git = " https://github.com/modelcontextprotocol/rust-sdk" , branch = " main" }
5059```
@@ -174,7 +183,7 @@ let quit_reason = server.cancel().await?;
174183
175184Tools let servers expose callable functions to clients. Each tool has a name, description, and a JSON Schema for its parameters. Clients discover tools via ` list_tools ` and invoke them via ` call_tool ` .
176185
177- ** MCP Spec:** [ Tools] ( https://modelcontextprotocol.io/specification/2025-11-25 /server/tools )
186+ ** MCP Spec:** [ Tools] ( https://modelcontextprotocol.io/specification/draft /server/tools )
178187
179188### Server-side
180189
@@ -210,6 +219,11 @@ async fn main() -> anyhow::Result<()> {
210219
211220The generated tool ` inputSchema ` and ` outputSchema ` are derived from the fields of ` T ` . The type name and documentation on ` T ` are ignored; only field names, field types, and field documentation are used.
212221
222+ > ** ` 2026-07-28 ` (SEP-2106):** ` outputSchema ` may now be any JSON Schema type
223+ > (not just ` object ` ), and a tool result's ` structuredContent ` may be any JSON
224+ > value (string, array, number, …) rather than only an object. Existing
225+ > object-typed tools are unaffected.
226+
213227When you need custom server metadata or multiple capabilities (tools + prompts), use explicit ` #[tool_handler] ` :
214228
215229``` rust,ignore
@@ -258,7 +272,7 @@ let result = client.call_tool(CallToolRequestParams::new("add")).await?;
258272
259273Resources let servers expose data (files, database records, API responses) that clients can read. Each resource is identified by a URI and returns content as text or binary (base64-encoded) data. Resource templates allow servers to declare URI patterns with dynamic parameters.
260274
261- ** MCP Spec:** [ Resources] ( https://modelcontextprotocol.io/specification/2025-11-25 /server/resources )
275+ ** MCP Spec:** [ Resources] ( https://modelcontextprotocol.io/specification/draft /server/resources )
262276
263277### Server-side
264278
@@ -393,7 +407,7 @@ impl ClientHandler for MyClient {
393407
394408Prompts are reusable message templates that servers expose to clients. They accept typed arguments and return conversation messages. The ` #[prompt] ` macro handles argument validation and routing automatically.
395409
396- ** MCP Spec:** [ Prompts] ( https://modelcontextprotocol.io/specification/2025-11-25 /server/prompts )
410+ ** MCP Spec:** [ Prompts] ( https://modelcontextprotocol.io/specification/draft /server/prompts )
397411
398412### Server-side
399413
@@ -507,7 +521,7 @@ context.peer.notify_prompt_list_changed().await?;
507521
508522Sampling flips the usual direction: the server asks the client to run an LLM completion. The server sends a ` create_message ` request, the client processes it through its LLM, and returns the result.
509523
510- ** MCP Spec:** [ Sampling] ( https://modelcontextprotocol.io/specification/2025-11-25 /client/sampling )
524+ ** MCP Spec:** [ Sampling] ( https://modelcontextprotocol.io/specification/draft /client/sampling )
511525
512526### Server-side (requesting sampling)
513527
@@ -579,7 +593,7 @@ impl ClientHandler for MyClient {
579593
580594Roots tell servers which directories or projects the client is working in. A root is a URI (typically ` file:// ` ) pointing to a workspace or repository. Servers can query roots to know where to look for files and how to scope their work.
581595
582- ** MCP Spec:** [ Roots] ( https://modelcontextprotocol.io/specification/2025-11-25 /client/roots )
596+ ** MCP Spec:** [ Roots] ( https://modelcontextprotocol.io/specification/draft /client/roots )
583597
584598### Server-side
585599
@@ -644,7 +658,7 @@ client.notify_roots_list_changed().await?;
644658
645659Servers can send structured log messages to clients. The client sets a minimum severity level, and the server sends messages through the peer notification interface.
646660
647- ** MCP Spec:** [ Logging] ( https://modelcontextprotocol.io/specification/2025-11-25 /server/utilities/logging )
661+ ** MCP Spec:** [ Logging] ( https://modelcontextprotocol.io/specification/draft /server/utilities/logging )
648662
649663### Server-side
650664
@@ -717,7 +731,7 @@ client.set_level(SetLevelRequestParams::new(LoggingLevel::Warning)).await?;
717731
718732Completions give auto-completion suggestions for prompt or resource template arguments. As a user fills in arguments, the client can ask the server for suggestions based on what's already been entered.
719733
720- ** MCP Spec:** [ Completions] ( https://modelcontextprotocol.io/specification/2025-11-25 /server/utilities/completion )
734+ ** MCP Spec:** [ Completions] ( https://modelcontextprotocol.io/specification/draft /server/utilities/completion )
721735
722736### Server-side
723737
@@ -799,7 +813,7 @@ let result = client.complete(CompleteRequestParams::new(
799813
800814Notifications are fire-and-forget messages -- no response is expected. They cover progress updates, cancellation, and lifecycle events. Both sides can send and receive them.
801815
802- ** MCP Spec:** [ Notifications] ( https://modelcontextprotocol.io/specification/2025-11-25 /basic/ notifications )
816+ ** MCP Spec:** [ Notifications] ( https://modelcontextprotocol.io/specification/draft /basic# notifications )
803817
804818### Progress notifications
805819
@@ -970,6 +984,81 @@ and [client](examples/clients/src/subscriptions_streamhttp.rs) examples.
970984
971985---
972986
987+ ## Multi-Round-Trip Requests
988+
989+ Protocol ` 2026-07-28 ` adds Multi-Round-Trip Requests (MRTR, SEP-2322): a server
990+ can answer a ` tools/call ` , ` prompts/get ` , or ` resources/read ` with an
991+ ` InputRequiredResult ` instead of a final result, asking the client to fulfill
992+ one or more embedded server requests (elicitation, sampling, or roots) and then
993+ retry. The exchange is stateless — the server carries its progress in an opaque
994+ ` requestState ` that the client echoes back verbatim.
995+
996+ ** MCP Spec:** [ Multiple Round-Trip Requests] ( https://modelcontextprotocol.io/specification/draft/server/tools#multiple-round-trip-requests )
997+
998+ ### Server-side
999+
1000+ Return an ` InputRequiredResult ` via the outcome enum for the method
1001+ (` CallToolResponse ` , ` GetPromptResponse ` , or ` ReadResourceResponse ` ). The SDK
1002+ only forwards it to peers that negotiated ` 2026-07-28 ` or newer — older peers
1003+ get a protocol error instead.
1004+
1005+ ``` rust, ignore
1006+ async fn call_tool(&self, request: CallToolRequestParams, _ctx: RequestContext<RoleServer>)
1007+ -> Result<CallToolResponse, ErrorData>
1008+ {
1009+ match request.request_state {
1010+ // First round: ask the client for input, seal progress into requestState.
1011+ None => {
1012+ let mut input_requests = InputRequests::new();
1013+ input_requests.insert("city".into(), InputRequest::Elicitation(elicit_city()));
1014+ let sealed = self.codec.seal_json(&json!({ "awaiting": "city" }))?;
1015+ Ok(InputRequiredResult::new(Some(input_requests), Some(sealed)).into())
1016+ }
1017+ // Retry round: verify the echoed state, read the responses, finish.
1018+ Some(sealed) => {
1019+ let _state = self.codec.open_json(&sealed)
1020+ .map_err(|_| ErrorData::invalid_params("tampered request state", None))?;
1021+ let city = request.input_responses.as_ref()
1022+ .and_then(|r| r.get("city"));
1023+ Ok(CallToolResult::success(vec![ContentBlock::text("It is sunny.")]).into())
1024+ }
1025+ }
1026+ }
1027+ ```
1028+
1029+ > ** ` requestState ` is untrusted.** The client echoes it back verbatim, so a
1030+ > stateless server that stores meaningful data in it MUST verify integrity
1031+ > first. Enable the ` request-state ` feature and use ` RequestStateCodec ` to seal
1032+ > and open it (HMAC-tagged), or keep state server-side and use ` requestState `
1033+ > only as an opaque handle.
1034+
1035+ ### Client-side
1036+
1037+ The high-level ` call_tool ` , ` get_prompt ` , and ` read_resource ` helpers drive MRTR
1038+ automatically: they fulfill each embedded request through the local
1039+ ` ClientHandler ` and retry, up to ` DEFAULT_MRTR_MAX_ROUNDS ` (10).
1040+
1041+ ``` rust, ignore
1042+ // Auto mode: the SDK fulfills embedded requests and retries for you.
1043+ let result = client.call_tool(CallToolRequestParams::new("weather")).await?;
1044+
1045+ // Choose a custom round cap.
1046+ let result = client
1047+ .call_tool_with_mrtr_max_rounds(CallToolRequestParams::new("weather"), 3)
1048+ .await?;
1049+
1050+ // Manual mode: get the intermediate InputRequiredResult and drive rounds yourself.
1051+ match client.call_tool_once(CallToolRequestParams::new("weather")).await? {
1052+ CallToolResponse::InputRequired(input_required) => { /* fulfill + retry */ }
1053+ CallToolResponse::Complete(result) => { /* done */ }
1054+ _ => {}
1055+ }
1056+ ```
1057+
1058+ ** Example:** [ ` examples/servers/src/mrtr.rs ` ] ( examples/servers/src/mrtr.rs ) (end-to-end server + client)
1059+
1060+ ---
1061+
9731062## Tasks (long-running tool invocations)
9741063
9751064` rmcp ` implements the [ MCP Tasks extension] ( https://modelcontextprotocol.io/extensions/tasks/overview )
@@ -1050,6 +1139,104 @@ peer.clear_response_cache().await;
10501139> last cached response (even if expired) as ` Ok(..) ` instead of an error. Set
10511140> ` with_serve_stale_on_error(false) ` if callers must observe fetch failures.
10521141
1142+ ## Standard HTTP Headers
1143+
1144+ Protocol ` 2026-07-28 ` standardizes a set of Streamable HTTP request headers
1145+ (SEP-2243) so proxies and gateways can route MCP traffic without parsing the
1146+ JSON body: ` Mcp-Method ` , ` Mcp-Name ` , and ` Mcp-Param-* ` . ` rmcp ` emits and
1147+ validates these automatically once a connection negotiates ` 2026-07-28 ` or
1148+ newer — no call-site changes are required, and older negotiated versions are
1149+ untouched.
1150+
1151+ ** MCP Spec:** [ Header standardization] ( https://modelcontextprotocol.io/specification/draft/basic/transports#header )
1152+
1153+ - ` Mcp-Method ` — the JSON-RPC method (e.g. ` tools/call ` ).
1154+ - ` Mcp-Name ` — the target name, sourced from ` params.name ` (` tools/call ` ,
1155+ ` prompts/get ` ), ` params.uri ` (` resources/* ` ), or ` params.taskId ` (` tasks/* ` ).
1156+ - ` Mcp-Param-* ` — selected ` tools/call ` arguments, promoted from the tool's
1157+ input schema.
1158+
1159+ To promote a tool argument into a routing header, annotate the top-level schema
1160+ property with ` x-mcp-header ` :
1161+
1162+ ``` rust, ignore
1163+ // A `region` argument surfaces as the `Mcp-Param-Region` request header.
1164+ let schema = serde_json::json!({
1165+ "type": "object",
1166+ "properties": {
1167+ "region": { "type": "string", "x-mcp-header": "Region" }
1168+ }
1169+ });
1170+ ```
1171+
1172+ Annotations must be non-empty RFC 9110 tokens, case-insensitively unique, and
1173+ applied only to top-level primitive (` string ` /` integer ` /` boolean ` ) properties.
1174+ Values that cannot travel as a bare header (leading/trailing whitespace,
1175+ control/non-ASCII characters) are transparently Base64-wrapped as
1176+ ` =?base64?<b64>?= ` .
1177+
1178+ ---
1179+
1180+ ## Stateless Streamable HTTP
1181+
1182+ Per SEP-2567, ` rmcp ` serves the ` 2026-07-28 ` draft statelessly ** automatically** :
1183+ no ` Mcp-Session-Id ` , no standalone GET/DELETE stream, and no ` Last-Event-ID `
1184+ resumption. The ` legacy_session_mode ` flag below only controls behavior for
1185+ * legacy* protocol versions (` < 2026-07-28 ` ).
1186+
1187+ ** MCP Spec:** [ Transports] ( https://modelcontextprotocol.io/specification/draft/basic/transports )
1188+
1189+ ### Server-side
1190+
1191+ A default server already serves ` 2026-07-28 ` clients statelessly. To also serve
1192+ * legacy* clients without sessions, disable ` legacy_session_mode ` (formerly
1193+ ` stateful_mode ` ; builder ` with_stateful_mode ` ). Optionally set
1194+ ` with_json_response(true) ` so simple request/response tools reply with a single
1195+ ` application/json ` body instead of an SSE stream (the server still falls back to
1196+ ` text/event-stream ` if a handler emits a notification or server request first):
1197+
1198+ ``` rust, ignore
1199+ use rmcp::transport::streamable_http_server::{
1200+ StreamableHttpService, StreamableHttpServerConfig,
1201+ session::local::LocalSessionManager,
1202+ };
1203+
1204+ let config = StreamableHttpServerConfig::default()
1205+ .with_legacy_session_mode(false) // stateless for legacy versions too
1206+ .with_json_response(true); // plain JSON replies for simple tools
1207+
1208+ let service = StreamableHttpService::new(
1209+ || Ok(Counter::new()), // a fresh handler per request
1210+ LocalSessionManager::default().into(),
1211+ config,
1212+ );
1213+
1214+ // `StreamableHttpService` is a Tower service — mount it on any router.
1215+ let router = axum::Router::new().nest_service("/mcp", service);
1216+ ```
1217+
1218+ > Because there is no per-session state, the ` service_factory ` runs per request.
1219+ > Keep shared state (DB pools, caches) in a ` Clone ` handle captured by the
1220+ > closure; don't rely on in-memory state surviving between requests.
1221+
1222+ ### Client-side
1223+
1224+ The Streamable HTTP client transport allows stateless operation by default
1225+ (` allow_stateless: true ` ), so no configuration is needed to talk to a stateless
1226+ server — it simply omits the session header when the server doesn't issue one:
1227+
1228+ ``` rust, ignore
1229+ use rmcp::transport::StreamableHttpClientTransport;
1230+
1231+ // Defaults are stateless-friendly.
1232+ let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp");
1233+ let client = ClientInfo::default().serve(transport).await?;
1234+ ```
1235+
1236+ ** Example:** [ ` examples/servers/src/counter_streamhttp.rs ` ] ( examples/servers/src/counter_streamhttp.rs ) (server), [ ` examples/clients/src/streamable_http.rs ` ] ( examples/clients/src/streamable_http.rs ) (client)
1237+
1238+ ---
1239+
10531240## Examples
10541241
10551242See [ examples] ( examples/README.md ) .
@@ -1060,8 +1247,8 @@ See [Oauth_support](docs/OAUTH_SUPPORT.md) for details.
10601247
10611248## Related Resources
10621249
1063- - [ MCP Specification] ( https://modelcontextprotocol.io/specification/2025-11-25 )
1064- - [ Schema] ( https://github.com/modelcontextprotocol/specification /blob/main/schema/2025-11-25 /schema.ts )
1250+ - [ MCP Specification] ( https://modelcontextprotocol.io/specification/draft )
1251+ - [ Schema] ( https://github.com/modelcontextprotocol/modelcontextprotocol /blob/main/schema/draft /schema.ts )
10651252
10661253## Related Projects
10671254
0 commit comments