diff --git a/crates/rmcp/src/handler/server/router/prompt.rs b/crates/rmcp/src/handler/server/router/prompt.rs index a48d2ad7..6ea925a0 100644 --- a/crates/rmcp/src/handler/server/router/prompt.rs +++ b/crates/rmcp/src/handler/server/router/prompt.rs @@ -187,7 +187,9 @@ where } pub fn list_all(&self) -> Vec { - self.map.values().map(|item| item.attr.clone()).collect() + let mut prompts: Vec<_> = self.map.values().map(|item| item.attr.clone()).collect(); + prompts.sort_by(|a, b| a.name.cmp(&b.name)); + prompts } } diff --git a/crates/rmcp/src/handler/server/router/tool.rs b/crates/rmcp/src/handler/server/router/tool.rs index 72b7f2e2..51d49d97 100644 --- a/crates/rmcp/src/handler/server/router/tool.rs +++ b/crates/rmcp/src/handler/server/router/tool.rs @@ -252,7 +252,9 @@ where } pub fn list_all(&self) -> Vec { - self.map.values().map(|item| item.attr.clone()).collect() + let mut tools: Vec<_> = self.map.values().map(|item| item.attr.clone()).collect(); + tools.sort_by(|a, b| a.name.cmp(&b.name)); + tools } /// Get a tool definition by name. diff --git a/crates/rmcp/tests/test_prompt_routers.rs b/crates/rmcp/tests/test_prompt_routers.rs index 6dc223b3..0917a7f1 100644 --- a/crates/rmcp/tests/test_prompt_routers.rs +++ b/crates/rmcp/tests/test_prompt_routers.rs @@ -103,3 +103,39 @@ fn test_prompt_router() { let prompts = test_prompt_router.list_all(); assert_eq!(prompts.len(), 4); } + +#[test] +fn test_prompt_router_list_all_is_sorted() { + let router = TestHandler::<()>::test_router() + .with_route(rmcp::handler::server::router::prompt::PromptRoute::new_dyn( + async_function_prompt_attr(), + |mut context| { + Box::pin(async move { + use rmcp::handler::server::{ + common::FromContextPart, prompt::IntoGetPromptResult, + }; + let params = Parameters::::from_context_part(&mut context)?; + let result = async_function(params).await; + result.into_get_prompt_result() + }) + }, + )) + .with_route(rmcp::handler::server::router::prompt::PromptRoute::new_dyn( + async_function2_prompt_attr(), + |context| { + Box::pin(async move { + use rmcp::handler::server::prompt::IntoGetPromptResult; + let result = async_function2(context.server).await; + result.into_get_prompt_result() + }) + }, + )); + let prompts = router.list_all(); + let names: Vec<&str> = prompts.iter().map(|p| p.name.as_ref()).collect(); + let mut sorted = names.clone(); + sorted.sort(); + assert_eq!( + names, sorted, + "list_all() should return prompts sorted alphabetically by name" + ); +} diff --git a/crates/rmcp/tests/test_tool_routers.rs b/crates/rmcp/tests/test_tool_routers.rs index 442c70ea..987d1a0b 100644 --- a/crates/rmcp/tests/test_tool_routers.rs +++ b/crates/rmcp/tests/test_tool_routers.rs @@ -66,3 +66,20 @@ where H: CallToolHandler, { } + +#[test] +fn test_tool_router_list_all_is_sorted() { + let router: ToolRouter> = ToolRouter::>::new() + .with_route((async_function_tool_attr(), async_function)) + .with_route((async_function2_tool_attr(), async_function2)) + + TestHandler::<()>::test_router_1() + + TestHandler::<()>::test_router_2(); + let tools = router.list_all(); + let names: Vec<&str> = tools.iter().map(|t| t.name.as_ref()).collect(); + let mut sorted = names.clone(); + sorted.sort(); + assert_eq!( + names, sorted, + "list_all() should return tools sorted alphabetically by name" + ); +}