fix: use cfg-gated Send+Sync supertraits to avoid semver break (#757)

This commit is contained in:
Dale Seo 2026-03-18 11:37:32 -04:00 • committed by GitHub
commit d485249048
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 248 additions and 213 deletions

View file

@ -4,8 +4,7 @@ use crate::{
error::ErrorData as McpError, error::ErrorData as McpError,
model::{TaskSupport, *}, model::{TaskSupport, *},
service::{ service::{
MaybeSend, MaybeSendFuture, NotificationContext, RequestContext, RoleServer, Service, MaybeSendFuture, NotificationContext, RequestContext, RoleServer, Service, ServiceRole,
ServiceRole,
}, },
}; };
@ -161,214 +160,226 @@ impl<H: ServerHandler> Service<RoleServer> for H {
} }
} }
#[allow(unused_variables)] macro_rules! server_handler_methods {
#[allow( () => {
private_bounds, fn enqueue_task(
reason = "MaybeSend is a sealed conditional Send + Sync alias" &self,
)] _request: CallToolRequestParams,
pub trait ServerHandler: Sized + MaybeSend + 'static { _context: RequestContext<RoleServer>,
fn enqueue_task( ) -> impl Future<Output = Result<CreateTaskResult, McpError>> + MaybeSendFuture + '_ {
&self, std::future::ready(Err(McpError::internal_error(
_request: CallToolRequestParams, "Task processing not implemented".to_string(),
_context: RequestContext<RoleServer>, None,
) -> impl Future<Output = Result<CreateTaskResult, McpError>> + MaybeSendFuture + '_ { )))
std::future::ready(Err(McpError::internal_error( }
"Task processing not implemented".to_string(), fn ping(
None, &self,
))) context: RequestContext<RoleServer>,
} ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
fn ping( std::future::ready(Ok(()))
&self, }
context: RequestContext<RoleServer>, // handle requests
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ { fn initialize(
std::future::ready(Ok(())) &self,
} request: InitializeRequestParams,
// handle requests context: RequestContext<RoleServer>,
fn initialize( ) -> impl Future<Output = Result<InitializeResult, McpError>> + MaybeSendFuture + '_ {
&self, if context.peer.peer_info().is_none() {
request: InitializeRequestParams, context.peer.set_peer_info(request);
context: RequestContext<RoleServer>, }
) -> impl Future<Output = Result<InitializeResult, McpError>> + MaybeSendFuture + '_ { std::future::ready(Ok(self.get_info()))
if context.peer.peer_info().is_none() { }
context.peer.set_peer_info(request); fn complete(
&self,
request: CompleteRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<CompleteResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(CompleteResult::default()))
}
fn set_level(
&self,
request: SetLevelRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<SetLevelRequestMethod>()))
}
fn get_prompt(
&self,
request: GetPromptRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<GetPromptResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<GetPromptRequestMethod>()))
}
fn list_prompts(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListPromptsResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(ListPromptsResult::default()))
}
fn list_resources(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListResourcesResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(ListResourcesResult::default()))
}
fn list_resource_templates(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListResourceTemplatesResult, McpError>>
+ MaybeSendFuture
+ '_ {
std::future::ready(Ok(ListResourceTemplatesResult::default()))
}
fn read_resource(
&self,
request: ReadResourceRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ReadResourceResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(
McpError::method_not_found::<ReadResourceRequestMethod>(),
))
}
fn subscribe(
&self,
request: SubscribeRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<SubscribeRequestMethod>()))
}
fn unsubscribe(
&self,
request: UnsubscribeRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(
McpError::method_not_found::<UnsubscribeRequestMethod>(),
))
}
fn call_tool(
&self,
request: CallToolRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<CallToolResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<CallToolRequestMethod>()))
}
fn list_tools(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListToolsResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(ListToolsResult::default()))
}
/// Get a tool definition by name.
///
/// The default implementation returns `None`, which bypasses validation.
/// When using `#[tool_handler]`, this method is automatically implemented.
fn get_tool(&self, _name: &str) -> Option<Tool> {
None
}
fn on_custom_request(
&self,
request: CustomRequest,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<CustomResult, McpError>> + MaybeSendFuture + '_ {
let CustomRequest { method, .. } = request;
let _ = context;
std::future::ready(Err(McpError::new(
ErrorCode::METHOD_NOT_FOUND,
method,
None,
)))
} }
std::future::ready(Ok(self.get_info()))
}
fn complete(
&self,
request: CompleteRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<CompleteResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(CompleteResult::default()))
}
fn set_level(
&self,
request: SetLevelRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<SetLevelRequestMethod>()))
}
fn get_prompt(
&self,
request: GetPromptRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<GetPromptResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<GetPromptRequestMethod>()))
}
fn list_prompts(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListPromptsResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(ListPromptsResult::default()))
}
fn list_resources(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListResourcesResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(ListResourcesResult::default()))
}
fn list_resource_templates(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListResourceTemplatesResult, McpError>> + MaybeSendFuture + '_
{
std::future::ready(Ok(ListResourceTemplatesResult::default()))
}
fn read_resource(
&self,
request: ReadResourceRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ReadResourceResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(
McpError::method_not_found::<ReadResourceRequestMethod>(),
))
}
fn subscribe(
&self,
request: SubscribeRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<SubscribeRequestMethod>()))
}
fn unsubscribe(
&self,
request: UnsubscribeRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<UnsubscribeRequestMethod>()))
}
fn call_tool(
&self,
request: CallToolRequestParams,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<CallToolResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<CallToolRequestMethod>()))
}
fn list_tools(
&self,
request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListToolsResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Ok(ListToolsResult::default()))
}
/// Get a tool definition by name.
///
/// The default implementation returns `None`, which bypasses validation.
/// When using `#[tool_handler]`, this method is automatically implemented.
fn get_tool(&self, _name: &str) -> Option<Tool> {
None
}
fn on_custom_request(
&self,
request: CustomRequest,
context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<CustomResult, McpError>> + MaybeSendFuture + '_ {
let CustomRequest { method, .. } = request;
let _ = context;
std::future::ready(Err(McpError::new(
ErrorCode::METHOD_NOT_FOUND,
method,
None,
)))
}
fn on_cancelled( fn on_cancelled(
&self, &self,
notification: CancelledNotificationParam, notification: CancelledNotificationParam,
context: NotificationContext<RoleServer>, context: NotificationContext<RoleServer>,
) -> impl Future<Output = ()> + MaybeSendFuture + '_ { ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
std::future::ready(()) std::future::ready(())
} }
fn on_progress( fn on_progress(
&self, &self,
notification: ProgressNotificationParam, notification: ProgressNotificationParam,
context: NotificationContext<RoleServer>, context: NotificationContext<RoleServer>,
) -> impl Future<Output = ()> + MaybeSendFuture + '_ { ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
std::future::ready(()) std::future::ready(())
} }
fn on_initialized( fn on_initialized(
&self, &self,
context: NotificationContext<RoleServer>, context: NotificationContext<RoleServer>,
) -> impl Future<Output = ()> + MaybeSendFuture + '_ { ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
tracing::info!("client initialized"); tracing::info!("client initialized");
std::future::ready(()) std::future::ready(())
} }
fn on_roots_list_changed( fn on_roots_list_changed(
&self, &self,
context: NotificationContext<RoleServer>, context: NotificationContext<RoleServer>,
) -> impl Future<Output = ()> + MaybeSendFuture + '_ { ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
std::future::ready(()) std::future::ready(())
} }
fn on_custom_notification( fn on_custom_notification(
&self, &self,
notification: CustomNotification, notification: CustomNotification,
context: NotificationContext<RoleServer>, context: NotificationContext<RoleServer>,
) -> impl Future<Output = ()> + MaybeSendFuture + '_ { ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
let _ = (notification, context); let _ = (notification, context);
std::future::ready(()) std::future::ready(())
} }
fn get_info(&self) -> ServerInfo { fn get_info(&self) -> ServerInfo {
ServerInfo::default() ServerInfo::default()
} }
fn list_tasks( fn list_tasks(
&self, &self,
request: Option<PaginatedRequestParams>, request: Option<PaginatedRequestParams>,
context: RequestContext<RoleServer>, context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<ListTasksResult, McpError>> + MaybeSendFuture + '_ { ) -> impl Future<Output = Result<ListTasksResult, McpError>> + MaybeSendFuture + '_ {
std::future::ready(Err(McpError::method_not_found::<ListTasksMethod>())) std::future::ready(Err(McpError::method_not_found::<ListTasksMethod>()))
} }
fn get_task_info( fn get_task_info(
&self, &self,
request: GetTaskInfoParams, request: GetTaskInfoParams,
context: RequestContext<RoleServer>, context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<GetTaskResult, McpError>> + MaybeSendFuture + '_ { ) -> impl Future<Output = Result<GetTaskResult, McpError>> + MaybeSendFuture + '_ {
let _ = (request, context); let _ = (request, context);
std::future::ready(Err(McpError::method_not_found::<GetTaskInfoMethod>())) std::future::ready(Err(McpError::method_not_found::<GetTaskInfoMethod>()))
} }
fn get_task_result( fn get_task_result(
&self, &self,
request: GetTaskResultParams, request: GetTaskResultParams,
context: RequestContext<RoleServer>, context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<GetTaskPayloadResult, McpError>> + MaybeSendFuture + '_ { ) -> impl Future<Output = Result<GetTaskPayloadResult, McpError>> + MaybeSendFuture + '_ {
let _ = (request, context); let _ = (request, context);
std::future::ready(Err(McpError::method_not_found::<GetTaskResultMethod>())) std::future::ready(Err(McpError::method_not_found::<GetTaskResultMethod>()))
} }
fn cancel_task( fn cancel_task(
&self, &self,
request: CancelTaskParams, request: CancelTaskParams,
context: RequestContext<RoleServer>, context: RequestContext<RoleServer>,
) -> impl Future<Output = Result<CancelTaskResult, McpError>> + MaybeSendFuture + '_ { ) -> impl Future<Output = Result<CancelTaskResult, McpError>> + MaybeSendFuture + '_ {
let _ = (request, context); let _ = (request, context);
std::future::ready(Err(McpError::method_not_found::<CancelTaskMethod>())) std::future::ready(Err(McpError::method_not_found::<CancelTaskMethod>()))
} }
};
}
#[allow(unused_variables)]
#[cfg(not(feature = "local"))]
pub trait ServerHandler: Sized + Send + Sync + 'static {
server_handler_methods!();
}
#[allow(unused_variables)]
#[cfg(feature = "local")]
pub trait ServerHandler: Sized + 'static {
server_handler_methods!();
} }
macro_rules! impl_server_handler_for_wrapper { macro_rules! impl_server_handler_for_wrapper {

View file

@ -128,11 +128,23 @@ pub type RxJsonRpcMessage<R> = JsonRpcMessage<
<R as ServiceRole>::PeerNot, <R as ServiceRole>::PeerNot,
>; >;
#[allow( #[cfg(not(feature = "local"))]
private_bounds, pub trait Service<R: ServiceRole>: Send + Sync + 'static {
reason = "MaybeSend is a sealed conditional Send + Sync alias" fn handle_request(
)] &self,
pub trait Service<R: ServiceRole>: MaybeSend + 'static { request: R::PeerReq,
context: RequestContext<R>,
) -> impl Future<Output = Result<R::Resp, McpError>> + MaybeSendFuture + '_;
fn handle_notification(
&self,
notification: R::PeerNot,
context: NotificationContext<R>,
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_;
fn get_info(&self) -> R::Info;
}
#[cfg(feature = "local")]
pub trait Service<R: ServiceRole>: 'static {
fn handle_request( fn handle_request(
&self, &self,
request: R::PeerReq, request: R::PeerReq,
@ -197,11 +209,23 @@ impl<R: ServiceRole> Service<R> for Box<dyn DynService<R>> {
} }
} }
#[allow( #[cfg(not(feature = "local"))]
private_bounds, pub trait DynService<R: ServiceRole>: Send + Sync {
reason = "MaybeSend is a sealed conditional Send + Sync alias" fn handle_request(
)] &self,
pub trait DynService<R: ServiceRole>: MaybeSend { request: R::PeerReq,
context: RequestContext<R>,
) -> MaybeBoxFuture<'_, Result<R::Resp, McpError>>;
fn handle_notification(
&self,
notification: R::PeerNot,
context: NotificationContext<R>,
) -> MaybeBoxFuture<'_, Result<(), McpError>>;
fn get_info(&self) -> R::Info;
}
#[cfg(feature = "local")]
pub trait DynService<R: ServiceRole> {
fn handle_request( fn handle_request(
&self, &self,
request: R::PeerReq, request: R::PeerReq,