* fix(model): deprecate ServerInfo and ClientInfo aliases Those names collide with the protocol's serverInfo/clientInfo fields, which are only Implementation identity. Prefer InitializeResult and InitializeRequestParams. Handler trait signatures, the tool_handler macro output, tests, examples and docs now use the canonical types; the aliases remain as deprecated shims for the rest of 3.x. Fixes #1082 * fix(model): keep public signatures on the deprecated aliases The public API check compares rendered signatures, and rustdoc records a type alias by the name it was written with. Respelling `ServerHandler::get_info`, `ClientHandler::get_info`, `DiscoverResult::from_server_info` and `impl ClientHandler for ClientInfo` onto `InitializeResult`/`InitializeRequestParams` therefore reads as changed public items even though the aliases resolve to those exact types and nothing downstream breaks. Put those four signatures back on the aliases so the published API is byte-identical, and note why at the alias definitions. The deprecation still steers new code, and the macro output, tests, examples and docs keep using the canonical names. * fix(model): migrate leftover ClientInfo test sites Two tests landed on main after the alias deprecation and still named ClientInfo. Clippy -D warnings and the no-local feature test job both failed on them.
433 lines
16 KiB
Rust
433 lines
16 KiB
Rust
// Sampling/Roots/Logging are SEP-2577-deprecated; internal references are expected.
|
|
#![expect(deprecated)]
|
|
pub mod progress;
|
|
use std::sync::Arc;
|
|
|
|
use crate::{
|
|
error::ErrorData as McpError,
|
|
model::*,
|
|
service::{
|
|
MaybeSendFuture, NotificationContext, RequestContext, RoleClient, Service, ServiceRole,
|
|
},
|
|
};
|
|
|
|
impl<H: ClientHandler> Service<RoleClient> for H {
|
|
async fn handle_request(
|
|
&self,
|
|
request: <RoleClient as ServiceRole>::PeerReq,
|
|
context: RequestContext<RoleClient>,
|
|
) -> Result<<RoleClient as ServiceRole>::Resp, McpError> {
|
|
match request {
|
|
ServerRequest::PingRequest(_) => self.ping(context).await.map(ClientResult::empty),
|
|
ServerRequest::CreateMessageRequest(request) => self
|
|
.create_message(request.params, context)
|
|
.await
|
|
.map(Box::new)
|
|
.map(ClientResult::CreateMessageResult),
|
|
ServerRequest::ListRootsRequest(_) => self
|
|
.list_roots(context)
|
|
.await
|
|
.map(ClientResult::ListRootsResult),
|
|
ServerRequest::ElicitRequest(request) => self
|
|
.create_elicitation(request.params, context)
|
|
.await
|
|
.map(ClientResult::ElicitResult),
|
|
ServerRequest::CustomRequest(request) => self
|
|
.on_custom_request(request, context)
|
|
.await
|
|
.map(ClientResult::CustomResult),
|
|
}
|
|
}
|
|
|
|
async fn handle_notification(
|
|
&self,
|
|
notification: <RoleClient as ServiceRole>::PeerNot,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> Result<(), McpError> {
|
|
match notification {
|
|
ServerNotification::CancelledNotification(notification) => {
|
|
self.on_cancelled(notification.params, context).await
|
|
}
|
|
ServerNotification::ProgressNotification(notification) => {
|
|
self.on_progress(notification.params, context).await
|
|
}
|
|
ServerNotification::LoggingMessageNotification(notification) => {
|
|
self.on_logging_message(notification.params, context).await
|
|
}
|
|
ServerNotification::ResourceUpdatedNotification(notification) => {
|
|
self.on_resource_updated(notification.params, context).await
|
|
}
|
|
ServerNotification::ResourceListChangedNotification(_notification_no_param) => {
|
|
self.on_resource_list_changed(context).await
|
|
}
|
|
ServerNotification::ToolListChangedNotification(_notification_no_param) => {
|
|
self.on_tool_list_changed(context).await
|
|
}
|
|
ServerNotification::PromptListChangedNotification(_notification_no_param) => {
|
|
self.on_prompt_list_changed(context).await
|
|
}
|
|
ServerNotification::SubscriptionsAcknowledgedNotification(notification) => {
|
|
self.on_subscriptions_acknowledged(notification.params, context)
|
|
.await
|
|
}
|
|
ServerNotification::TaskStatusNotification(notification) => {
|
|
self.on_task_status(notification.params, context).await
|
|
}
|
|
ServerNotification::CustomNotification(notification) => {
|
|
self.on_custom_notification(notification, context).await
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
fn get_info(&self) -> <RoleClient as ServiceRole>::Info {
|
|
self.get_info()
|
|
}
|
|
}
|
|
|
|
macro_rules! client_handler_methods {
|
|
() => {
|
|
fn ping(
|
|
&self,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
|
|
std::future::ready(Ok(()))
|
|
}
|
|
|
|
fn create_message(
|
|
&self,
|
|
params: CreateMessageRequestParams,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<CreateMessageResult, McpError>> + MaybeSendFuture + '_ {
|
|
std::future::ready(Err(
|
|
McpError::method_not_found::<CreateMessageRequestMethod>(),
|
|
))
|
|
}
|
|
|
|
fn list_roots(
|
|
&self,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<ListRootsResult, McpError>> + MaybeSendFuture + '_ {
|
|
std::future::ready(Ok(ListRootsResult::default()))
|
|
}
|
|
|
|
/// Handle an elicitation request from a server asking for user input.
|
|
///
|
|
/// This method is called when a server needs interactive input from the user
|
|
/// during tool execution. Implementations should present the message to the user,
|
|
/// collect their input according to the requested schema, and return the result.
|
|
///
|
|
/// # Arguments
|
|
/// * `request` - The elicitation request with message and schema
|
|
/// * `context` - The request context
|
|
///
|
|
/// # Returns
|
|
/// The user's response including action (accept/decline/cancel) and optional data
|
|
///
|
|
/// # Default Behavior
|
|
/// The default implementation automatically declines all elicitation requests.
|
|
/// Real clients should override this to provide user interaction.
|
|
///
|
|
/// # Example
|
|
/// ```rust,no_run
|
|
/// use rmcp::{
|
|
/// ClientHandler,
|
|
/// model::{
|
|
/// ElicitRequestParams, ElicitResult, ElicitationAction, ElicitationSchema,
|
|
/// ErrorData as McpError,
|
|
/// },
|
|
/// service::{RequestContext, RoleClient},
|
|
/// };
|
|
///
|
|
/// # struct MyClient;
|
|
/// #
|
|
/// # async fn get_user_input(
|
|
/// # _message: String,
|
|
/// # _schema: ElicitationSchema,
|
|
/// # ) -> Result<serde_json::Value, McpError> {
|
|
/// # std::future::pending().await
|
|
/// # }
|
|
/// #
|
|
/// # async fn open_url_in_browser(_url: String) -> Result<(), McpError> {
|
|
/// # Ok(())
|
|
/// # }
|
|
/// #
|
|
/// impl ClientHandler for MyClient {
|
|
/// async fn create_elicitation(
|
|
/// &self,
|
|
/// request: ElicitRequestParams,
|
|
/// _context: RequestContext<RoleClient>,
|
|
/// ) -> Result<ElicitResult, McpError> {
|
|
/// match request {
|
|
/// ElicitRequestParams::FormElicitationParams {
|
|
/// message,
|
|
/// requested_schema,
|
|
/// ..
|
|
/// } => {
|
|
/// let input = get_user_input(message, requested_schema).await?;
|
|
/// Ok(ElicitResult::new(ElicitationAction::Accept).with_content(input))
|
|
/// }
|
|
/// ElicitRequestParams::UrlElicitationParams { url, .. } => {
|
|
/// open_url_in_browser(url).await?;
|
|
/// Ok(ElicitResult::new(ElicitationAction::Accept))
|
|
/// }
|
|
/// _ => Ok(ElicitResult::new(ElicitationAction::Decline)),
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
fn create_elicitation(
|
|
&self,
|
|
request: ElicitRequestParams,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<ElicitResult, McpError>> + MaybeSendFuture + '_ {
|
|
// Default implementation declines all requests - real clients should override this
|
|
let _ = (request, context);
|
|
std::future::ready(Ok(ElicitResult {
|
|
action: ElicitationAction::Decline,
|
|
content: None,
|
|
meta: None,
|
|
}))
|
|
}
|
|
|
|
fn on_custom_request(
|
|
&self,
|
|
request: CustomRequest,
|
|
context: RequestContext<RoleClient>,
|
|
) -> 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(
|
|
&self,
|
|
params: CancelledNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_progress(
|
|
&self,
|
|
params: ProgressNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_logging_message(
|
|
&self,
|
|
params: LoggingMessageNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_resource_updated(
|
|
&self,
|
|
params: ResourceUpdatedNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_resource_list_changed(
|
|
&self,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_tool_list_changed(
|
|
&self,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_prompt_list_changed(
|
|
&self,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_subscriptions_acknowledged(
|
|
&self,
|
|
params: SubscriptionsAcknowledgedNotificationParams,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
|
|
fn on_task_status(
|
|
&self,
|
|
params: TaskStatusNotificationParams,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
std::future::ready(())
|
|
}
|
|
fn on_custom_notification(
|
|
&self,
|
|
notification: CustomNotification,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
let _ = (notification, context);
|
|
std::future::ready(())
|
|
}
|
|
|
|
fn get_info(&self) -> ClientInfo {
|
|
InitializeRequestParams::default()
|
|
}
|
|
};
|
|
}
|
|
|
|
#[allow(unused_variables)]
|
|
#[cfg(not(feature = "local"))]
|
|
pub trait ClientHandler: Sized + Send + Sync + 'static {
|
|
client_handler_methods!();
|
|
}
|
|
|
|
#[allow(unused_variables)]
|
|
#[cfg(feature = "local")]
|
|
pub trait ClientHandler: Sized + 'static {
|
|
client_handler_methods!();
|
|
}
|
|
|
|
/// Do nothing, with default client info.
|
|
impl ClientHandler for () {}
|
|
|
|
/// Do nothing, with a specific client info.
|
|
impl ClientHandler for ClientInfo {
|
|
fn get_info(&self) -> 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>> + MaybeSendFuture + '_ {
|
|
(**self).ping(context)
|
|
}
|
|
|
|
fn create_message(
|
|
&self,
|
|
params: CreateMessageRequestParams,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<CreateMessageResult, McpError>> + MaybeSendFuture + '_
|
|
{
|
|
(**self).create_message(params, context)
|
|
}
|
|
|
|
fn list_roots(
|
|
&self,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<ListRootsResult, McpError>> + MaybeSendFuture + '_
|
|
{
|
|
(**self).list_roots(context)
|
|
}
|
|
|
|
fn create_elicitation(
|
|
&self,
|
|
request: ElicitRequestParams,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<ElicitResult, McpError>> + MaybeSendFuture + '_ {
|
|
(**self).create_elicitation(request, context)
|
|
}
|
|
|
|
fn on_custom_request(
|
|
&self,
|
|
request: CustomRequest,
|
|
context: RequestContext<RoleClient>,
|
|
) -> impl Future<Output = Result<CustomResult, McpError>> + MaybeSendFuture + '_ {
|
|
(**self).on_custom_request(request, context)
|
|
}
|
|
|
|
fn on_cancelled(
|
|
&self,
|
|
params: CancelledNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_cancelled(params, context)
|
|
}
|
|
|
|
fn on_progress(
|
|
&self,
|
|
params: ProgressNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_progress(params, context)
|
|
}
|
|
|
|
fn on_logging_message(
|
|
&self,
|
|
params: LoggingMessageNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_logging_message(params, context)
|
|
}
|
|
|
|
fn on_resource_updated(
|
|
&self,
|
|
params: ResourceUpdatedNotificationParam,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_resource_updated(params, context)
|
|
}
|
|
|
|
fn on_resource_list_changed(
|
|
&self,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_resource_list_changed(context)
|
|
}
|
|
|
|
fn on_tool_list_changed(
|
|
&self,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_tool_list_changed(context)
|
|
}
|
|
|
|
fn on_prompt_list_changed(
|
|
&self,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_prompt_list_changed(context)
|
|
}
|
|
|
|
fn on_subscriptions_acknowledged(
|
|
&self,
|
|
params: SubscriptionsAcknowledgedNotificationParams,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_subscriptions_acknowledged(params, context)
|
|
}
|
|
|
|
fn on_task_status(
|
|
&self,
|
|
params: TaskStatusNotificationParams,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**self).on_task_status(params, context)
|
|
}
|
|
|
|
fn on_custom_notification(
|
|
&self,
|
|
notification: CustomNotification,
|
|
context: NotificationContext<RoleClient>,
|
|
) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
|
|
(**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);
|