Skip to main content

rmcp_soddygo/service/
client.rs

1// Sampling/Roots/Logging are SEP-2577-deprecated; internal references are expected.
2#![expect(deprecated)]
3use std::{borrow::Cow, sync::Arc, time::Duration};
4
5use thiserror::Error;
6
7use super::*;
8use crate::{
9    model::{
10        ArgumentInfo, CallToolRequest, CallToolRequestParams, CallToolResponse, CallToolResult,
11        CancelledNotification, CancelledNotificationParam, ClientInfo, ClientJsonRpcMessage,
12        ClientNotification, ClientRequest, ClientResult, CompleteRequest, CompleteRequestParams,
13        CompleteResult, CompletionContext, CompletionInfo, DEFAULT_MRTR_MAX_ROUNDS,
14        DiscoverRequest, DiscoverRequestParams, DiscoverResult, ErrorData, GetExtensions, GetMeta,
15        GetPromptRequest, GetPromptRequestParams, GetPromptResponse, GetPromptResult,
16        InitializeRequest, InitializedNotification, InputRequest, InputRequiredResult,
17        InputResponses, JsonRpcResponse, ListPromptsRequest, ListPromptsResult,
18        ListResourceTemplatesRequest, ListResourceTemplatesResult, ListResourcesRequest,
19        ListResourcesResult, ListToolsRequest, ListToolsResult, NumberOrString,
20        PaginatedRequestParams, ProgressNotification, ProgressNotificationParam, ProtocolVersion,
21        ReadResourceRequest, ReadResourceRequestParams, ReadResourceResponse, ReadResourceResult,
22        Reference, RequestId, RequestMetaObject, RootsListChangedNotification, ServerInfo,
23        ServerJsonRpcMessage, ServerNotification, ServerRequest, ServerResult, SetLevelRequest,
24        SetLevelRequestParams, SubscribeRequest, SubscribeRequestParams, UnsubscribeRequest,
25        UnsubscribeRequestParams,
26    },
27    transport::DynamicTransportError,
28};
29
30/// It represents the error that may occur when serving the client.
31///
32/// if you want to handle the error, you can use `serve_client_with_ct` or `serve_client` with `Result<RunningService<RoleClient, S>, ClientError>`
33#[derive(Error, Debug)]
34#[non_exhaustive]
35pub enum ClientInitializeError {
36    #[error("expect initialized response, but received: {0:?}")]
37    ExpectedInitResponse(Option<ServerJsonRpcMessage>),
38
39    #[error("expect initialized result, but received: {0:?}")]
40    ExpectedInitResult(Option<ServerResult>),
41
42    #[error("conflict initialized response id: expected {0}, got {1}")]
43    ConflictInitResponseId(RequestId, RequestId),
44
45    #[error("connection closed: {0}")]
46    ConnectionClosed(String),
47
48    #[error("Send message error {error}, when {context}")]
49    TransportError {
50        error: DynamicTransportError,
51        context: Cow<'static, str>,
52    },
53
54    #[error("JSON-RPC error: {0}")]
55    JsonRpcError(ErrorData),
56
57    #[error(
58        "no compatible protocol version (client: {client_supported:?}, server: {server_supported:?})"
59    )]
60    NoCompatibleProtocolVersion {
61        client_supported: Vec<ProtocolVersion>,
62        server_supported: Vec<ProtocolVersion>,
63    },
64
65    #[error("discover startup requires at least one preferred protocol version")]
66    NoPreferredProtocolVersion,
67
68    #[error("Cancelled")]
69    Cancelled,
70}
71
72impl ClientInitializeError {
73    pub fn transport<T: Transport<RoleClient> + 'static>(
74        error: T::Error,
75        context: impl Into<Cow<'static, str>>,
76    ) -> Self {
77        Self::TransportError {
78            error: DynamicTransportError::new::<T, _>(error),
79            context: context.into(),
80        }
81    }
82}
83
84/// Helper function to get the next message from the stream
85async fn expect_next_message<T>(
86    transport: &mut T,
87    context: &str,
88) -> Result<ServerJsonRpcMessage, ClientInitializeError>
89where
90    T: Transport<RoleClient>,
91{
92    transport
93        .receive()
94        .await
95        .ok_or_else(|| ClientInitializeError::ConnectionClosed(context.to_string()))
96}
97
98/// Helper function to expect a response from the stream
99async fn expect_response<T, S>(
100    transport: &mut T,
101    context: &str,
102    service: &S,
103    peer: Peer<RoleClient>,
104) -> Result<(ServerResult, RequestId), ClientInitializeError>
105where
106    T: Transport<RoleClient>,
107    S: Service<RoleClient>,
108{
109    loop {
110        let message = expect_next_message(transport, context).await?;
111        match message {
112            // Expected message to complete the initialization
113            ServerJsonRpcMessage::Response(JsonRpcResponse { id, result, .. }) => {
114                break Ok((result, id));
115            }
116            // Handle JSON-RPC error responses
117            ServerJsonRpcMessage::Error(error) => {
118                break Err(ClientInitializeError::JsonRpcError(error.error));
119            }
120            // Server could send logging messages before handshake
121            ServerJsonRpcMessage::Notification(mut notification) => {
122                let ServerNotification::LoggingMessageNotification(logging) =
123                    &mut notification.notification
124                else {
125                    tracing::warn!(?notification, "Received unexpected message");
126                    continue;
127                };
128
129                let mut context = NotificationContext {
130                    peer: peer.clone(),
131                    meta: NotificationMetaObject::default(),
132                    extensions: Extensions::default(),
133                };
134
135                if let Some(meta) = logging.extensions.get_mut::<NotificationMetaObject>() {
136                    std::mem::swap(&mut context.meta, meta);
137                }
138                std::mem::swap(&mut context.extensions, &mut logging.extensions);
139
140                if let Err(error) = service
141                    .handle_notification(notification.notification, context)
142                    .await
143                {
144                    tracing::warn!(?error, "Handle logging before handshake failed.");
145                }
146            }
147            // Server could send pings before handshake
148            ServerJsonRpcMessage::Request(ref request)
149                if matches!(request.request, ServerRequest::PingRequest(_)) =>
150            {
151                tracing::trace!("Received ping request. Ignored.")
152            }
153            // Server SHOULD NOT send any other messages before handshake. We ignore them anyway
154            _ => tracing::warn!(?message, "Received unexpected message"),
155        }
156    }
157}
158
159#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
160#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
161pub struct RoleClient;
162
163/// Select the first client-preferred protocol version supported by the server.
164///
165/// Returns `None` when no version is shared.
166pub fn select_protocol_version(
167    client_preference: &[ProtocolVersion],
168    server_supported: &[ProtocolVersion],
169) -> Option<ProtocolVersion> {
170    client_preference
171        .iter()
172        .find(|version| server_supported.contains(version))
173        .cloned()
174}
175
176impl ServiceRole for RoleClient {
177    type Req = ClientRequest;
178    type Resp = ClientResult;
179    type Not = ClientNotification;
180    type PeerReq = ServerRequest;
181    type PeerResp = ServerResult;
182    type PeerNot = ServerNotification;
183    type Info = ClientInfo;
184    type PeerInfo = ServerInfo;
185    type InitializeError = ClientInitializeError;
186    const IS_CLIENT: bool = true;
187}
188
189pub type ServerSink = Peer<RoleClient>;
190
191/// Selects how a client establishes its MCP lifecycle.
192///
193/// Existing [`ServiceExt::serve`] behavior remains legacy initialization.
194#[derive(Debug, Clone, PartialEq, Eq)]
195#[non_exhaustive]
196pub enum ClientLifecycleMode {
197    /// Use the legacy `initialize` / `notifications/initialized` handshake.
198    Initialize,
199    /// Use `server/discover` and send self-contained per-request metadata.
200    Discover {
201        preferred_versions: Vec<ProtocolVersion>,
202    },
203    /// Probe with `server/discover`, falling back only when the peer proves it is legacy.
204    Auto {
205        preferred_versions: Vec<ProtocolVersion>,
206        legacy_version: Option<ProtocolVersion>,
207    },
208}
209
210/// Client-specific lifecycle entry points.
211pub trait ClientServiceExt: Service<RoleClient> + Sized {
212    fn serve_with_lifecycle<T, E, A>(
213        self,
214        transport: T,
215        lifecycle: ClientLifecycleMode,
216    ) -> impl Future<Output = Result<RunningService<RoleClient, Self>, ClientInitializeError>>
217    + MaybeSendFuture
218    where
219        T: IntoTransport<RoleClient, E, A>,
220        E: std::error::Error + Send + Sync + 'static,
221    {
222        serve_client_with_lifecycle(self, transport, lifecycle)
223    }
224}
225
226impl<S: Service<RoleClient>> ClientServiceExt for S {}
227
228impl<S: Service<RoleClient>> ServiceExt<RoleClient> for S {
229    fn serve_with_ct<T, E, A>(
230        self,
231        transport: T,
232        ct: CancellationToken,
233    ) -> impl Future<Output = Result<RunningService<RoleClient, Self>, ClientInitializeError>>
234    + MaybeSendFuture
235    where
236        T: IntoTransport<RoleClient, E, A>,
237        E: std::error::Error + Send + Sync + 'static,
238        Self: Sized,
239    {
240        serve_client_with_ct(self, transport, ct)
241    }
242}
243
244pub async fn serve_client<S, T, E, A>(
245    service: S,
246    transport: T,
247) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
248where
249    S: Service<RoleClient>,
250    T: IntoTransport<RoleClient, E, A>,
251    E: std::error::Error + Send + Sync + 'static,
252{
253    serve_client_with_lifecycle_and_ct(
254        service,
255        transport,
256        ClientLifecycleMode::Initialize,
257        Default::default(),
258    )
259    .await
260}
261
262pub async fn serve_client_with_ct<S, T, E, A>(
263    service: S,
264    transport: T,
265    ct: CancellationToken,
266) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
267where
268    S: Service<RoleClient>,
269    T: IntoTransport<RoleClient, E, A>,
270    E: std::error::Error + Send + Sync + 'static,
271{
272    serve_client_with_lifecycle_and_ct(service, transport, ClientLifecycleMode::Initialize, ct)
273        .await
274}
275
276pub async fn serve_client_with_lifecycle<S, T, E, A>(
277    service: S,
278    transport: T,
279    lifecycle: ClientLifecycleMode,
280) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
281where
282    S: Service<RoleClient>,
283    T: IntoTransport<RoleClient, E, A>,
284    E: std::error::Error + Send + Sync + 'static,
285{
286    serve_client_with_lifecycle_and_ct(service, transport, lifecycle, Default::default()).await
287}
288
289pub async fn serve_client_with_lifecycle_and_ct<S, T, E, A>(
290    service: S,
291    transport: T,
292    lifecycle: ClientLifecycleMode,
293    ct: CancellationToken,
294) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
295where
296    S: Service<RoleClient>,
297    T: IntoTransport<RoleClient, E, A>,
298    E: std::error::Error + Send + Sync + 'static,
299{
300    tokio::select! {
301        result = serve_client_with_ct_inner(service, transport.into_transport(), lifecycle, ct.clone()) => { result }
302        _ = ct.cancelled() => {
303            Err(ClientInitializeError::Cancelled)
304        }
305    }
306}
307
308async fn serve_client_with_ct_inner<S, T>(
309    service: S,
310    transport: T,
311    lifecycle: ClientLifecycleMode,
312    ct: CancellationToken,
313) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
314where
315    S: Service<RoleClient>,
316    T: Transport<RoleClient> + 'static,
317{
318    let mut transport = transport.into_transport();
319    let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
320    let (peer, peer_rx) = Peer::new(id_provider.clone(), None);
321    let client_info = service.get_info();
322
323    match lifecycle {
324        ClientLifecycleMode::Initialize => {
325            legacy_startup(&service, &mut transport, &id_provider, &peer, client_info).await?;
326        }
327        ClientLifecycleMode::Discover { preferred_versions } => {
328            discover_startup(
329                &service,
330                &mut transport,
331                &id_provider,
332                &peer,
333                &client_info,
334                preferred_versions,
335            )
336            .await?;
337        }
338        ClientLifecycleMode::Auto {
339            preferred_versions,
340            legacy_version,
341        } => {
342            let discover_result = discover_startup(
343                &service,
344                &mut transport,
345                &id_provider,
346                &peer,
347                &client_info,
348                preferred_versions,
349            )
350            .await;
351            match discover_result {
352                Ok(()) => {}
353                Err(ClientInitializeError::JsonRpcError(error))
354                    if error.code == crate::model::ErrorCode::METHOD_NOT_FOUND =>
355                {
356                    let mut legacy_info = client_info;
357                    if let Some(version) = legacy_version {
358                        legacy_info.protocol_version = version;
359                    }
360                    legacy_startup(&service, &mut transport, &id_provider, &peer, legacy_info)
361                        .await?;
362                }
363                Err(error) => return Err(error),
364            }
365        }
366    }
367    Ok(serve_inner(service, transport, peer, peer_rx, ct))
368}
369
370async fn legacy_startup<S, T>(
371    service: &S,
372    transport: &mut T,
373    id_provider: &Arc<AtomicU32RequestIdProvider>,
374    peer: &Peer<RoleClient>,
375    client_info: ClientInfo,
376) -> Result<(), ClientInitializeError>
377where
378    S: Service<RoleClient>,
379    T: Transport<RoleClient> + 'static,
380{
381    let id = id_provider.next_request_id();
382    let init_request = InitializeRequest {
383        method: Default::default(),
384        params: client_info,
385        extensions: Default::default(),
386    };
387    transport
388        .send(ClientJsonRpcMessage::request(
389            ClientRequest::InitializeRequest(init_request),
390            id.clone(),
391        ))
392        .await
393        .map_err(|error| ClientInitializeError::TransportError {
394            error: DynamicTransportError::new::<T, _>(error),
395            context: "send initialize request".into(),
396        })?;
397
398    let (response, response_id) =
399        expect_response(transport, "initialize response", service, peer.clone()).await?;
400
401    if id != response_id {
402        return Err(ClientInitializeError::ConflictInitResponseId(
403            id,
404            response_id,
405        ));
406    }
407
408    let ServerResult::InitializeResult(initialize_result) = response else {
409        return Err(ClientInitializeError::ExpectedInitResult(Some(response)));
410    };
411    peer.set_peer_info(initialize_result);
412
413    // send notification
414    let notification = ClientJsonRpcMessage::notification(
415        ClientNotification::InitializedNotification(InitializedNotification {
416            method: Default::default(),
417            extensions: Default::default(),
418        }),
419    );
420    transport.send(notification).await.map_err(|error| {
421        ClientInitializeError::transport::<T>(error, "send initialized notification")
422    })?;
423    Ok(())
424}
425
426async fn discover_startup<S, T>(
427    service: &S,
428    transport: &mut T,
429    id_provider: &Arc<AtomicU32RequestIdProvider>,
430    peer: &Peer<RoleClient>,
431    client_info: &ClientInfo,
432    preferred_versions: Vec<ProtocolVersion>,
433) -> Result<(), ClientInitializeError>
434where
435    S: Service<RoleClient>,
436    T: Transport<RoleClient> + 'static,
437{
438    if preferred_versions.is_empty() {
439        return Err(ClientInitializeError::NoPreferredProtocolVersion);
440    }
441
442    let mut attempted = Vec::new();
443    let mut candidate = preferred_versions[0].clone();
444    loop {
445        attempted.push(candidate.clone());
446
447        let meta = RequestMetaObject::with_client_context(
448            candidate.clone(),
449            client_info.client_info.clone(),
450            client_info.capabilities.clone(),
451        );
452        let mut discover = DiscoverRequest::new(DiscoverRequestParams {});
453        discover.extensions.insert(meta);
454        let id = id_provider.next_request_id();
455        transport
456            .send(ClientJsonRpcMessage::request(
457                ClientRequest::DiscoverRequest(discover),
458                id.clone(),
459            ))
460            .await
461            .map_err(|error| {
462                ClientInitializeError::transport::<T>(error, "send discover request")
463            })?;
464
465        match expect_response(transport, "discover response", service, peer.clone()).await {
466            Ok((ServerResult::DiscoverResult(result), response_id)) => {
467                if response_id != id {
468                    return Err(ClientInitializeError::ConflictInitResponseId(
469                        id,
470                        response_id,
471                    ));
472                }
473                let Some(selected) =
474                    select_protocol_version(&preferred_versions, &result.supported_versions)
475                else {
476                    return Err(ClientInitializeError::NoCompatibleProtocolVersion {
477                        client_supported: preferred_versions,
478                        server_supported: result.supported_versions,
479                    });
480                };
481                peer.set_peer_info(ServerInfo {
482                    protocol_version: selected.clone(),
483                    capabilities: result.capabilities,
484                    server_info: result.server_info,
485                    instructions: result.instructions,
486                    meta: result.meta,
487                });
488                peer.set_client_request_metadata(ClientRequestMetadata {
489                    protocol_version: selected,
490                    client_info: client_info.client_info.clone(),
491                    client_capabilities: client_info.capabilities.clone(),
492                });
493                return Ok(());
494            }
495            Ok((response, _)) => {
496                return Err(ClientInitializeError::ExpectedInitResult(Some(response)));
497            }
498            Err(ClientInitializeError::JsonRpcError(error))
499                if error.code == crate::model::ErrorCode::UNSUPPORTED_PROTOCOL_VERSION =>
500            {
501                let supported = error
502                    .data
503                    .as_ref()
504                    .and_then(|data| data.get("supported"))
505                    .cloned()
506                    .and_then(|value| serde_json::from_value::<Vec<ProtocolVersion>>(value).ok())
507                    .unwrap_or_default();
508                let may_retry_current = attempted
509                    .iter()
510                    .filter(|version| *version == &candidate)
511                    .count()
512                    == 1;
513                let next = preferred_versions
514                    .iter()
515                    .find(|version| {
516                        supported.contains(version)
517                            && (!attempted.contains(version)
518                                || (may_retry_current && *version == &candidate))
519                    })
520                    .cloned();
521                let Some(next) = next else {
522                    return Err(ClientInitializeError::NoCompatibleProtocolVersion {
523                        client_supported: preferred_versions,
524                        server_supported: supported,
525                    });
526                };
527                candidate = next;
528            }
529            Err(error) => return Err(error),
530        }
531    }
532}
533
534macro_rules! method {
535    ($(#[$meta:meta])* peer_req $method:ident $Req:ident() => $Resp: ident ) => {
536        $(#[$meta])*
537        pub async fn $method(&self) -> Result<$Resp, ServiceError> {
538            let result = self
539                .send_request(ClientRequest::$Req($Req {
540                    method: Default::default(),
541                }))
542                .await?;
543            match result {
544                ServerResult::$Resp(result) => Ok(result),
545                _ => Err(ServiceError::UnexpectedResponse),
546            }
547        }
548    };
549    ($(#[$meta:meta])* peer_req $method:ident $Req:ident($Param: ident) => $Resp: ident ) => {
550        $(#[$meta])*
551        pub async fn $method(&self, params: $Param) -> Result<$Resp, ServiceError> {
552            let result = self
553                .send_request(ClientRequest::$Req($Req {
554                    method: Default::default(),
555                    params,
556                    extensions: Default::default(),
557                }))
558                .await?;
559            match result {
560                ServerResult::$Resp(result) => Ok(result),
561                _ => Err(ServiceError::UnexpectedResponse),
562            }
563        }
564    };
565    ($(#[$meta:meta])* peer_req $method:ident $Req:ident($Param: ident)? => $Resp: ident ) => {
566        $(#[$meta])*
567        pub async fn $method(&self, params: Option<$Param>) -> Result<$Resp, ServiceError> {
568            let result = self
569                .send_request(ClientRequest::$Req($Req {
570                    method: Default::default(),
571                    params,
572                    extensions: Default::default(),
573                }))
574                .await?;
575            match result {
576                ServerResult::$Resp(result) => Ok(result),
577                _ => Err(ServiceError::UnexpectedResponse),
578            }
579        }
580    };
581    ($(#[$meta:meta])* peer_req $method:ident $Req:ident($Param: ident)) => {
582        $(#[$meta])*
583        pub async fn $method(&self, params: $Param) -> Result<(), ServiceError> {
584            let result = self
585                .send_request(ClientRequest::$Req($Req {
586                    method: Default::default(),
587                    params,
588                    extensions: Default::default(),
589                }))
590                .await?;
591            match result {
592                ServerResult::EmptyResult(_) => Ok(()),
593                _ => Err(ServiceError::UnexpectedResponse),
594            }
595        }
596    };
597
598    ($(#[$meta:meta])* peer_not $method:ident $Not:ident($Param: ident)) => {
599        $(#[$meta])*
600        pub async fn $method(&self, params: $Param) -> Result<(), ServiceError> {
601            self.send_notification(ClientNotification::$Not($Not {
602                method: Default::default(),
603                params,
604                extensions: Default::default(),
605            }))
606            .await?;
607            Ok(())
608        }
609    };
610    ($(#[$meta:meta])* peer_not $method:ident $Not:ident) => {
611        $(#[$meta])*
612        pub async fn $method(&self) -> Result<(), ServiceError> {
613            self.send_notification(ClientNotification::$Not($Not {
614                method: Default::default(),
615                extensions: Default::default(),
616            }))
617            .await?;
618            Ok(())
619        }
620    };
621}
622
623impl Peer<RoleClient> {
624    /// Discover the server's supported protocol versions and capabilities.
625    ///
626    /// The high-level client currently exposes this peer only after initialization;
627    /// pre-initialization probing is planned as follow-up work.
628    pub async fn discover(&self, meta: RequestMetaObject) -> Result<DiscoverResult, ServiceError> {
629        let mut request = DiscoverRequest::new(DiscoverRequestParams {});
630        request.extensions.insert(meta);
631        let result = self
632            .send_request(ClientRequest::DiscoverRequest(request))
633            .await?;
634        match result {
635            ServerResult::DiscoverResult(result) => Ok(result),
636            _ => Err(ServiceError::UnexpectedResponse),
637        }
638    }
639
640    /// Send one `tools/call` request and return either a final result or an MRTR
641    /// `InputRequiredResult` without driving any follow-up rounds.
642    pub async fn call_tool_once(
643        &self,
644        params: CallToolRequestParams,
645    ) -> Result<CallToolResponse, ServiceError> {
646        let result = self
647            .send_request(ClientRequest::CallToolRequest(CallToolRequest {
648                method: Default::default(),
649                params,
650                extensions: Default::default(),
651            }))
652            .await?;
653        match result {
654            ServerResult::CallToolResult(result) => Ok(CallToolResponse::Complete(result)),
655            ServerResult::InputRequiredResult(result) => {
656                Ok(CallToolResponse::InputRequired(result))
657            }
658            _ => Err(ServiceError::UnexpectedResponse),
659        }
660    }
661
662    /// Send one `prompts/get` request and return either a final result or an MRTR
663    /// `InputRequiredResult` without driving any follow-up rounds.
664    pub async fn get_prompt_once(
665        &self,
666        params: GetPromptRequestParams,
667    ) -> Result<GetPromptResponse, ServiceError> {
668        let result = self
669            .send_request(ClientRequest::GetPromptRequest(GetPromptRequest {
670                method: Default::default(),
671                params,
672                extensions: Default::default(),
673            }))
674            .await?;
675        match result {
676            ServerResult::GetPromptResult(result) => Ok(GetPromptResponse::Complete(result)),
677            ServerResult::InputRequiredResult(result) => {
678                Ok(GetPromptResponse::InputRequired(result))
679            }
680            _ => Err(ServiceError::UnexpectedResponse),
681        }
682    }
683
684    /// Send one `resources/read` request and return either a final result or an
685    /// MRTR `InputRequiredResult` without driving any follow-up rounds.
686    pub async fn read_resource_once(
687        &self,
688        params: ReadResourceRequestParams,
689    ) -> Result<ReadResourceResponse, ServiceError> {
690        let result = self
691            .send_request(ClientRequest::ReadResourceRequest(ReadResourceRequest {
692                method: Default::default(),
693                params,
694                extensions: Default::default(),
695            }))
696            .await?;
697        match result {
698            ServerResult::ReadResourceResult(result) => Ok(ReadResourceResponse::Complete(result)),
699            ServerResult::InputRequiredResult(result) => {
700                Ok(ReadResourceResponse::InputRequired(result))
701            }
702            _ => Err(ServiceError::UnexpectedResponse),
703        }
704    }
705
706    method!(peer_req complete CompleteRequest(CompleteRequestParams) => CompleteResult);
707    method!(
708        #[deprecated(
709            since = "1.8.0",
710            note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
711        )]
712        peer_req set_level SetLevelRequest(SetLevelRequestParams)
713    );
714    method!(peer_req get_prompt GetPromptRequest(GetPromptRequestParams) => GetPromptResult);
715    method!(peer_req list_prompts ListPromptsRequest(PaginatedRequestParams)? => ListPromptsResult);
716    method!(peer_req list_resources ListResourcesRequest(PaginatedRequestParams)? => ListResourcesResult);
717    method!(peer_req list_resource_templates ListResourceTemplatesRequest(PaginatedRequestParams)? => ListResourceTemplatesResult);
718    method!(peer_req read_resource ReadResourceRequest(ReadResourceRequestParams) => ReadResourceResult);
719    method!(peer_req subscribe SubscribeRequest(SubscribeRequestParams) );
720    method!(peer_req unsubscribe UnsubscribeRequest(UnsubscribeRequestParams));
721    method!(peer_req call_tool CallToolRequest(CallToolRequestParams) => CallToolResult);
722    method!(peer_req list_tools ListToolsRequest(PaginatedRequestParams)? => ListToolsResult);
723
724    method!(peer_not notify_cancelled CancelledNotification(CancelledNotificationParam));
725    method!(peer_not notify_progress ProgressNotification(ProgressNotificationParam));
726    method!(peer_not notify_initialized InitializedNotification);
727    method!(peer_not notify_roots_list_changed RootsListChangedNotification);
728}
729
730impl Peer<RoleClient> {
731    /// A wrapper method for [`Peer<RoleClient>::list_tools`].
732    ///
733    /// This function will call [`Peer<RoleClient>::list_tools`] multiple times until all tools are listed.
734    pub async fn list_all_tools(&self) -> Result<Vec<crate::model::Tool>, ServiceError> {
735        let mut tools = Vec::new();
736        let mut cursor = None;
737        loop {
738            let result = self
739                .list_tools(Some(PaginatedRequestParams { meta: None, cursor }))
740                .await?;
741            tools.extend(result.tools);
742            cursor = result.next_cursor;
743            if cursor.is_none() {
744                break;
745            }
746        }
747        Ok(tools)
748    }
749
750    /// A wrapper method for [`Peer<RoleClient>::list_prompts`].
751    ///
752    /// This function will call [`Peer<RoleClient>::list_prompts`] multiple times until all prompts are listed.
753    pub async fn list_all_prompts(&self) -> Result<Vec<crate::model::Prompt>, ServiceError> {
754        let mut prompts = Vec::new();
755        let mut cursor = None;
756        loop {
757            let result = self
758                .list_prompts(Some(PaginatedRequestParams { meta: None, cursor }))
759                .await?;
760            prompts.extend(result.prompts);
761            cursor = result.next_cursor;
762            if cursor.is_none() {
763                break;
764            }
765        }
766        Ok(prompts)
767    }
768
769    /// A wrapper method for [`Peer<RoleClient>::list_resources`].
770    ///
771    /// This function will call [`Peer<RoleClient>::list_resources`] multiple times until all resources are listed.
772    pub async fn list_all_resources(&self) -> Result<Vec<crate::model::Resource>, ServiceError> {
773        let mut resources = Vec::new();
774        let mut cursor = None;
775        loop {
776            let result = self
777                .list_resources(Some(PaginatedRequestParams { meta: None, cursor }))
778                .await?;
779            resources.extend(result.resources);
780            cursor = result.next_cursor;
781            if cursor.is_none() {
782                break;
783            }
784        }
785        Ok(resources)
786    }
787
788    /// A wrapper method for [`Peer<RoleClient>::list_resource_templates`].
789    ///
790    /// This function will call [`Peer<RoleClient>::list_resource_templates`] multiple times until all resource templates are listed.
791    pub async fn list_all_resource_templates(
792        &self,
793    ) -> Result<Vec<crate::model::ResourceTemplate>, ServiceError> {
794        let mut resource_templates = Vec::new();
795        let mut cursor = None;
796        loop {
797            let result = self
798                .list_resource_templates(Some(PaginatedRequestParams { meta: None, cursor }))
799                .await?;
800            resource_templates.extend(result.resource_templates);
801            cursor = result.next_cursor;
802            if cursor.is_none() {
803                break;
804            }
805        }
806        Ok(resource_templates)
807    }
808
809    /// Convenient method to get completion suggestions for a prompt argument
810    ///
811    /// # Arguments
812    /// * `prompt_name` - Name of the prompt being completed
813    /// * `argument_name` - Name of the argument being completed  
814    /// * `current_value` - Current partial value of the argument
815    /// * `context` - Optional context with previously resolved arguments
816    ///
817    /// # Returns
818    /// CompletionInfo with suggestions for the specified prompt argument
819    pub async fn complete_prompt_argument(
820        &self,
821        prompt_name: impl Into<String>,
822        argument_name: impl Into<String>,
823        current_value: impl Into<String>,
824        context: Option<CompletionContext>,
825    ) -> Result<CompletionInfo, ServiceError> {
826        let request = CompleteRequestParams {
827            meta: None,
828            r#ref: Reference::for_prompt(prompt_name),
829            argument: ArgumentInfo {
830                name: argument_name.into(),
831                value: current_value.into(),
832            },
833            context,
834        };
835
836        let result = self.complete(request).await?;
837        Ok(result.completion)
838    }
839
840    /// Convenient method to get completion suggestions for a resource URI argument
841    ///
842    /// # Arguments
843    /// * `uri_template` - URI template pattern being completed
844    /// * `argument_name` - Name of the URI parameter being completed
845    /// * `current_value` - Current partial value of the parameter
846    /// * `context` - Optional context with previously resolved arguments
847    ///
848    /// # Returns
849    /// CompletionInfo with suggestions for the specified resource URI argument
850    pub async fn complete_resource_argument(
851        &self,
852        uri_template: impl Into<String>,
853        argument_name: impl Into<String>,
854        current_value: impl Into<String>,
855        context: Option<CompletionContext>,
856    ) -> Result<CompletionInfo, ServiceError> {
857        let request = CompleteRequestParams {
858            meta: None,
859            r#ref: Reference::for_resource(uri_template),
860            argument: ArgumentInfo {
861                name: argument_name.into(),
862                value: current_value.into(),
863            },
864            context,
865        };
866
867        let result = self.complete(request).await?;
868        Ok(result.completion)
869    }
870
871    /// Simple completion for a prompt argument without context
872    ///
873    /// This is a convenience wrapper around `complete_prompt_argument` for
874    /// simple completion scenarios that don't require context awareness.
875    pub async fn complete_prompt_simple(
876        &self,
877        prompt_name: impl Into<String>,
878        argument_name: impl Into<String>,
879        current_value: impl Into<String>,
880    ) -> Result<Vec<String>, ServiceError> {
881        let completion = self
882            .complete_prompt_argument(prompt_name, argument_name, current_value, None)
883            .await?;
884        Ok(completion.values)
885    }
886
887    /// Simple completion for a resource URI argument without context
888    ///
889    /// This is a convenience wrapper around `complete_resource_argument` for
890    /// simple completion scenarios that don't require context awareness.
891    pub async fn complete_resource_simple(
892        &self,
893        uri_template: impl Into<String>,
894        argument_name: impl Into<String>,
895        current_value: impl Into<String>,
896    ) -> Result<Vec<String>, ServiceError> {
897        let completion = self
898            .complete_resource_argument(uri_template, argument_name, current_value, None)
899            .await?;
900        Ok(completion.values)
901    }
902}
903
904impl<S> RunningService<RoleClient, S>
905where
906    S: Service<RoleClient>,
907{
908    /// Send one `tools/call` request without driving MRTR follow-up rounds.
909    pub async fn call_tool_once(
910        &self,
911        params: CallToolRequestParams,
912    ) -> Result<CallToolResponse, ServiceError> {
913        self.peer.call_tool_once(params).await
914    }
915
916    /// Send one `prompts/get` request without driving MRTR follow-up rounds.
917    pub async fn get_prompt_once(
918        &self,
919        params: GetPromptRequestParams,
920    ) -> Result<GetPromptResponse, ServiceError> {
921        self.peer.get_prompt_once(params).await
922    }
923
924    /// Send one `resources/read` request without driving MRTR follow-up rounds.
925    pub async fn read_resource_once(
926        &self,
927        params: ReadResourceRequestParams,
928    ) -> Result<ReadResourceResponse, ServiceError> {
929        self.peer.read_resource_once(params).await
930    }
931
932    /// High-level `tools/call` helper that automatically fulfils SEP-2322
933    /// `input_required` rounds through the local [`ClientHandler`](crate::ClientHandler) service.
934    ///
935    /// # Errors
936    ///
937    /// Returns [`ServiceError::InputRequiredRoundsExceeded`] if the peer does
938    /// not produce a final [`CallToolResult`] within the default MRTR round cap.
939    /// Other transport, protocol, and local input-handler errors are propagated.
940    pub async fn call_tool(
941        &self,
942        params: CallToolRequestParams,
943    ) -> Result<CallToolResult, ServiceError> {
944        self.call_tool_with_mrtr_max_rounds(params, DEFAULT_MRTR_MAX_ROUNDS)
945            .await
946    }
947
948    /// Same as [`Self::call_tool`], with an explicit MRTR round cap.
949    ///
950    /// # Errors
951    ///
952    /// Returns [`ServiceError::InputRequiredRoundsExceeded`] once `max_rounds`
953    /// `input_required` responses have been driven without receiving a final
954    /// [`CallToolResult`]. Other transport, protocol, and local input-handler
955    /// errors are propagated.
956    pub async fn call_tool_with_mrtr_max_rounds(
957        &self,
958        mut params: CallToolRequestParams,
959        max_rounds: usize,
960    ) -> Result<CallToolResult, ServiceError> {
961        let mut state_only_rounds = 0usize;
962        for _round in 0..max_rounds {
963            match self.peer.call_tool_once(params.clone()).await? {
964                CallToolResponse::Complete(result) => return Ok(result),
965                CallToolResponse::InputRequired(result) => {
966                    let (input_responses, request_state) = self
967                        .prepare_input_required_retry(result, &mut state_only_rounds)
968                        .await?;
969                    params.input_responses = input_responses;
970                    params.request_state = request_state;
971                }
972            }
973        }
974        Err(ServiceError::InputRequiredRoundsExceeded { max_rounds })
975    }
976
977    /// High-level `prompts/get` helper that automatically fulfils SEP-2322
978    /// `input_required` rounds through the local [`ClientHandler`](crate::ClientHandler) service.
979    ///
980    /// # Errors
981    ///
982    /// Returns [`ServiceError::InputRequiredRoundsExceeded`] if the peer does
983    /// not produce a final [`GetPromptResult`] within the default MRTR round cap.
984    /// Other transport, protocol, and local input-handler errors are propagated.
985    pub async fn get_prompt(
986        &self,
987        params: GetPromptRequestParams,
988    ) -> Result<GetPromptResult, ServiceError> {
989        self.get_prompt_with_mrtr_max_rounds(params, DEFAULT_MRTR_MAX_ROUNDS)
990            .await
991    }
992
993    /// Same as [`Self::get_prompt`], with an explicit MRTR round cap.
994    ///
995    /// # Errors
996    ///
997    /// Returns [`ServiceError::InputRequiredRoundsExceeded`] once `max_rounds`
998    /// `input_required` responses have been driven without receiving a final
999    /// [`GetPromptResult`]. Other transport, protocol, and local input-handler
1000    /// errors are propagated.
1001    pub async fn get_prompt_with_mrtr_max_rounds(
1002        &self,
1003        mut params: GetPromptRequestParams,
1004        max_rounds: usize,
1005    ) -> Result<GetPromptResult, ServiceError> {
1006        let mut state_only_rounds = 0usize;
1007        for _round in 0..max_rounds {
1008            match self.peer.get_prompt_once(params.clone()).await? {
1009                GetPromptResponse::Complete(result) => return Ok(result),
1010                GetPromptResponse::InputRequired(result) => {
1011                    let (input_responses, request_state) = self
1012                        .prepare_input_required_retry(result, &mut state_only_rounds)
1013                        .await?;
1014                    params.input_responses = input_responses;
1015                    params.request_state = request_state;
1016                }
1017            }
1018        }
1019        Err(ServiceError::InputRequiredRoundsExceeded { max_rounds })
1020    }
1021
1022    /// High-level `resources/read` helper that automatically fulfils SEP-2322
1023    /// `input_required` rounds through the local [`ClientHandler`](crate::ClientHandler) service.
1024    ///
1025    /// # Errors
1026    ///
1027    /// Returns [`ServiceError::InputRequiredRoundsExceeded`] if the peer does
1028    /// not produce a final [`ReadResourceResult`] within the default MRTR round
1029    /// cap. Other transport, protocol, and local input-handler errors are
1030    /// propagated.
1031    pub async fn read_resource(
1032        &self,
1033        params: ReadResourceRequestParams,
1034    ) -> Result<ReadResourceResult, ServiceError> {
1035        self.read_resource_with_mrtr_max_rounds(params, DEFAULT_MRTR_MAX_ROUNDS)
1036            .await
1037    }
1038
1039    /// Same as [`Self::read_resource`], with an explicit MRTR round cap.
1040    ///
1041    /// # Errors
1042    ///
1043    /// Returns [`ServiceError::InputRequiredRoundsExceeded`] once `max_rounds`
1044    /// `input_required` responses have been driven without receiving a final
1045    /// [`ReadResourceResult`]. Other transport, protocol, and local input-handler
1046    /// errors are propagated.
1047    pub async fn read_resource_with_mrtr_max_rounds(
1048        &self,
1049        mut params: ReadResourceRequestParams,
1050        max_rounds: usize,
1051    ) -> Result<ReadResourceResult, ServiceError> {
1052        let mut state_only_rounds = 0usize;
1053        for _round in 0..max_rounds {
1054            match self.peer.read_resource_once(params.clone()).await? {
1055                ReadResourceResponse::Complete(result) => return Ok(result),
1056                ReadResourceResponse::InputRequired(result) => {
1057                    let (input_responses, request_state) = self
1058                        .prepare_input_required_retry(result, &mut state_only_rounds)
1059                        .await?;
1060                    params.input_responses = input_responses;
1061                    params.request_state = request_state;
1062                }
1063            }
1064        }
1065        Err(ServiceError::InputRequiredRoundsExceeded { max_rounds })
1066    }
1067
1068    async fn prepare_input_required_retry(
1069        &self,
1070        result: InputRequiredResult,
1071        state_only_rounds: &mut usize,
1072    ) -> Result<(Option<InputResponses>, Option<String>), ServiceError> {
1073        let had_input_requests = result
1074            .input_requests
1075            .as_ref()
1076            .is_some_and(|requests| !requests.is_empty());
1077        if !had_input_requests && result.request_state.is_none() {
1078            return Err(ServiceError::UnexpectedResponse);
1079        }
1080
1081        let responses = self
1082            .fulfill_input_requests(result.input_requests.unwrap_or_default())
1083            .await?;
1084        if had_input_requests {
1085            *state_only_rounds = 0;
1086        } else {
1087            Self::sleep_state_only_round(*state_only_rounds).await;
1088            *state_only_rounds += 1;
1089        }
1090
1091        Ok((
1092            (!responses.is_empty()).then_some(responses),
1093            result.request_state,
1094        ))
1095    }
1096
1097    async fn fulfill_input_requests(
1098        &self,
1099        requests: crate::model::InputRequests,
1100    ) -> Result<InputResponses, ServiceError> {
1101        let responses = futures::future::try_join_all(
1102            requests
1103                .into_iter()
1104                .map(|(key, request)| self.fulfill_input_request(key, request)),
1105        )
1106        .await?;
1107        Ok(responses.into_iter().collect())
1108    }
1109
1110    async fn fulfill_input_request(
1111        &self,
1112        key: String,
1113        request: InputRequest,
1114    ) -> Result<(String, serde_json::Value), ServiceError> {
1115        let response = match request {
1116            InputRequest::CreateMessage(request) => {
1117                let mut request = ServerRequest::CreateMessageRequest(request);
1118                let context = self.input_request_context(&key, &mut request);
1119                match self
1120                    .service
1121                    .handle_request(request, context)
1122                    .await
1123                    .map_err(ServiceError::McpError)?
1124                {
1125                    ClientResult::CreateMessageResult(result) => {
1126                        serde_json::to_value(result).map_err(Self::serde_to_service_error)?
1127                    }
1128                    _ => return Err(ServiceError::UnexpectedResponse),
1129                }
1130            }
1131            InputRequest::Elicitation(request) => {
1132                let mut request = ServerRequest::ElicitRequest(request);
1133                let context = self.input_request_context(&key, &mut request);
1134                match self
1135                    .service
1136                    .handle_request(request, context)
1137                    .await
1138                    .map_err(ServiceError::McpError)?
1139                {
1140                    ClientResult::ElicitResult(result) => {
1141                        serde_json::to_value(result).map_err(Self::serde_to_service_error)?
1142                    }
1143                    _ => return Err(ServiceError::UnexpectedResponse),
1144                }
1145            }
1146            InputRequest::ListRoots(request) => {
1147                let mut request = ServerRequest::ListRootsRequest(request);
1148                let context = self.input_request_context(&key, &mut request);
1149                match self
1150                    .service
1151                    .handle_request(request, context)
1152                    .await
1153                    .map_err(ServiceError::McpError)?
1154                {
1155                    ClientResult::ListRootsResult(result) => {
1156                        serde_json::to_value(result).map_err(Self::serde_to_service_error)?
1157                    }
1158                    _ => return Err(ServiceError::UnexpectedResponse),
1159                }
1160            }
1161        };
1162        Ok((key, response))
1163    }
1164
1165    fn input_request_context<T>(&self, key: &str, request: &mut T) -> RequestContext<RoleClient>
1166    where
1167        T: GetMeta<Metadata = crate::model::RequestMetaObject> + GetExtensions,
1168    {
1169        let mut meta = Default::default();
1170        let mut extensions = Default::default();
1171        std::mem::swap(&mut meta, request.get_meta_mut());
1172        std::mem::swap(&mut extensions, request.extensions_mut());
1173        RequestContext {
1174            ct: tokio_util::sync::CancellationToken::new(),
1175            id: NumberOrString::String(Arc::from(key)),
1176            peer: self.peer.clone(),
1177            meta,
1178            extensions,
1179        }
1180    }
1181
1182    async fn sleep_state_only_round(state_only_rounds: usize) {
1183        let millis = (50u64.saturating_mul(1_u64 << state_only_rounds.min(3))).min(250);
1184        tokio::time::sleep(Duration::from_millis(millis)).await;
1185    }
1186
1187    fn serde_to_service_error(error: serde_json::Error) -> ServiceError {
1188        ServiceError::McpError(ErrorData::internal_error(
1189            format!("failed to serialize MRTR input response: {error}"),
1190            None,
1191        ))
1192    }
1193}