feat: provide blanket implementations for ClientHandler and ServerHandler traits (#609)
* feat!: implement ServerHandler for Box<H> and Arc<H> where H is a ServerHandler * feat!: implement ClientHandler for Box<H> and Arc<H> where H is a ClientHandler * test: test Box and Arc have blanket implementations for handler traits * refactor: deduplicate blanket implementations with macros
This commit is contained in:
parent
acb06ea819
commit
81f858836d
4 changed files with 348 additions and 1 deletions
|
|
@ -1,4 +1,6 @@
|
|||
pub mod progress;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
error::ErrorData as McpError,
|
||||
model::*,
|
||||
|
|
@ -210,3 +212,115 @@ impl ClientHandler for ClientInfo {
|
|||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_client_handler_for_wrapper {
|
||||
($wrapper:ident) => {
|
||||
impl<T: ClientHandler> ClientHandler for $wrapper<T> {
|
||||
fn ping(
|
||||
&self,
|
||||
context: RequestContext<RoleClient>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
(**self).ping(context)
|
||||
}
|
||||
|
||||
fn create_message(
|
||||
&self,
|
||||
params: CreateMessageRequestParam,
|
||||
context: RequestContext<RoleClient>,
|
||||
) -> impl Future<Output = Result<CreateMessageResult, McpError>> + Send + '_ {
|
||||
(**self).create_message(params, context)
|
||||
}
|
||||
|
||||
fn list_roots(
|
||||
&self,
|
||||
context: RequestContext<RoleClient>,
|
||||
) -> impl Future<Output = Result<ListRootsResult, McpError>> + Send + '_ {
|
||||
(**self).list_roots(context)
|
||||
}
|
||||
|
||||
fn create_elicitation(
|
||||
&self,
|
||||
request: CreateElicitationRequestParam,
|
||||
context: RequestContext<RoleClient>,
|
||||
) -> impl Future<Output = Result<CreateElicitationResult, McpError>> + Send + '_ {
|
||||
(**self).create_elicitation(request, context)
|
||||
}
|
||||
|
||||
fn on_custom_request(
|
||||
&self,
|
||||
request: CustomRequest,
|
||||
context: RequestContext<RoleClient>,
|
||||
) -> impl Future<Output = Result<CustomResult, McpError>> + Send + '_ {
|
||||
(**self).on_custom_request(request, context)
|
||||
}
|
||||
|
||||
fn on_cancelled(
|
||||
&self,
|
||||
params: CancelledNotificationParam,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_cancelled(params, context)
|
||||
}
|
||||
|
||||
fn on_progress(
|
||||
&self,
|
||||
params: ProgressNotificationParam,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_progress(params, context)
|
||||
}
|
||||
|
||||
fn on_logging_message(
|
||||
&self,
|
||||
params: LoggingMessageNotificationParam,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_logging_message(params, context)
|
||||
}
|
||||
|
||||
fn on_resource_updated(
|
||||
&self,
|
||||
params: ResourceUpdatedNotificationParam,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_resource_updated(params, context)
|
||||
}
|
||||
|
||||
fn on_resource_list_changed(
|
||||
&self,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_resource_list_changed(context)
|
||||
}
|
||||
|
||||
fn on_tool_list_changed(
|
||||
&self,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_tool_list_changed(context)
|
||||
}
|
||||
|
||||
fn on_prompt_list_changed(
|
||||
&self,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_prompt_list_changed(context)
|
||||
}
|
||||
|
||||
fn on_custom_notification(
|
||||
&self,
|
||||
notification: CustomNotification,
|
||||
context: NotificationContext<RoleClient>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_custom_notification(notification, context)
|
||||
}
|
||||
|
||||
fn get_info(&self) -> ClientInfo {
|
||||
(**self).get_info()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_client_handler_for_wrapper!(Box);
|
||||
impl_client_handler_for_wrapper!(Arc);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
error::ErrorData as McpError,
|
||||
model::*,
|
||||
|
|
@ -327,3 +329,206 @@ pub trait ServerHandler: Sized + Send + Sync + 'static {
|
|||
std::future::ready(Err(McpError::method_not_found::<CancelTaskMethod>()))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_server_handler_for_wrapper {
|
||||
($wrapper:ident) => {
|
||||
impl<T: ServerHandler> ServerHandler for $wrapper<T> {
|
||||
fn enqueue_task(
|
||||
&self,
|
||||
request: CallToolRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<CreateTaskResult, McpError>> + Send + '_ {
|
||||
(**self).enqueue_task(request, context)
|
||||
}
|
||||
|
||||
fn ping(
|
||||
&self,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
(**self).ping(context)
|
||||
}
|
||||
|
||||
fn initialize(
|
||||
&self,
|
||||
request: InitializeRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<InitializeResult, McpError>> + Send + '_ {
|
||||
(**self).initialize(request, context)
|
||||
}
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
request: CompleteRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<CompleteResult, McpError>> + Send + '_ {
|
||||
(**self).complete(request, context)
|
||||
}
|
||||
|
||||
fn set_level(
|
||||
&self,
|
||||
request: SetLevelRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
(**self).set_level(request, context)
|
||||
}
|
||||
|
||||
fn get_prompt(
|
||||
&self,
|
||||
request: GetPromptRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<GetPromptResult, McpError>> + Send + '_ {
|
||||
(**self).get_prompt(request, context)
|
||||
}
|
||||
|
||||
fn list_prompts(
|
||||
&self,
|
||||
request: Option<PaginatedRequestParam>,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<ListPromptsResult, McpError>> + Send + '_ {
|
||||
(**self).list_prompts(request, context)
|
||||
}
|
||||
|
||||
fn list_resources(
|
||||
&self,
|
||||
request: Option<PaginatedRequestParam>,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<ListResourcesResult, McpError>> + Send + '_ {
|
||||
(**self).list_resources(request, context)
|
||||
}
|
||||
|
||||
fn list_resource_templates(
|
||||
&self,
|
||||
request: Option<PaginatedRequestParam>,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<ListResourceTemplatesResult, McpError>> + Send + '_
|
||||
{
|
||||
(**self).list_resource_templates(request, context)
|
||||
}
|
||||
|
||||
fn read_resource(
|
||||
&self,
|
||||
request: ReadResourceRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<ReadResourceResult, McpError>> + Send + '_ {
|
||||
(**self).read_resource(request, context)
|
||||
}
|
||||
|
||||
fn subscribe(
|
||||
&self,
|
||||
request: SubscribeRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
(**self).subscribe(request, context)
|
||||
}
|
||||
|
||||
fn unsubscribe(
|
||||
&self,
|
||||
request: UnsubscribeRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
(**self).unsubscribe(request, context)
|
||||
}
|
||||
|
||||
fn call_tool(
|
||||
&self,
|
||||
request: CallToolRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<CallToolResult, McpError>> + Send + '_ {
|
||||
(**self).call_tool(request, context)
|
||||
}
|
||||
|
||||
fn list_tools(
|
||||
&self,
|
||||
request: Option<PaginatedRequestParam>,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<ListToolsResult, McpError>> + Send + '_ {
|
||||
(**self).list_tools(request, context)
|
||||
}
|
||||
|
||||
fn on_custom_request(
|
||||
&self,
|
||||
request: CustomRequest,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<CustomResult, McpError>> + Send + '_ {
|
||||
(**self).on_custom_request(request, context)
|
||||
}
|
||||
|
||||
fn on_cancelled(
|
||||
&self,
|
||||
notification: CancelledNotificationParam,
|
||||
context: NotificationContext<RoleServer>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_cancelled(notification, context)
|
||||
}
|
||||
|
||||
fn on_progress(
|
||||
&self,
|
||||
notification: ProgressNotificationParam,
|
||||
context: NotificationContext<RoleServer>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_progress(notification, context)
|
||||
}
|
||||
|
||||
fn on_initialized(
|
||||
&self,
|
||||
context: NotificationContext<RoleServer>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_initialized(context)
|
||||
}
|
||||
|
||||
fn on_roots_list_changed(
|
||||
&self,
|
||||
context: NotificationContext<RoleServer>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_roots_list_changed(context)
|
||||
}
|
||||
|
||||
fn on_custom_notification(
|
||||
&self,
|
||||
notification: CustomNotification,
|
||||
context: NotificationContext<RoleServer>,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
(**self).on_custom_notification(notification, context)
|
||||
}
|
||||
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
(**self).get_info()
|
||||
}
|
||||
|
||||
fn list_tasks(
|
||||
&self,
|
||||
request: Option<PaginatedRequestParam>,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<ListTasksResult, McpError>> + Send + '_ {
|
||||
(**self).list_tasks(request, context)
|
||||
}
|
||||
|
||||
fn get_task_info(
|
||||
&self,
|
||||
request: GetTaskInfoParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<GetTaskInfoResult, McpError>> + Send + '_ {
|
||||
(**self).get_task_info(request, context)
|
||||
}
|
||||
|
||||
fn get_task_result(
|
||||
&self,
|
||||
request: GetTaskResultParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<TaskResult, McpError>> + Send + '_ {
|
||||
(**self).get_task_result(request, context)
|
||||
}
|
||||
|
||||
fn cancel_task(
|
||||
&self,
|
||||
request: CancelTaskParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
(**self).cancel_task(request, context)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_server_handler_for_wrapper!(Box);
|
||||
impl_server_handler_for_wrapper!(Arc);
|
||||
|
|
|
|||
|
|
@ -133,6 +133,6 @@ where
|
|||
}
|
||||
|
||||
fn get_info(&self) -> <RoleServer as crate::service::ServiceRole>::Info {
|
||||
self.service.get_info()
|
||||
ServerHandler::get_info(&self.service)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
crates/rmcp/tests/test_handler_wrappers.rs
Normal file
28
crates/rmcp/tests/test_handler_wrappers.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// cargo test --test test_handler_wrappers --features "client server"
|
||||
|
||||
mod common;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::handlers::{TestClientHandler, TestServer};
|
||||
use rmcp::{ClientHandler, ServerHandler};
|
||||
|
||||
#[test]
|
||||
fn test_wrapped_server_handlers() {
|
||||
// This test asserts that, when T: ServerHandler, both Box<T> and Arc<T> also implement ServerHandler.
|
||||
fn accepts_server_handler<H: ServerHandler>(_handler: H) {}
|
||||
|
||||
accepts_server_handler(Box::new(TestServer::new()));
|
||||
accepts_server_handler(Arc::new(TestServer::new()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrapped_client_handlers() {
|
||||
// This test asserts that, when T: ClientHandler, both Box<T> and Arc<T> also implement ClientHandler.
|
||||
fn accepts_client_handler<H: ClientHandler>(_handler: H) {}
|
||||
|
||||
let client = TestClientHandler::new(false, false);
|
||||
|
||||
accepts_client_handler(Box::new(client.clone()));
|
||||
accepts_client_handler(Arc::new(client));
|
||||
}
|
||||
Loading…
Reference in a new issue