1use std::sync::OnceLock;
2
3use futures::FutureExt;
4#[cfg(not(feature = "local"))]
5use futures::future::BoxFuture;
6#[cfg(feature = "local")]
7use futures::future::LocalBoxFuture;
8use thiserror::Error;
9
10#[cfg(not(feature = "local"))]
19#[doc(hidden)]
20pub trait MaybeSend: Send + Sync {}
21#[cfg(not(feature = "local"))]
22impl<T: Send + Sync> MaybeSend for T {}
23
24#[cfg(feature = "local")]
25#[doc(hidden)]
26pub trait MaybeSend {}
27#[cfg(feature = "local")]
28impl<T> MaybeSend for T {}
29
30#[cfg(not(feature = "local"))]
31#[doc(hidden)]
32pub trait MaybeSendFuture: Send {}
33#[cfg(not(feature = "local"))]
34impl<T: Send> MaybeSendFuture for T {}
35
36#[cfg(feature = "local")]
37#[doc(hidden)]
38pub trait MaybeSendFuture {}
39#[cfg(feature = "local")]
40impl<T> MaybeSendFuture for T {}
41
42#[cfg(not(feature = "local"))]
43pub(crate) type MaybeBoxFuture<'a, T> = BoxFuture<'a, T>;
44#[cfg(feature = "local")]
45pub(crate) type MaybeBoxFuture<'a, T> = LocalBoxFuture<'a, T>;
46
47#[cfg(feature = "server")]
48use crate::model::ClientNotification;
49#[cfg(feature = "server")]
50use crate::model::ServerJsonRpcMessage;
51#[cfg(feature = "client")]
52use crate::model::ServerNotification;
53use crate::{
54 error::ErrorData as McpError,
55 model::{
56 CancelledNotification, CancelledNotificationParam, ClientCapabilities, Extensions,
57 GetExtensions, GetMeta, Implementation, JsonRpcError, JsonRpcMessage, JsonRpcNotification,
58 JsonRpcRequest, JsonRpcResponse, NotificationMetaObject, NumberOrString, ProgressToken,
59 ProtocolVersion, RequestId, RequestMetaObject,
60 },
61 transport::{DynamicTransportError, IntoTransport, Transport},
62};
63#[cfg(feature = "client")]
64mod client;
65#[cfg(feature = "client")]
66pub use client::*;
67#[cfg(feature = "server")]
68mod server;
69#[cfg(feature = "server")]
70pub use server::*;
71#[cfg(feature = "tower")]
72mod tower;
73use tokio_util::sync::{CancellationToken, DropGuard};
74#[cfg(feature = "tower")]
75pub use tower::*;
76use tracing::{Instrument as _, instrument};
77#[derive(Error, Debug)]
78#[non_exhaustive]
79pub enum ServiceError {
80 #[error("Mcp error: {0}")]
81 McpError(McpError),
82 #[error("Transport send error: {0}")]
83 TransportSend(DynamicTransportError),
84 #[error("Transport closed")]
85 TransportClosed,
86 #[error("Unexpected response type")]
87 UnexpectedResponse,
88 #[error("task cancelled for reason {}", reason.as_deref().unwrap_or("<unknown>"))]
89 Cancelled { reason: Option<String> },
90 #[error("request timeout after {}", chrono::Duration::from_std(*timeout).unwrap_or_default())]
91 Timeout { timeout: Duration },
92 #[error("input_required did not complete within {max_rounds} MRTR rounds")]
94 InputRequiredRoundsExceeded { max_rounds: usize },
95}
96
97trait TransferObject:
98 std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static
99{
100}
101
102impl<T> TransferObject for T where
103 T: std::fmt::Debug
104 + serde::Serialize
105 + serde::de::DeserializeOwned
106 + Send
107 + Sync
108 + 'static
109 + Clone
110{
111}
112
113#[allow(private_bounds, reason = "there's no the third implementation")]
114pub trait ServiceRole: std::fmt::Debug + Send + Sync + 'static + Copy + Clone {
115 type Req: TransferObject + GetMeta<Metadata = RequestMetaObject> + GetExtensions;
116 type Resp: TransferObject;
117 type Not: TryInto<CancelledNotification, Error = Self::Not>
118 + From<CancelledNotification>
119 + TransferObject;
120 type PeerReq: TransferObject + GetMeta<Metadata = RequestMetaObject> + GetExtensions;
121 type PeerResp: TransferObject;
122 type PeerNot: TryInto<CancelledNotification, Error = Self::PeerNot>
123 + From<CancelledNotification>
124 + TransferObject
125 + GetMeta<Metadata = NotificationMetaObject>
126 + GetExtensions;
127 type InitializeError;
128 const IS_CLIENT: bool;
129 type Info: TransferObject;
130 type PeerInfo: TransferObject;
131}
132
133pub type TxJsonRpcMessage<R> =
134 JsonRpcMessage<<R as ServiceRole>::Req, <R as ServiceRole>::Resp, <R as ServiceRole>::Not>;
135pub type RxJsonRpcMessage<R> = JsonRpcMessage<
136 <R as ServiceRole>::PeerReq,
137 <R as ServiceRole>::PeerResp,
138 <R as ServiceRole>::PeerNot,
139>;
140
141#[cfg(not(feature = "local"))]
142pub trait Service<R: ServiceRole>: Send + Sync + 'static {
143 fn handle_request(
144 &self,
145 request: R::PeerReq,
146 context: RequestContext<R>,
147 ) -> impl Future<Output = Result<R::Resp, McpError>> + MaybeSendFuture + '_;
148 fn handle_notification(
149 &self,
150 notification: R::PeerNot,
151 context: NotificationContext<R>,
152 ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_;
153 fn get_info(&self) -> R::Info;
154}
155
156#[cfg(feature = "local")]
157pub trait Service<R: ServiceRole>: 'static {
158 fn handle_request(
159 &self,
160 request: R::PeerReq,
161 context: RequestContext<R>,
162 ) -> impl Future<Output = Result<R::Resp, McpError>> + MaybeSendFuture + '_;
163 fn handle_notification(
164 &self,
165 notification: R::PeerNot,
166 context: NotificationContext<R>,
167 ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_;
168 fn get_info(&self) -> R::Info;
169}
170
171pub trait ServiceExt<R: ServiceRole>: Service<R> + Sized {
172 fn into_dyn(self) -> Box<dyn DynService<R>> {
176 Box::new(self)
177 }
178 fn serve<T, E, A>(
179 self,
180 transport: T,
181 ) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError>> + MaybeSendFuture
182 where
183 T: IntoTransport<R, E, A>,
184 E: std::error::Error + Send + Sync + 'static,
185 Self: Sized,
186 {
187 Self::serve_with_ct(self, transport, Default::default())
188 }
189 fn serve_with_ct<T, E, A>(
190 self,
191 transport: T,
192 ct: CancellationToken,
193 ) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError>> + MaybeSendFuture
194 where
195 T: IntoTransport<R, E, A>,
196 E: std::error::Error + Send + Sync + 'static,
197 Self: Sized;
198}
199
200impl<R: ServiceRole> Service<R> for Box<dyn DynService<R>> {
201 fn handle_request(
202 &self,
203 request: R::PeerReq,
204 context: RequestContext<R>,
205 ) -> impl Future<Output = Result<R::Resp, McpError>> + MaybeSendFuture + '_ {
206 DynService::handle_request(self.as_ref(), request, context)
207 }
208
209 fn handle_notification(
210 &self,
211 notification: R::PeerNot,
212 context: NotificationContext<R>,
213 ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
214 DynService::handle_notification(self.as_ref(), notification, context)
215 }
216
217 fn get_info(&self) -> R::Info {
218 DynService::get_info(self.as_ref())
219 }
220}
221
222#[cfg(not(feature = "local"))]
223pub trait DynService<R: ServiceRole>: Send + Sync {
224 fn handle_request(
225 &self,
226 request: R::PeerReq,
227 context: RequestContext<R>,
228 ) -> MaybeBoxFuture<'_, Result<R::Resp, McpError>>;
229 fn handle_notification(
230 &self,
231 notification: R::PeerNot,
232 context: NotificationContext<R>,
233 ) -> MaybeBoxFuture<'_, Result<(), McpError>>;
234 fn get_info(&self) -> R::Info;
235}
236
237#[cfg(feature = "local")]
238pub trait DynService<R: ServiceRole> {
239 fn handle_request(
240 &self,
241 request: R::PeerReq,
242 context: RequestContext<R>,
243 ) -> MaybeBoxFuture<'_, Result<R::Resp, McpError>>;
244 fn handle_notification(
245 &self,
246 notification: R::PeerNot,
247 context: NotificationContext<R>,
248 ) -> MaybeBoxFuture<'_, Result<(), McpError>>;
249 fn get_info(&self) -> R::Info;
250}
251
252impl<R: ServiceRole, S: Service<R>> DynService<R> for S {
253 fn handle_request(
254 &self,
255 request: R::PeerReq,
256 context: RequestContext<R>,
257 ) -> MaybeBoxFuture<'_, Result<R::Resp, McpError>> {
258 Box::pin(self.handle_request(request, context))
259 }
260 fn handle_notification(
261 &self,
262 notification: R::PeerNot,
263 context: NotificationContext<R>,
264 ) -> MaybeBoxFuture<'_, Result<(), McpError>> {
265 Box::pin(self.handle_notification(notification, context))
266 }
267 fn get_info(&self) -> R::Info {
268 self.get_info()
269 }
270}
271
272use std::{
273 collections::{HashMap, VecDeque},
274 ops::Deref,
275 sync::{Arc, atomic::AtomicU64},
276 time::Duration,
277};
278
279use tokio::sync::mpsc;
280
281pub trait RequestIdProvider: Send + Sync + 'static {
282 fn next_request_id(&self) -> RequestId;
283}
284
285pub trait ProgressTokenProvider: Send + Sync + 'static {
286 fn next_progress_token(&self) -> ProgressToken;
287}
288
289pub type AtomicU32RequestIdProvider = AtomicU32Provider;
290pub type AtomicU32ProgressTokenProvider = AtomicU32Provider;
291
292#[derive(Debug, Default)]
293pub struct AtomicU32Provider {
294 id: AtomicU64,
295}
296
297impl RequestIdProvider for AtomicU32Provider {
298 fn next_request_id(&self) -> RequestId {
299 let id = self.id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
300 RequestId::Number(id as i64)
302 }
303}
304
305impl ProgressTokenProvider for AtomicU32Provider {
306 fn next_progress_token(&self) -> ProgressToken {
307 let id = self.id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
308 ProgressToken(NumberOrString::Number(id as i64))
309 }
310}
311
312#[doc(hidden)]
313pub trait ProgressNotificationToken {
314 fn progress_token(&self) -> Option<&ProgressToken>;
315}
316
317#[cfg(feature = "server")]
318impl ProgressNotificationToken for ClientNotification {
319 fn progress_token(&self) -> Option<&ProgressToken> {
320 match self {
321 ClientNotification::ProgressNotification(notification) => {
322 Some(¬ification.params.progress_token)
323 }
324 _ => None,
325 }
326 }
327}
328
329#[cfg(feature = "client")]
330impl ProgressNotificationToken for ServerNotification {
331 fn progress_token(&self) -> Option<&ProgressToken> {
332 match self {
333 ServerNotification::ProgressNotification(notification) => {
334 Some(¬ification.params.progress_token)
335 }
336 _ => None,
337 }
338 }
339}
340
341type Responder<T> = tokio::sync::oneshot::Sender<T>;
342type ProgressTimeoutWatchers = Arc<tokio::sync::RwLock<HashMap<ProgressToken, mpsc::Sender<()>>>>;
343
344#[derive(Debug)]
350#[non_exhaustive]
351pub struct RequestHandle<R: ServiceRole> {
352 pub rx: tokio::sync::oneshot::Receiver<Result<R::PeerResp, ServiceError>>,
353 pub options: PeerRequestOptions,
354 pub peer: Peer<R>,
355 pub id: RequestId,
356 pub progress_token: ProgressToken,
357 progress_reset_rx: Option<mpsc::Receiver<()>>,
358}
359
360impl<R: ServiceRole> RequestHandle<R> {
361 pub const REQUEST_TIMEOUT_REASON: &str = "request timeout";
362 pub const REQUEST_MAX_TOTAL_TIMEOUT_REASON: &str = "maximum total timeout exceeded";
363
364 pub async fn await_response(mut self) -> Result<R::PeerResp, ServiceError> {
365 let timeout = self.options.timeout;
366 let max_total_timeout = self.options.max_total_timeout;
367 let reset_timeout_on_progress = self.options.reset_timeout_on_progress;
368
369 let has_progress_reset_rx = self.progress_reset_rx.is_some();
370 let progress_token = self.progress_token.clone();
371
372 let result = match (timeout, max_total_timeout, reset_timeout_on_progress) {
373 (Some(timeout), None, false) => match tokio::time::timeout(timeout, &mut self.rx).await
374 {
375 Ok(response) => response.map_err(|_e| ServiceError::TransportClosed)?,
376 Err(_) => {
377 let error = Err(ServiceError::Timeout { timeout });
378 self.send_timeout_cancel_notification(Self::REQUEST_TIMEOUT_REASON)
380 .await;
381 error
382 }
383 },
384 (None, None, _) => (&mut self.rx)
385 .await
386 .map_err(|_e| ServiceError::TransportClosed)?,
387 _ => {
388 self.await_response_with_progress_timeout(
389 timeout,
390 max_total_timeout,
391 reset_timeout_on_progress,
392 )
393 .await
394 }
395 };
396
397 Self::cleanup_progress_timeout_watcher(
398 &self.peer.progress_timeout_watchers,
399 &progress_token,
400 has_progress_reset_rx,
401 )
402 .await;
403 result
404 }
405
406 async fn send_timeout_cancel_notification(&self, reason: &str) {
407 let notification = CancelledNotification {
408 params: CancelledNotificationParam {
409 request_id: Some(self.id.clone()),
410 reason: Some(reason.to_owned()),
411 meta: None,
412 },
413 method: crate::model::CancelledNotificationMethod,
414 extensions: Default::default(),
415 };
416 let _ = self.peer.send_notification(notification.into()).await;
417 }
418
419 async fn await_response_with_progress_timeout(
420 &mut self,
421 timeout: Option<Duration>,
422 max_total_timeout: Option<Duration>,
423 reset_timeout_on_progress: bool,
424 ) -> Result<R::PeerResp, ServiceError> {
425 let mut idle_sleep =
426 timeout.map(|timeout| (timeout, Box::pin(tokio::time::sleep(timeout))));
427 let mut max_total_sleep =
428 max_total_timeout.map(|timeout| (timeout, Box::pin(tokio::time::sleep(timeout))));
429
430 loop {
431 tokio::select! {
432 biased;
433
434 response = &mut self.rx => {
435 return response.map_err(|_e| ServiceError::TransportClosed)?;
436 }
437 _ = async {
438 if let Some((_, sleep)) = idle_sleep.as_mut() {
439 sleep.as_mut().await;
440 }
441 }, if idle_sleep.is_some() => {
442 if let Some((timeout, _)) = idle_sleep.as_ref() {
443 self.send_timeout_cancel_notification(Self::REQUEST_TIMEOUT_REASON).await;
444 return Err(ServiceError::Timeout { timeout: *timeout });
445 }
446 }
447 _ = async {
448 if let Some((_, sleep)) = max_total_sleep.as_mut() {
449 sleep.as_mut().await;
450 }
451 }, if max_total_sleep.is_some() => {
452 if let Some((timeout, _)) = max_total_sleep.as_ref() {
453 self.send_timeout_cancel_notification(Self::REQUEST_MAX_TOTAL_TIMEOUT_REASON).await;
454 return Err(ServiceError::Timeout { timeout: *timeout });
455 }
456 }
457 progress = async {
458 match self.progress_reset_rx.as_mut() {
459 Some(rx) => rx.recv().await,
460 None => None,
461 }
462 }, if reset_timeout_on_progress && idle_sleep.is_some() && self.progress_reset_rx.is_some() => {
463 if progress.is_some() {
464 if let Some((timeout, sleep)) = idle_sleep.as_mut() {
465 sleep.as_mut().reset(tokio::time::Instant::now() + *timeout);
466 }
467 }
468 }
469 }
470 }
471 }
472
473 pub async fn cancel(self, reason: Option<String>) -> Result<(), ServiceError> {
475 Self::cleanup_progress_timeout_watcher(
476 &self.peer.progress_timeout_watchers,
477 &self.progress_token,
478 self.progress_reset_rx.is_some(),
479 )
480 .await;
481 let notification = CancelledNotification {
482 params: CancelledNotificationParam {
483 request_id: Some(self.id),
484 reason,
485 meta: None,
486 },
487 method: crate::model::CancelledNotificationMethod,
488 extensions: Default::default(),
489 };
490 self.peer.send_notification(notification.into()).await?;
491 Ok(())
492 }
493
494 async fn cleanup_progress_timeout_watcher(
495 progress_timeout_watchers: &ProgressTimeoutWatchers,
496 progress_token: &ProgressToken,
497 has_progress_reset_rx: bool,
498 ) {
499 if has_progress_reset_rx {
500 progress_timeout_watchers
501 .write()
502 .await
503 .remove(progress_token);
504 }
505 }
506}
507
508#[derive(Debug)]
509pub(crate) enum PeerSinkMessage<R: ServiceRole> {
510 Request {
511 request: R::Req,
512 id: RequestId,
513 responder: Responder<Result<R::PeerResp, ServiceError>>,
514 },
515 Notification {
516 notification: R::Not,
517 responder: Responder<Result<(), ServiceError>>,
518 },
519}
520
521#[derive(Debug, Clone)]
522pub(crate) struct ClientRequestMetadata {
523 pub protocol_version: ProtocolVersion,
524 pub client_info: Implementation,
525 pub client_capabilities: ClientCapabilities,
526}
527
528#[derive(Clone)]
534pub struct Peer<R: ServiceRole> {
535 tx: mpsc::Sender<PeerSinkMessage<R>>,
536 request_id_provider: Arc<dyn RequestIdProvider>,
537 progress_token_provider: Arc<dyn ProgressTokenProvider>,
538 progress_timeout_watchers: ProgressTimeoutWatchers,
539 info: Arc<std::sync::RwLock<Option<Arc<R::PeerInfo>>>>,
540 client_request_metadata: Arc<OnceLock<ClientRequestMetadata>>,
541 request_metadata_required: Arc<std::sync::atomic::AtomicBool>,
542}
543
544impl<R: ServiceRole> std::fmt::Debug for Peer<R> {
545 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
546 f.debug_struct("PeerSink")
547 .field("tx", &self.tx)
548 .field("is_client", &R::IS_CLIENT)
549 .finish()
550 }
551}
552
553type ProxyOutbound<R> = mpsc::Receiver<PeerSinkMessage<R>>;
554
555#[derive(Debug, Default)]
556#[non_exhaustive]
557pub struct PeerRequestOptions {
558 pub timeout: Option<Duration>,
559 pub meta: Option<RequestMetaObject>,
560 pub reset_timeout_on_progress: bool,
562 pub max_total_timeout: Option<Duration>,
564}
565
566impl PeerRequestOptions {
567 pub fn no_options() -> Self {
568 Self::default()
569 }
570
571 pub fn with_timeout(timeout: Duration) -> Self {
572 Self {
573 timeout: Some(timeout),
574 ..Self::default()
575 }
576 }
577
578 pub fn with_meta(mut self, meta: RequestMetaObject) -> Self {
582 self.meta = Some(meta);
583 self
584 }
585
586 pub fn reset_timeout_on_progress(mut self) -> Self {
587 self.reset_timeout_on_progress = true;
588 self
589 }
590
591 pub fn with_max_total_timeout(mut self, timeout: Duration) -> Self {
592 self.max_total_timeout = Some(timeout);
593 self
594 }
595}
596
597impl<R: ServiceRole> Peer<R> {
598 const CLIENT_CHANNEL_BUFFER_SIZE: usize = 1024;
599 pub(crate) fn new(
600 request_id_provider: Arc<dyn RequestIdProvider>,
601 peer_info: Option<R::PeerInfo>,
602 ) -> (Peer<R>, ProxyOutbound<R>) {
603 let (tx, rx) = mpsc::channel(Self::CLIENT_CHANNEL_BUFFER_SIZE);
604 (
605 Self {
606 tx,
607 request_id_provider,
608 progress_token_provider: Arc::new(AtomicU32ProgressTokenProvider::default()),
609 progress_timeout_watchers: Default::default(),
610 info: Arc::new(std::sync::RwLock::new(peer_info.map(Arc::new))),
611 client_request_metadata: Default::default(),
612 request_metadata_required: Default::default(),
613 },
614 rx,
615 )
616 }
617 pub async fn send_notification(&self, notification: R::Not) -> Result<(), ServiceError> {
618 let (responder, receiver) = tokio::sync::oneshot::channel();
619 self.tx
620 .send(PeerSinkMessage::Notification {
621 notification,
622 responder,
623 })
624 .await
625 .map_err(|_m| ServiceError::TransportClosed)?;
626 receiver.await.map_err(|_e| ServiceError::TransportClosed)?
627 }
628 pub async fn send_request(&self, request: R::Req) -> Result<R::PeerResp, ServiceError> {
629 self.send_request_with_option(request, PeerRequestOptions::no_options())
630 .await?
631 .await_response()
632 .await
633 }
634
635 pub async fn send_cancellable_request(
636 &self,
637 request: R::Req,
638 options: PeerRequestOptions,
639 ) -> Result<RequestHandle<R>, ServiceError> {
640 self.send_request_with_option(request, options).await
641 }
642
643 pub async fn send_request_with_option(
644 &self,
645 mut request: R::Req,
646 options: PeerRequestOptions,
647 ) -> Result<RequestHandle<R>, ServiceError> {
648 let id = self.request_id_provider.next_request_id();
649 let progress_token = self.progress_token_provider.next_progress_token();
650 if let Some(metadata) = self.client_request_metadata.get() {
651 let meta = request.get_meta_mut();
652 meta.set_protocol_version(metadata.protocol_version.clone());
653 meta.set_client_info(metadata.client_info.clone());
654 meta.set_client_capabilities(metadata.client_capabilities.clone());
655 }
656 if let Some(meta) = options.meta.clone() {
657 request.get_meta_mut().extend(meta);
658 }
659 request
660 .get_meta_mut()
661 .set_progress_token(progress_token.clone());
662 let (responder, receiver) = tokio::sync::oneshot::channel();
663 let progress_reset_rx = if options.reset_timeout_on_progress && options.timeout.is_some() {
664 let (sender, receiver) = mpsc::channel(1);
665 self.progress_timeout_watchers
666 .write()
667 .await
668 .insert(progress_token.clone(), sender);
669 Some(receiver)
670 } else {
671 None
672 };
673 if self
674 .tx
675 .send(PeerSinkMessage::Request {
676 request,
677 id: id.clone(),
678 responder,
679 })
680 .await
681 .is_err()
682 {
683 if progress_reset_rx.is_some() {
684 self.progress_timeout_watchers
685 .write()
686 .await
687 .remove(&progress_token);
688 }
689 return Err(ServiceError::TransportClosed);
690 }
691 Ok(RequestHandle {
692 id,
693 rx: receiver,
694 progress_token,
695 options,
696 peer: self.clone(),
697 progress_reset_rx,
698 })
699 }
700
701 async fn notify_progress_timeout_watcher(&self, progress_token: &ProgressToken) {
702 let sender = self
703 .progress_timeout_watchers
704 .read()
705 .await
706 .get(progress_token)
707 .cloned();
708 if let Some(sender) = sender {
709 match sender.try_send(()) {
710 Ok(()) => {}
711 Err(mpsc::error::TrySendError::Full(_)) => {
712 tracing::trace!(?progress_token, "progress timeout watcher channel is full");
713 }
714 Err(mpsc::error::TrySendError::Closed(_)) => {
715 self.progress_timeout_watchers
716 .write()
717 .await
718 .remove(progress_token);
719 }
720 }
721 }
722 }
723
724 pub fn peer_info(&self) -> Option<Arc<R::PeerInfo>> {
726 self.info.read().expect("peer info lock poisoned").clone()
727 }
728
729 pub fn set_peer_info(&self, info: R::PeerInfo) {
731 *self.info.write().expect("peer info lock poisoned") = Some(Arc::new(info));
732 }
733
734 pub(crate) fn set_client_request_metadata(&self, metadata: ClientRequestMetadata) {
735 let result = self.client_request_metadata.set(metadata);
736 debug_assert!(result.is_ok(), "client request metadata set more than once");
737 }
738
739 pub(crate) fn require_request_metadata(&self) {
740 self.request_metadata_required
741 .store(true, std::sync::atomic::Ordering::Release);
742 }
743
744 pub(crate) fn request_metadata_required(&self) -> bool {
745 self.request_metadata_required
746 .load(std::sync::atomic::Ordering::Acquire)
747 }
748
749 pub fn is_transport_closed(&self) -> bool {
750 self.tx.is_closed()
751 }
752}
753
754#[derive(Debug)]
755pub struct RunningService<R: ServiceRole, S: Service<R>> {
756 service: Arc<S>,
757 peer: Peer<R>,
758 handle: Option<tokio::task::JoinHandle<QuitReason>>,
759 cancellation_token: CancellationToken,
760 dg: DropGuard,
761}
762impl<R: ServiceRole, S: Service<R>> Deref for RunningService<R, S> {
763 type Target = Peer<R>;
764
765 fn deref(&self) -> &Self::Target {
766 &self.peer
767 }
768}
769
770impl<R: ServiceRole, S: Service<R>> RunningService<R, S> {
771 #[inline]
772 pub fn peer(&self) -> &Peer<R> {
773 &self.peer
774 }
775 #[inline]
776 pub fn service(&self) -> &S {
777 self.service.as_ref()
778 }
779 #[inline]
780 pub fn cancellation_token(&self) -> RunningServiceCancellationToken {
781 RunningServiceCancellationToken(self.cancellation_token.clone())
782 }
783
784 #[inline]
786 pub fn is_closed(&self) -> bool {
787 self.handle.is_none() || self.cancellation_token.is_cancelled()
788 }
789
790 #[inline]
795 pub async fn waiting(mut self) -> Result<QuitReason, tokio::task::JoinError> {
796 match self.handle.take() {
797 Some(handle) => handle.await,
798 None => Ok(QuitReason::Closed),
799 }
800 }
801
802 pub async fn close(&mut self) -> Result<QuitReason, tokio::task::JoinError> {
820 if let Some(handle) = self.handle.take() {
821 self.cancellation_token.cancel();
824 handle.await
825 } else {
826 Ok(QuitReason::Closed)
828 }
829 }
830
831 pub async fn close_with_timeout(
840 &mut self,
841 timeout: Duration,
842 ) -> Result<Option<QuitReason>, tokio::task::JoinError> {
843 if let Some(handle) = self.handle.take() {
844 self.cancellation_token.cancel();
845 match tokio::time::timeout(timeout, handle).await {
846 Ok(result) => result.map(Some),
847 Err(_elapsed) => {
848 tracing::warn!(
849 "close_with_timeout: cleanup did not complete within {:?}",
850 timeout
851 );
852 Ok(None)
853 }
854 }
855 } else {
856 Ok(Some(QuitReason::Closed))
857 }
858 }
859
860 pub async fn cancel(mut self) -> Result<QuitReason, tokio::task::JoinError> {
865 let _ = std::mem::replace(&mut self.dg, self.cancellation_token.clone().drop_guard());
867 self.close().await
868 }
869}
870
871impl<R: ServiceRole, S: Service<R>> Drop for RunningService<R, S> {
872 fn drop(&mut self) {
873 if self.handle.is_some() && !self.cancellation_token.is_cancelled() {
874 tracing::debug!(
875 "RunningService dropped without explicit close(). \
876 The connection will be closed asynchronously. \
877 For guaranteed cleanup, call close() or cancel() before dropping."
878 );
879 }
880 }
882}
883
884pub struct RunningServiceCancellationToken(CancellationToken);
886
887impl RunningServiceCancellationToken {
888 pub fn cancel(self) {
889 self.0.cancel();
890 }
891}
892
893#[derive(Debug)]
894#[non_exhaustive]
895pub enum QuitReason {
896 Cancelled,
897 Closed,
898 JoinError(tokio::task::JoinError),
899}
900
901#[derive(Debug, Clone)]
903#[non_exhaustive]
904pub struct RequestContext<R: ServiceRole> {
905 pub ct: CancellationToken,
907 pub id: RequestId,
908 pub meta: RequestMetaObject,
909 pub extensions: Extensions,
910 pub peer: Peer<R>,
912}
913
914impl<R: ServiceRole> RequestContext<R> {
915 pub fn new(id: RequestId, peer: Peer<R>) -> Self {
917 Self {
918 ct: CancellationToken::new(),
919 id,
920 meta: RequestMetaObject::default(),
921 extensions: Extensions::default(),
922 peer,
923 }
924 }
925}
926
927#[cfg(feature = "server")]
928impl RequestContext<RoleServer> {
929 pub fn protocol_version(&self) -> Option<crate::model::ProtocolVersion> {
931 self.meta.protocol_version().or_else(|| {
932 self.peer
933 .peer_info()
934 .map(|info| info.protocol_version.clone())
935 })
936 }
937
938 pub fn client_info(&self) -> Option<Implementation> {
940 if self.peer.request_metadata_required() {
941 self.meta.client_info()
942 } else {
943 self.meta
944 .client_info()
945 .or_else(|| self.peer.peer_info().map(|info| info.client_info.clone()))
946 }
947 }
948
949 pub fn client_capabilities(&self) -> Option<ClientCapabilities> {
951 if self.peer.request_metadata_required() {
952 self.meta.client_capabilities()
953 } else {
954 self.meta
955 .client_capabilities()
956 .or_else(|| self.peer.peer_info().map(|info| info.capabilities.clone()))
957 }
958 }
959}
960
961#[derive(Debug, Clone)]
963#[non_exhaustive]
964pub struct NotificationContext<R: ServiceRole> {
965 pub meta: NotificationMetaObject,
966 pub extensions: Extensions,
967 pub peer: Peer<R>,
969}
970
971pub fn serve_directly<R, S, T, E, A>(
973 service: S,
974 transport: T,
975 peer_info: Option<R::PeerInfo>,
976) -> RunningService<R, S>
977where
978 R: ServiceRole,
979 R::PeerNot: ProgressNotificationToken,
980 S: Service<R>,
981 T: IntoTransport<R, E, A>,
982 E: std::error::Error + Send + Sync + 'static,
983{
984 serve_directly_with_ct(service, transport, peer_info, Default::default())
985}
986
987pub fn serve_directly_with_ct<R, S, T, E, A>(
989 service: S,
990 transport: T,
991 peer_info: Option<R::PeerInfo>,
992 ct: CancellationToken,
993) -> RunningService<R, S>
994where
995 R: ServiceRole,
996 R::PeerNot: ProgressNotificationToken,
997 S: Service<R>,
998 T: IntoTransport<R, E, A>,
999 E: std::error::Error + Send + Sync + 'static,
1000{
1001 let (peer, peer_rx) = Peer::new(Arc::new(AtomicU32RequestIdProvider::default()), peer_info);
1002 serve_inner(service, transport.into_transport(), peer, peer_rx, ct)
1003}
1004
1005#[cfg(not(feature = "local"))]
1010fn spawn_service_task<F>(future: F) -> tokio::task::JoinHandle<F::Output>
1011where
1012 F: Future + Send + 'static,
1013 F::Output: Send + 'static,
1014{
1015 tokio::spawn(future)
1016}
1017
1018#[cfg(feature = "local")]
1019fn spawn_service_task<F>(future: F) -> tokio::task::JoinHandle<F::Output>
1020where
1021 F: Future + 'static,
1022 F::Output: 'static,
1023{
1024 tokio::task::spawn_local(future)
1025}
1026
1027#[instrument(skip_all)]
1028fn serve_inner<R, S, T>(
1029 service: S,
1030 transport: T,
1031 peer: Peer<R>,
1032 mut peer_rx: tokio::sync::mpsc::Receiver<PeerSinkMessage<R>>,
1033 ct: CancellationToken,
1034) -> RunningService<R, S>
1035where
1036 R: ServiceRole,
1037 R::PeerNot: ProgressNotificationToken,
1038 S: Service<R>,
1039 T: Transport<R> + 'static,
1040{
1041 const SINK_PROXY_BUFFER_SIZE: usize = 64;
1042 let (sink_proxy_tx, mut sink_proxy_rx) =
1043 tokio::sync::mpsc::channel::<TxJsonRpcMessage<R>>(SINK_PROXY_BUFFER_SIZE);
1044 let peer_info = peer.peer_info();
1045 if R::IS_CLIENT {
1046 tracing::info!(?peer_info, "Service initialized as client");
1047 } else {
1048 tracing::info!(?peer_info, "Service initialized as server");
1049 }
1050
1051 let mut local_responder_pool =
1052 HashMap::<RequestId, Responder<Result<R::PeerResp, ServiceError>>>::new();
1053 let mut local_ct_pool = HashMap::<RequestId, CancellationToken>::new();
1054 let shared_service = Arc::new(service);
1055 let service = shared_service.clone();
1057
1058 let serve_loop_ct = ct.child_token();
1061 let peer_return: Peer<R> = peer.clone();
1062 let current_span = tracing::Span::current();
1063 let handle = spawn_service_task(async move {
1064 let mut transport = transport.into_transport();
1065 let mut batch_messages = VecDeque::<RxJsonRpcMessage<R>>::new();
1066 let mut send_task_set = tokio::task::JoinSet::<SendTaskResult>::new();
1067 let mut response_send_tasks = tokio::task::JoinSet::<()>::new();
1068 #[derive(Debug)]
1069 enum SendTaskResult {
1070 Request {
1071 id: RequestId,
1072 result: Result<(), DynamicTransportError>,
1073 },
1074 Notification {
1075 responder: Responder<Result<(), ServiceError>>,
1076 cancellation_param: Option<CancelledNotificationParam>,
1077 result: Result<(), DynamicTransportError>,
1078 },
1079 }
1080 #[derive(Debug)]
1081 enum Event<R: ServiceRole> {
1082 ProxyMessage(PeerSinkMessage<R>),
1083 PeerMessage(RxJsonRpcMessage<R>),
1084 ToSink(TxJsonRpcMessage<R>),
1085 SendTaskResult(SendTaskResult),
1086 }
1087
1088 let quit_reason = loop {
1089 let evt = if let Some(m) = batch_messages.pop_front() {
1090 Event::PeerMessage(m)
1091 } else {
1092 tokio::select! {
1093 m = sink_proxy_rx.recv(), if !sink_proxy_rx.is_closed() => {
1094 if let Some(m) = m {
1095 Event::ToSink(m)
1096 } else {
1097 continue
1098 }
1099 }
1100 m = transport.receive() => {
1101 if let Some(m) = m {
1102 Event::PeerMessage(m)
1103 } else {
1104 tracing::info!("input stream terminated");
1106 break QuitReason::Closed
1107 }
1108 }
1109 m = peer_rx.recv(), if !peer_rx.is_closed() => {
1110 if let Some(m) = m {
1111 Event::ProxyMessage(m)
1112 } else {
1113 continue
1114 }
1115 }
1116 m = send_task_set.join_next(), if !send_task_set.is_empty() => {
1117 let Some(result) = m else {
1118 continue
1119 };
1120 match result {
1121 Err(e) => {
1122 tracing::error!(%e, "send request task encounter a tokio join error");
1124 break QuitReason::JoinError(e)
1125 }
1126 Ok(result) => {
1127 Event::SendTaskResult(result)
1128 }
1129 }
1130 }
1131 _ = serve_loop_ct.cancelled() => {
1132 tracing::info!("task cancelled");
1133 break QuitReason::Cancelled
1134 }
1135 }
1136 };
1137
1138 tracing::trace!(?evt, "new event");
1139 match evt {
1140 Event::SendTaskResult(SendTaskResult::Request { id, result }) => {
1141 if let Err(e) = result {
1142 if let Some(responder) = local_responder_pool.remove(&id) {
1143 let _ = responder.send(Err(ServiceError::TransportSend(e)));
1144 }
1145 }
1146 }
1147 Event::SendTaskResult(SendTaskResult::Notification {
1148 responder,
1149 result,
1150 cancellation_param,
1151 }) => {
1152 let response = if let Err(e) = result {
1153 Err(ServiceError::TransportSend(e))
1154 } else {
1155 Ok(())
1156 };
1157 let _ = responder.send(response);
1158 if let Some(param) = cancellation_param {
1159 if let Some(request_id) = ¶m.request_id {
1160 if let Some(responder) = local_responder_pool.remove(request_id) {
1161 tracing::info!(id = %request_id, reason = param.reason, "cancelled");
1162 let _response_result = responder.send(Err(ServiceError::Cancelled {
1163 reason: param.reason.clone(),
1164 }));
1165 }
1166 }
1167 }
1168 }
1169 Event::ToSink(m) => {
1171 if let Some(id) = match &m {
1172 JsonRpcMessage::Response(response) => Some(&response.id),
1173 JsonRpcMessage::Error(error) => error.id.as_ref(),
1174 _ => None,
1175 } {
1176 let Some(ct) = local_ct_pool.remove(id) else {
1177 tracing::debug!(%id, "dropping response for cancelled request");
1178 continue;
1179 };
1180 ct.cancel();
1181 let send = transport.send(m);
1182 let current_span = tracing::Span::current();
1183 response_send_tasks.spawn(async move {
1184 let send_result = send.await;
1185 if let Err(error) = send_result {
1186 tracing::error!(%error, "fail to response message");
1187 }
1188 }.instrument(current_span));
1189 }
1190 }
1191 Event::ProxyMessage(PeerSinkMessage::Request {
1192 request,
1193 id,
1194 responder,
1195 }) => {
1196 local_responder_pool.insert(id.clone(), responder);
1197 let send = transport.send(JsonRpcMessage::request(request, id.clone()));
1198 {
1199 let id = id.clone();
1200 let current_span = tracing::Span::current();
1201 send_task_set.spawn(send.map(move |r| SendTaskResult::Request {
1202 id,
1203 result: r.map_err(DynamicTransportError::new::<T, R>),
1204 }).instrument(current_span));
1205 }
1206 }
1207 Event::ProxyMessage(PeerSinkMessage::Notification {
1208 notification,
1209 responder,
1210 }) => {
1211 let mut cancellation_param = None;
1213 let notification = match notification.try_into() {
1214 Ok::<CancelledNotification, _>(cancelled) => {
1215 cancellation_param.replace(cancelled.params.clone());
1216 cancelled.into()
1217 }
1218 Err(notification) => notification,
1219 };
1220 let send = transport.send(JsonRpcMessage::notification(notification));
1221 let current_span = tracing::Span::current();
1222 send_task_set.spawn(send.map(move |result| SendTaskResult::Notification {
1223 responder,
1224 cancellation_param,
1225 result: result.map_err(DynamicTransportError::new::<T, R>),
1226 }).instrument(current_span));
1227 }
1228 Event::PeerMessage(JsonRpcMessage::Request(JsonRpcRequest {
1229 id,
1230 mut request,
1231 ..
1232 })) => {
1233 tracing::debug!(%id, ?request, "received request");
1234 {
1235 let service = shared_service.clone();
1236 let sink = sink_proxy_tx.clone();
1237 let request_ct = serve_loop_ct.child_token();
1238 let context_ct = request_ct.child_token();
1239 local_ct_pool.insert(id.clone(), request_ct);
1240 let mut extensions = Extensions::new();
1241 let mut meta = RequestMetaObject::new();
1242 std::mem::swap(&mut meta, request.get_meta_mut());
1245 std::mem::swap(&mut extensions, request.extensions_mut());
1246 let context = RequestContext {
1247 ct: context_ct,
1248 id: id.clone(),
1249 peer: peer.clone(),
1250 meta,
1251 extensions,
1252 };
1253 let current_span = tracing::Span::current();
1254 spawn_service_task(async move {
1255 let result = service
1256 .handle_request(request, context)
1257 .await;
1258 let response = match result {
1259 Ok(result) => {
1260 tracing::debug!(%id, ?result, "response message");
1261 JsonRpcMessage::response(result, id)
1262 }
1263 Err(error) => {
1264 tracing::warn!(%id, ?error, "response error");
1265 JsonRpcMessage::error(error, Some(id))
1266 }
1267 };
1268 let _send_result = sink.send(response).await;
1269 }.instrument(current_span));
1270 }
1271 }
1272 Event::PeerMessage(JsonRpcMessage::Notification(JsonRpcNotification {
1273 notification,
1274 ..
1275 })) => {
1276 tracing::info!(?notification, "received notification");
1277 let mut notification = match notification.try_into() {
1279 Ok::<CancelledNotification, _>(cancelled) => {
1280 if let Some(request_id) = &cancelled.params.request_id {
1281 if let Some(ct) = local_ct_pool.remove(request_id) {
1282 tracing::info!(id = %request_id, reason = cancelled.params.reason, "cancelled");
1283 ct.cancel();
1284 }
1285 }
1286 cancelled.into()
1287 }
1288 Err(notification) => notification,
1289 };
1290 if let Some(progress_token) = notification.progress_token() {
1291 peer.notify_progress_timeout_watcher(progress_token).await;
1292 }
1293 {
1294 let service = shared_service.clone();
1295 let mut extensions = Extensions::new();
1296 let mut meta = NotificationMetaObject::new();
1297 std::mem::swap(&mut extensions, notification.extensions_mut());
1299 std::mem::swap(&mut meta, notification.get_meta_mut());
1300 let context = NotificationContext {
1301 peer: peer.clone(),
1302 meta,
1303 extensions,
1304 };
1305 let current_span = tracing::Span::current();
1306 spawn_service_task(async move {
1307 let result = service.handle_notification(notification, context).await;
1308 if let Err(error) = result {
1309 tracing::warn!(%error, "Error sending notification");
1310 }
1311 }.instrument(current_span));
1312 }
1313 }
1314 Event::PeerMessage(JsonRpcMessage::Response(JsonRpcResponse {
1315 result,
1316 id,
1317 ..
1318 })) => {
1319 if let Some(responder) = local_responder_pool.remove(&id) {
1320 let response_result = responder.send(Ok(result));
1321 if let Err(_error) = response_result {
1322 tracing::warn!(%id, "Error sending response");
1323 }
1324 }
1325 }
1326 Event::PeerMessage(JsonRpcMessage::Error(JsonRpcError { error, id, .. })) => {
1327 let Some(id) = id else {
1328 tracing::debug!(?error, "received id-less peer error");
1331 continue;
1332 };
1333 if let Some(responder) = local_responder_pool.remove(&id) {
1334 let _response_result = responder.send(Err(ServiceError::McpError(error)));
1335 if let Err(_error) = _response_result {
1336 tracing::warn!(%id, "Error sending response");
1337 }
1338 }
1339 }
1340 }
1341 };
1342
1343 let drain_timeout = match &quit_reason {
1349 QuitReason::Closed => Some(Duration::from_secs(5)),
1350 QuitReason::Cancelled => Some(Duration::from_secs(2)),
1351 _ => None,
1352 };
1353 if let Some(timeout_duration) = drain_timeout {
1354 drop(sink_proxy_tx);
1357 let drain_result = tokio::time::timeout(timeout_duration, async {
1358 while let Some(result) = response_send_tasks.join_next().await {
1361 if let Err(error) = result {
1362 tracing::error!(%error, "response send task failed during drain");
1363 }
1364 }
1365 while let Some(m) = sink_proxy_rx.recv().await {
1368 if let Err(error) = transport.send(m).await {
1369 tracing::error!(%error, "failed to send pending response during drain");
1370 break;
1371 }
1372 }
1373 })
1374 .await;
1375 if drain_result.is_err() {
1376 tracing::warn!("timed out draining in-flight responses");
1377 }
1378 }
1379
1380 let sink_close_result = transport.close().await;
1381 if let Err(e) = sink_close_result {
1382 tracing::error!(%e, "fail to close sink");
1383 }
1384 tracing::info!(?quit_reason, "serve finished");
1385 quit_reason
1386 }.instrument(current_span));
1387 RunningService {
1388 service,
1389 peer: peer_return,
1390 handle: Some(handle),
1391 cancellation_token: ct.clone(),
1392 dg: ct.drop_guard(),
1393 }
1394}