chore: consolidate repeated rmcp tests (#931)

This commit is contained in:
Dale Seo 2026-06-26 16:15:20 -04:00 • committed by GitHub
commit e1af378949
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 306 additions and 332 deletions

View file

@ -23,6 +23,7 @@ use tokio::io::{AsyncRead, ReadBuf};
// A slow tool server that sleeps before returning a response.
#[derive(Debug, Clone)]
struct SlowToolServer {
#[expect(dead_code, reason = "tool_handler macro accesses this router field")]
tool_router: ToolRouter<Self>,
}

View file

@ -12,7 +12,7 @@ use serde_json::json;
use tokio::sync::{Mutex, Notify};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
pub struct Server {}
struct Server {}
impl ServerHandler for Server {
fn get_info(&self) -> ServerInfo {

View file

@ -39,6 +39,7 @@ impl ClientHandler for MyClient {
}
pub struct MyServer {
#[expect(dead_code, reason = "tool_handler macro accesses this router field")]
tool_router: ToolRouter<Self>,
}

View file

@ -17,9 +17,9 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct CodeReviewRequest {
pub file_path: String,
pub language: String,
struct CodeReviewRequest {
file_path: String,
language: String,
}
#[prompt_handler(router = self.prompt_router)]
@ -194,17 +194,17 @@ impl CodeReviewRequest {}
// Struct defined for testing optional field schema generation
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OptionalFieldTestSchema {
struct OptionalFieldTestSchema {
#[schemars(description = "An optional description field")]
pub description: Option<String>,
description: Option<String>,
}
// Struct defined for testing optional i64 field schema generation and null handling
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OptionalI64TestSchema {
struct OptionalI64TestSchema {
#[schemars(description = "An optional i64 field")]
pub count: Option<i64>,
pub mandatory_field: String, // Added to ensure non-empty object schema
count: Option<i64>,
mandatory_field: String, // Added to ensure non-empty object schema
}
// Dummy struct to host the test prompt method

View file

@ -9,21 +9,21 @@ use rmcp::{
};
#[derive(Debug, Default)]
pub struct TestHandler<T: 'static = ()> {
pub _marker: std::marker::PhantomData<fn(*const T)>,
struct TestHandler<T: 'static = ()> {
_marker: std::marker::PhantomData<fn(*const T)>,
}
impl<T: 'static> ServerHandler for TestHandler<T> {}
#[derive(Debug, schemars::JsonSchema, serde::Deserialize, serde::Serialize)]
pub struct Request {
pub fields: HashMap<String, String>,
struct Request {
fields: HashMap<String, String>,
}
#[derive(Debug, schemars::JsonSchema, serde::Deserialize, serde::Serialize)]
pub struct Sum {
pub a: i32,
pub b: i32,
struct Sum {
a: i32,
b: i32,
}
#[rmcp::prompt_router(router = "test_router")]

View file

@ -18,6 +18,7 @@ use rmcp::{
/// Server with tools having different task support modes.
#[derive(Debug, Clone)]
pub struct TaskSupportTestServer {
#[expect(dead_code, reason = "tool_handler macro accesses this router field")]
tool_router: ToolRouter<Self>,
}

View file

@ -45,33 +45,26 @@ impl ServerHandler for TestToolServer {
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
}
fn call_tool(
async fn call_tool(
&self,
request: rmcp::model::CallToolRequestParams,
context: rmcp::service::RequestContext<RoleServer>,
) -> impl std::future::Future<Output = Result<CallToolResult, rmcp::ErrorData>> + MaybeSendFuture + '_
{
async move {
let router = self.router.read().await;
let tcc = ToolCallContext::new(self, request, context);
router.call(tcc).await
}
) -> Result<CallToolResult, rmcp::ErrorData> {
let router = self.router.read().await;
let tcc = ToolCallContext::new(self, request, context);
router.call(tcc).await
}
fn list_tools(
async fn list_tools(
&self,
_request: Option<rmcp::model::PaginatedRequestParams>,
_context: rmcp::service::RequestContext<RoleServer>,
) -> impl std::future::Future<Output = Result<rmcp::model::ListToolsResult, rmcp::ErrorData>>
+ MaybeSendFuture
+ '_ {
async move {
let router = self.router.read().await;
Ok(rmcp::model::ListToolsResult {
tools: router.list_all(),
..Default::default()
})
}
) -> Result<rmcp::model::ListToolsResult, rmcp::ErrorData> {
let router = self.router.read().await;
Ok(rmcp::model::ListToolsResult {
tools: router.list_all(),
..Default::default()
})
}
fn on_initialized(

View file

@ -18,11 +18,11 @@ use serde::{Deserialize, Serialize};
/// Parameters for weather tool.
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct GetWeatherRequest {
struct GetWeatherRequest {
/// City of interest.
pub city: String,
city: String,
/// Date of interest.
pub date: String,
date: String,
}
#[tool_handler(router = self.tool_router)]
@ -162,21 +162,21 @@ impl GetWeatherRequest {}
/// Struct defined for testing optional field schema generation.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OptionalFieldTestSchema {
struct OptionalFieldTestSchema {
/// Field description.
#[schemars(description = "An optional description field")]
pub description: Option<String>,
description: Option<String>,
}
/// Struct defined for testing optional i64 field schema generation and null handling.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OptionalI64TestSchema {
struct OptionalI64TestSchema {
/// Optional count field.
#[schemars(description = "An optional i64 field")]
pub count: Option<i64>,
count: Option<i64>,
/// Added to ensure non-empty object schema.
pub mandatory_field: String,
mandatory_field: String,
}
/// Dummy struct to host the test tool method.
@ -370,7 +370,7 @@ async fn test_optional_i64_field_with_null_input() -> anyhow::Result<()> {
/// Minimal server: no tool_router field, no new(), no get_info().
#[derive(Debug, Clone)]
pub struct MinimalServer;
struct MinimalServer;
#[tool_router]
impl MinimalServer {
@ -452,7 +452,7 @@ async fn test_minimal_server_tool_call() -> anyhow::Result<()> {
/// Same minimal pattern as [`MinimalServer`], but `#[tool_handler]` is omitted using
/// `#[tool_router(server_handler)]` (emits `#[tool_handler]` for a second macro pass).
#[derive(Debug, Clone)]
pub struct ElidedToolHandlerServer;
struct ElidedToolHandlerServer;
#[tool_router(server_handler)]
impl ElidedToolHandlerServer {
@ -509,7 +509,7 @@ async fn test_tool_router_server_handler_flag_end_to_end_tool_call() -> anyhow::
/// Server with custom name/version/instructions via tool_handler attributes.
#[derive(Debug, Clone)]
pub struct CustomInfoServer;
struct CustomInfoServer;
#[tool_router]
impl CustomInfoServer {
@ -539,7 +539,7 @@ fn test_custom_info_server() {
/// Server that provides its own get_info() — macro should not override it.
#[derive(Debug, Clone)]
pub struct ManualInfoServer;
struct ManualInfoServer;
#[tool_router]
impl ManualInfoServer {

View file

@ -11,20 +11,20 @@ use rmcp::{
};
#[derive(Debug, Default)]
pub struct TestHandler<T: 'static = ()> {
pub _marker: std::marker::PhantomData<fn(*const T)>,
struct TestHandler<T: 'static = ()> {
_marker: std::marker::PhantomData<fn(*const T)>,
}
impl<T: 'static> ServerHandler for TestHandler<T> {}
#[derive(Debug, schemars::JsonSchema, serde::Deserialize, serde::Serialize)]
pub struct Request {
pub fields: HashMap<String, String>,
struct Request {
fields: HashMap<String, String>,
}
#[derive(Debug, schemars::JsonSchema, serde::Deserialize, serde::Serialize)]
pub struct Sum {
pub a: i32,
pub b: i32,
struct Sum {
a: i32,
b: i32,
}
#[rmcp::tool_router(router = test_router_1)]