Skip to content

Commit 2e6df18

Browse files
authored
feat!: update to rust-mcp-schema 0.10 with BTreeMap for deterministic serialization (#137)
* feat: health check handler * introduce healthcheck support * introduce healthcheck support * chore: clippy * chore: add missing arg when auth is disabled * feat: introduce McpObserver * chore: rename * chore: make McpObserver to be generic * feat: implement example observer * chore: update readme * chore: update readme * chore: clippy & tests * chore: add tests * chore: update dependencies and runner node version * chore: clippy * feat: update to rust-mcp-schema 0.10 with BTreeMap for deterministic serialization
1 parent 58df88f commit 2e6df18

7 files changed

Lines changed: 27 additions & 26 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ rust-mcp-macros = { version = "0.8.1", path = "crates/rust-mcp-macros", default-
1818
rust-mcp-extra = { version="0.1.0", path = "crates/rust-mcp-extra", default-features = false }
1919

2020
# External crates
21-
rust-mcp-schema = { version="0.9", default-features = false }
21+
rust-mcp-schema = { version="0.10", default-features = false }
2222

2323
futures = { version = "0.3" }
2424
tokio = { version = "1.4", features = ["full"] }

crates/rust-mcp-macros/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ pub fn mcp_tool(attributes: TokenStream, input: TokenStream) -> TokenStream {
186186
};
187187

188188
let properties: Option<
189-
std::collections::HashMap<String, serde_json::Map<String, serde_json::Value>>,
189+
std::collections::BTreeMap<String, serde_json::Map<String, serde_json::Value>>,
190190
> = json_schema
191191
.get("properties")
192192
.and_then(|v| v.as_object()) // Safely extract "properties" as an object.
@@ -270,7 +270,7 @@ pub fn mcp_elicit(args: TokenStream, input: TokenStream) -> TokenStream {
270270
}
271271

272272
pub fn from_elicit_result_content(
273-
mut content: Option<std::collections::HashMap<String, #base_crate::ElicitResultContent>>,
273+
mut content: Option<std::collections::BTreeMap<String, #base_crate::ElicitResultContent>>,
274274
) -> Result<Self, #base_crate::RpcError> {
275275
use #base_crate::{ElicitResultContent as V, RpcError};
276276
let mut map = content.take().unwrap_or_default();
@@ -313,7 +313,7 @@ pub fn mcp_elicit(args: TokenStream, input: TokenStream) -> TokenStream {
313313
}
314314

315315
pub fn from_elicit_result_content(
316-
mut content: Option<std::collections::HashMap<String, #base_crate::ElicitResultContent>>,
316+
mut content: Option<std::collections::BTreeMap<String, #base_crate::ElicitResultContent>>,
317317
) -> Result<Self, RpcError> {
318318
use #base_crate::{ElicitResultContent as V, RpcError};
319319
let mut map = content.take().unwrap_or_default();

crates/rust-mcp-macros/tests/test_mcp_elicit.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rust_mcp_schema::{
33
ElicitRequestFormParams, ElicitRequestParams, ElicitRequestUrlParams, ElicitResultContent,
44
RpcError,
55
};
6-
use std::collections::HashMap;
6+
use std::collections::BTreeMap;
77

88
#[test]
99
fn test_form_basic_conversion() {
@@ -16,7 +16,7 @@ fn test_form_basic_conversion() {
1616
pub expertise: Vec<String>,
1717
}
1818
assert_eq!(BasicUser::message(), "Please enter your name and age");
19-
let mut content: std::collections::HashMap<String, ElicitResultContent> = HashMap::new();
19+
let mut content: std::collections::BTreeMap<String, ElicitResultContent> = BTreeMap::new();
2020
content.insert(
2121
"name".to_string(),
2222
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::String(
@@ -67,7 +67,7 @@ fn test_url_basic_conversion() {
6767
"https://github.com/rust-mcp-stack/rust-mcp-sdk"
6868
);
6969

70-
let mut content: std::collections::HashMap<String, ElicitResultContent> = HashMap::new();
70+
let mut content: std::collections::BTreeMap<String, ElicitResultContent> = BTreeMap::new();
7171
content.insert(
7272
"name".to_string(),
7373
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::String(
@@ -109,7 +109,7 @@ fn test_missing_required_field_returns_error() {
109109
pub tags: Vec<String>,
110110
}
111111

112-
let mut content = HashMap::new();
112+
let mut content = BTreeMap::new();
113113
content.insert(
114114
"name".to_string(),
115115
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::String(
@@ -130,7 +130,7 @@ fn test_extra_unknown_field_is_ignored() {
130130
pub name: String,
131131
}
132132

133-
let mut content = HashMap::new();
133+
let mut content = BTreeMap::new();
134134
content.insert(
135135
"name".to_string(),
136136
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::String(
@@ -158,7 +158,7 @@ fn test_type_mismatch_returns_error() {
158158
pub active: bool,
159159
}
160160

161-
let mut content = HashMap::new();
161+
let mut content = BTreeMap::new();
162162
content.insert(
163163
"age".to_string(),
164164
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::String(
@@ -183,7 +183,7 @@ fn test_empty_string_array_when_missing_optional_vec() {
183183
pub hobbies: Option<Vec<String>>,
184184
}
185185

186-
let mut content = HashMap::new();
186+
let mut content = BTreeMap::new();
187187
content.insert(
188188
"name".to_string(),
189189
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::String(
@@ -210,7 +210,7 @@ fn test_empty_content_map_becomes_default_values() {
210210
let result = WithOptionals::from_elicit_result_content(None);
211211
assert!(result.is_err());
212212

213-
let result_empty = WithOptionals::from_elicit_result_content(Some(HashMap::new()));
213+
let result_empty = WithOptionals::from_elicit_result_content(Some(BTreeMap::new()));
214214
assert!(result_empty.is_err());
215215
}
216216

@@ -223,7 +223,7 @@ fn test_boolean_handling() {
223223
pub has_permission: Option<bool>,
224224
}
225225

226-
let mut content = HashMap::new();
226+
let mut content = BTreeMap::new();
227227
content.insert(
228228
"is_active".to_string(),
229229
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::Boolean(
@@ -251,7 +251,7 @@ fn test_numeric_types_variations() {
251251
pub ratio: Option<i32>,
252252
}
253253

254-
let mut content = HashMap::new();
254+
let mut content = BTreeMap::new();
255255
content.insert(
256256
"count".to_string(),
257257
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::Integer(42)),
@@ -298,7 +298,7 @@ fn test_form_and_url_share_same_from_elicit_result_content_logic() {
298298
pub x: String,
299299
}
300300

301-
let mut content = HashMap::new();
301+
let mut content = BTreeMap::new();
302302
content.insert(
303303
"x".to_string(),
304304
ElicitResultContent::Primitive(rust_mcp_schema::ElicitResultContentPrimitive::String(
@@ -321,7 +321,7 @@ fn test_string_array_empty_input_becomes_empty_vec() {
321321
pub items: Vec<String>,
322322
}
323323

324-
let mut content = HashMap::new();
324+
let mut content = BTreeMap::new();
325325
content.insert(
326326
"items".to_string(),
327327
ElicitResultContent::StringArray(vec![]),
@@ -335,7 +335,7 @@ fn test_string_array_empty_input_becomes_empty_vec() {
335335
fn readme_example_elicitation() {
336336
use rust_mcp_macros::{mcp_elicit, JsonSchema};
337337
use rust_mcp_schema::{ElicitRequestParams, ElicitResultContent};
338-
use std::collections::HashMap;
338+
use std::collections::BTreeMap;
339339

340340
#[mcp_elicit(message = "Please enter your info", mode = form)]
341341
#[derive(JsonSchema)]
@@ -356,7 +356,7 @@ fn readme_example_elicitation() {
356356
}
357357

358358
// Simulate user input
359-
let mut content: HashMap<String, ElicitResultContent> = HashMap::new();
359+
let mut content: BTreeMap<String, ElicitResultContent> = BTreeMap::new();
360360
content.insert("name".to_string(), "Alice".into());
361361
content.insert("email".to_string(), "alice@Borderland.com".into());
362362
content.insert("age".to_string(), 25.into());
@@ -389,7 +389,7 @@ fn readme_example_elicitation_url() {
389389
assert_eq!(elicit_url.message, "Complete the form");
390390

391391
// Simulate user input
392-
let mut content: HashMap<String, ElicitResultContent> = HashMap::new();
392+
let mut content: BTreeMap<String, ElicitResultContent> = BTreeMap::new();
393393
content.insert("name".to_string(), "Alice".into());
394394
content.insert("email".to_string(), "alice@Borderland.com".into());
395395
content.insert("age".to_string(), 25.into());

crates/rust-mcp-sdk/tests/common/test_client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rust_mcp_sdk::{
1212
McpClient,
1313
};
1414
use serde_json::json;
15-
use std::{collections::HashMap, sync::Arc};
15+
use std::{collections::BTreeMap, sync::Arc};
1616
use tokio::sync::RwLock;
1717

1818
use crate::common::task_runner::{McpTaskRunner, TaskJobInfo};
@@ -254,7 +254,7 @@ impl ClientHandler for TestClientHandler {
254254
meta: None,
255255
})
256256
.await;
257-
let mut content: HashMap<String, ElicitResultContent> = HashMap::new();
257+
let mut content: BTreeMap<String, ElicitResultContent> = BTreeMap::new();
258258
content.insert(
259259
"content".to_string(),
260260
ElicitResultContentPrimitive::String("hello".to_string()).into(),

crates/rust-mcp-sdk/tests/test_server_task.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rust_mcp_sdk::schema::{
1818
ClientJsonrpcResponse, ResultFromServer, ServerJsonrpcNotification, ServerJsonrpcResponse,
1919
};
2020
use serde_json::json;
21-
use std::{collections::HashMap, panic, sync::Arc, time::Duration};
21+
use std::{collections::BTreeMap, panic, sync::Arc, time::Duration};
2222
use test_streamable_http_server::*;
2323

2424
#[tokio::test]
@@ -215,7 +215,7 @@ async fn test_server_task_wait_for_result() {
215215
.expect("Request failed");
216216
tokio::time::sleep(Duration::from_millis(100)).await;
217217

218-
let mut data: HashMap<String, ElicitResultContent> = HashMap::new();
218+
let mut data: BTreeMap<String, ElicitResultContent> = BTreeMap::new();
219219
data.insert("email".into(), "email@example.com".into());
220220
let elicit_result: ElicitResult = ElicitResult {
221221
action: rust_mcp_schema::ElicitResultAction::Accept,

crates/rust-mcp-sdk/tests/test_streamable_http_server.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use rust_mcp_sdk::{
2828
};
2929
use serde_json::{json, Map, Value};
3030
use std::{
31+
collections::BTreeMap,
3132
collections::HashMap,
3233
error::Error,
3334
sync::Arc,
@@ -1746,7 +1747,7 @@ async fn should_handle_elicitation() {
17461747
let response = get_standalone_stream(&server.streamable_url, &session_id, None).await;
17471748
assert_eq!(response.status(), StatusCode::OK);
17481749

1749-
let mut content: HashMap<String, ElicitResultContent> = HashMap::new();
1750+
let mut content: BTreeMap<String, ElicitResultContent> = BTreeMap::new();
17501751
content.insert("name".to_string(), "Alice".into());
17511752
content.insert("email".to_string(), "alice@Borderland.com".into());
17521753
content.insert("age".to_string(), 25.into());

0 commit comments

Comments
 (0)