1use std::sync::atomic::{AtomicI64, Ordering};
117use std::sync::{Arc, RwLock};
118
119use async_trait::async_trait;
120use tokio::sync::mpsc;
121
122use crate::error::{Error, Result};
123use crate::protocol::{
124 CallToolResult, CancelTaskParams, CreateMessageParams, CreateMessageResult, ElicitFormParams,
125 ElicitRequestParams, ElicitResult, ElicitUrlParams, GetTaskInfoParams, GetTaskResultParams,
126 ListTasksParams, ListTasksResult, LogLevel, LoggingMessageParams, ProgressParams,
127 ProgressToken, RequestId, TaskObject, TaskStatus,
128};
129
130#[derive(Debug, Clone)]
132#[non_exhaustive]
133pub enum ServerNotification {
134 Progress(ProgressParams),
136 LogMessage(LoggingMessageParams),
138 ResourceUpdated {
140 uri: String,
142 },
143 ResourcesListChanged,
145 ToolsListChanged,
147 PromptsListChanged,
149 TaskStatusChanged(crate::protocol::TaskStatusParams),
151 FinalTaskStatusChanged(crate::tasks::TaskStatusNotificationParams),
157}
158
159pub type NotificationSender = mpsc::Sender<ServerNotification>;
161
162pub type NotificationReceiver = mpsc::Receiver<ServerNotification>;
164
165pub fn notification_channel(buffer: usize) -> (NotificationSender, NotificationReceiver) {
167 mpsc::channel(buffer)
168}
169
170#[async_trait]
180pub trait ClientRequester: Send + Sync {
181 async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult>;
185
186 async fn elicit(&self, params: ElicitRequestParams) -> Result<ElicitResult>;
193
194 async fn request(
202 &self,
203 method: String,
204 params: serde_json::Value,
205 ) -> Result<serde_json::Value> {
206 let _ = (method, params);
207 Err(Error::Internal(
208 "ClientRequester does not support arbitrary requests".to_string(),
209 ))
210 }
211}
212
213pub type ClientRequesterHandle = Arc<dyn ClientRequester>;
215
216#[derive(Debug)]
218pub struct OutgoingRequest {
219 pub id: RequestId,
221 pub method: String,
223 pub params: serde_json::Value,
225 pub response_tx: tokio::sync::oneshot::Sender<Result<serde_json::Value>>,
227}
228
229pub type OutgoingRequestSender = mpsc::Sender<OutgoingRequest>;
231
232pub type OutgoingRequestReceiver = mpsc::Receiver<OutgoingRequest>;
234
235pub fn outgoing_request_channel(buffer: usize) -> (OutgoingRequestSender, OutgoingRequestReceiver) {
237 mpsc::channel(buffer)
238}
239
240#[derive(Clone)]
242pub struct ChannelClientRequester {
243 request_tx: OutgoingRequestSender,
244 next_id: Arc<AtomicI64>,
245}
246
247impl ChannelClientRequester {
248 pub fn new(request_tx: OutgoingRequestSender) -> Self {
250 Self {
251 request_tx,
252 next_id: Arc::new(AtomicI64::new(1)),
253 }
254 }
255
256 #[cfg(feature = "http")]
262 pub(crate) fn with_id_allocator(
263 request_tx: OutgoingRequestSender,
264 next_id: Arc<AtomicI64>,
265 ) -> Self {
266 Self {
267 request_tx,
268 next_id,
269 }
270 }
271
272 fn next_request_id(&self) -> RequestId {
273 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
274 RequestId::Number(id)
275 }
276}
277
278impl ChannelClientRequester {
279 async fn dispatch(&self, method: &str, params: serde_json::Value) -> Result<serde_json::Value> {
280 let id = self.next_request_id();
281 let (response_tx, response_rx) = tokio::sync::oneshot::channel();
282
283 let request = OutgoingRequest {
284 id,
285 method: method.to_string(),
286 params,
287 response_tx,
288 };
289
290 self.request_tx
291 .send(request)
292 .await
293 .map_err(|_| Error::Internal("Failed to send request: channel closed".to_string()))?;
294
295 response_rx.await.map_err(|_| {
296 Error::Internal("Failed to receive response: channel closed".to_string())
297 })?
298 }
299}
300
301#[async_trait]
302impl ClientRequester for ChannelClientRequester {
303 async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult> {
304 let params_json = serde_json::to_value(¶ms)
305 .map_err(|e| Error::Internal(format!("Failed to serialize params: {}", e)))?;
306 let response = self.dispatch("sampling/createMessage", params_json).await?;
307 serde_json::from_value(response)
308 .map_err(|e| Error::Internal(format!("Failed to deserialize response: {}", e)))
309 }
310
311 async fn elicit(&self, params: ElicitRequestParams) -> Result<ElicitResult> {
312 let params_json = serde_json::to_value(¶ms)
313 .map_err(|e| Error::Internal(format!("Failed to serialize params: {}", e)))?;
314 let response = self.dispatch("elicitation/create", params_json).await?;
315 serde_json::from_value(response)
316 .map_err(|e| Error::Internal(format!("Failed to deserialize response: {}", e)))
317 }
318
319 async fn request(
320 &self,
321 method: String,
322 params: serde_json::Value,
323 ) -> Result<serde_json::Value> {
324 self.dispatch(&method, params).await
325 }
326}
327
328#[derive(Clone)]
330pub struct RequestContext {
331 request_id: RequestId,
333 progress_token: Option<ProgressToken>,
335 cancellation: tokio_util::sync::CancellationToken,
337 notification_tx: Option<NotificationSender>,
339 client_requester: Option<ClientRequesterHandle>,
341 extensions: Arc<Extensions>,
343 min_log_level: Option<Arc<RwLock<LogLevel>>>,
345}
346
347#[derive(Clone, Default)]
352pub struct Extensions {
353 map: std::collections::HashMap<std::any::TypeId, Arc<dyn std::any::Any + Send + Sync>>,
354}
355
356impl Extensions {
357 pub fn new() -> Self {
359 Self::default()
360 }
361
362 pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) {
366 self.map.insert(std::any::TypeId::of::<T>(), Arc::new(val));
367 }
368
369 pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
373 self.map
374 .get(&std::any::TypeId::of::<T>())
375 .and_then(|val| val.downcast_ref::<T>())
376 }
377
378 pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
380 self.map.contains_key(&std::any::TypeId::of::<T>())
381 }
382
383 pub fn merge(&mut self, other: &Extensions) {
387 for (k, v) in &other.map {
388 self.map.insert(*k, v.clone());
389 }
390 }
391
392 pub fn len(&self) -> usize {
394 self.map.len()
395 }
396
397 pub fn is_empty(&self) -> bool {
399 self.map.is_empty()
400 }
401}
402
403impl std::fmt::Debug for Extensions {
404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405 f.debug_struct("Extensions")
406 .field("len", &self.map.len())
407 .finish()
408 }
409}
410
411impl std::fmt::Debug for RequestContext {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 f.debug_struct("RequestContext")
414 .field("request_id", &self.request_id)
415 .field("progress_token", &self.progress_token)
416 .field("cancelled", &self.cancellation.is_cancelled())
417 .finish()
418 }
419}
420
421impl RequestContext {
422 pub fn new(request_id: RequestId) -> Self {
424 Self {
425 request_id,
426 progress_token: None,
427 cancellation: tokio_util::sync::CancellationToken::new(),
428 notification_tx: None,
429 client_requester: None,
430 extensions: Arc::new(Extensions::new()),
431 min_log_level: None,
432 }
433 }
434
435 pub fn with_progress_token(mut self, token: ProgressToken) -> Self {
437 self.progress_token = Some(token);
438 self
439 }
440
441 pub fn with_notification_sender(mut self, tx: NotificationSender) -> Self {
443 self.notification_tx = Some(tx);
444 self
445 }
446
447 pub fn with_min_log_level(mut self, level: Arc<RwLock<LogLevel>>) -> Self {
452 self.min_log_level = Some(level);
453 self
454 }
455
456 pub fn with_client_requester(mut self, requester: ClientRequesterHandle) -> Self {
458 self.client_requester = Some(requester);
459 self
460 }
461
462 pub fn with_extensions(mut self, extensions: Arc<Extensions>) -> Self {
466 self.extensions = extensions;
467 self
468 }
469
470 pub fn extension<T: Send + Sync + 'static>(&self) -> Option<&T> {
486 self.extensions.get::<T>()
487 }
488
489 pub fn negotiated_extensions(&self) -> Option<&crate::NegotiatedExtensions> {
494 self.extension()
495 }
496
497 pub fn extensions_mut(&mut self) -> &mut Extensions {
502 Arc::make_mut(&mut self.extensions)
503 }
504
505 pub fn extensions(&self) -> &Extensions {
507 &self.extensions
508 }
509
510 #[cfg(feature = "stateless")]
537 pub fn per_request_meta(&self) -> Option<&crate::stateless::StatelessRequestMeta> {
538 self.extension::<crate::stateless::StatelessRequestMeta>()
539 }
540
541 #[cfg(feature = "stateless")]
543 pub fn mrtr(&self) -> Option<&crate::mrtr::MrtrRequest> {
544 self.extension::<crate::mrtr::MrtrRequest>()
545 }
546
547 #[cfg(feature = "stateless")]
549 pub fn input_responses(&self) -> Option<&crate::protocol::InputResponses> {
550 self.mrtr()
551 .and_then(crate::mrtr::MrtrRequest::input_responses)
552 }
553
554 #[cfg(feature = "stateless")]
556 pub fn request_state(&self) -> Option<&str> {
557 self.mrtr()
558 .and_then(crate::mrtr::MrtrRequest::request_state)
559 }
560
561 #[cfg(feature = "stateless")]
563 pub fn request_state_codec(&self) -> Option<&crate::mrtr::RequestStateCodec> {
564 self.extension::<crate::mrtr::RequestStateCodec>()
565 }
566
567 pub fn request_id(&self) -> &RequestId {
569 &self.request_id
570 }
571
572 pub fn progress_token(&self) -> Option<&ProgressToken> {
574 self.progress_token.as_ref()
575 }
576
577 pub fn is_cancelled(&self) -> bool {
579 self.cancellation.is_cancelled()
580 }
581
582 pub fn cancel(&self) {
584 self.cancellation.cancel();
585 }
586
587 pub async fn cancelled(&self) {
601 self.cancellation.cancelled().await
602 }
603
604 pub fn cancellation_token(&self) -> CancellationToken {
606 CancellationToken {
607 inner: self.cancellation.clone(),
608 }
609 }
610
611 pub fn with_cancellation_token(mut self, token: CancellationToken) -> Self {
619 self.cancellation = token.inner;
620 self
621 }
622
623 pub async fn report_progress(&self, progress: f64, total: Option<f64>, message: Option<&str>) {
627 let Some(token) = &self.progress_token else {
628 return;
629 };
630 let Some(tx) = &self.notification_tx else {
631 return;
632 };
633
634 let params = ProgressParams {
635 progress_token: token.clone(),
636 progress,
637 total,
638 message: message.map(|s| s.to_string()),
639 meta: None,
640 };
641
642 let _ = tx.try_send(ServerNotification::Progress(params));
644 }
645
646 pub fn report_progress_sync(&self, progress: f64, total: Option<f64>, message: Option<&str>) {
650 let Some(token) = &self.progress_token else {
651 return;
652 };
653 let Some(tx) = &self.notification_tx else {
654 return;
655 };
656
657 let params = ProgressParams {
658 progress_token: token.clone(),
659 progress,
660 total,
661 message: message.map(|s| s.to_string()),
662 meta: None,
663 };
664
665 let _ = tx.try_send(ServerNotification::Progress(params));
666 }
667
668 pub fn notify_tools_list_changed(&self) -> bool {
670 self.notification_tx
671 .as_ref()
672 .is_some_and(|tx| tx.try_send(ServerNotification::ToolsListChanged).is_ok())
673 }
674
675 pub fn notify_prompts_list_changed(&self) -> bool {
677 self.notification_tx
678 .as_ref()
679 .is_some_and(|tx| tx.try_send(ServerNotification::PromptsListChanged).is_ok())
680 }
681
682 pub fn notify_resources_list_changed(&self) -> bool {
684 self.notification_tx.as_ref().is_some_and(|tx| {
685 tx.try_send(ServerNotification::ResourcesListChanged)
686 .is_ok()
687 })
688 }
689
690 pub fn notify_resource_updated(&self, uri: impl Into<String>) -> bool {
692 self.notification_tx.as_ref().is_some_and(|tx| {
693 tx.try_send(ServerNotification::ResourceUpdated { uri: uri.into() })
694 .is_ok()
695 })
696 }
697
698 pub fn send_log(&self, params: LoggingMessageParams) {
715 let Some(tx) = &self.notification_tx else {
716 return;
717 };
718
719 #[cfg(feature = "stateless")]
722 if let Some(meta) = self.per_request_meta()
723 && meta.protocol_version.as_deref()
724 == Some(crate::protocol::PROTOCOL_VERSION_2026_07_28)
725 {
726 let Some(request_level) = meta.log_level else {
727 return;
728 };
729 let request_level = match request_level {
730 crate::stateless::LogLevel::Debug => LogLevel::Debug,
731 crate::stateless::LogLevel::Info => LogLevel::Info,
732 crate::stateless::LogLevel::Notice => LogLevel::Notice,
733 crate::stateless::LogLevel::Warning => LogLevel::Warning,
734 crate::stateless::LogLevel::Error => LogLevel::Error,
735 crate::stateless::LogLevel::Critical => LogLevel::Critical,
736 crate::stateless::LogLevel::Alert => LogLevel::Alert,
737 crate::stateless::LogLevel::Emergency => LogLevel::Emergency,
738 };
739 if params.level > request_level {
740 return;
741 }
742 let _ = tx.try_send(ServerNotification::LogMessage(params));
743 return;
744 }
745
746 if let Some(min_level) = &self.min_log_level
751 && let Ok(min) = min_level.read()
752 && params.level > *min
753 {
754 return;
755 }
756
757 let _ = tx.try_send(ServerNotification::LogMessage(params));
758 }
759
760 pub fn can_sample(&self) -> bool {
765 self.client_requester.is_some()
766 }
767
768 pub async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult> {
792 let requester = self.client_requester.as_ref().ok_or_else(|| {
793 Error::Internal("Sampling not available: no client requester configured".to_string())
794 })?;
795
796 requester.sample(params).await
797 }
798
799 pub fn can_elicit(&self) -> bool {
805 self.client_requester.is_some()
806 }
807
808 pub async fn elicit_form(&self, params: ElicitFormParams) -> Result<ElicitResult> {
840 let requester = self.client_requester.as_ref().ok_or_else(|| {
841 Error::Internal("Elicitation not available: no client requester configured".to_string())
842 })?;
843
844 requester.elicit(ElicitRequestParams::Form(params)).await
845 }
846
847 pub async fn elicit_url(&self, params: ElicitUrlParams) -> Result<ElicitResult> {
884 let requester = self.client_requester.as_ref().ok_or_else(|| {
885 Error::Internal("Elicitation not available: no client requester configured".to_string())
886 })?;
887
888 requester.elicit(ElicitRequestParams::Url(params)).await
889 }
890
891 pub async fn confirm(&self, message: impl Into<String>) -> Result<bool> {
915 use crate::protocol::{ElicitAction, ElicitFormParams, ElicitFormSchema, ElicitMode};
916
917 let params = ElicitFormParams {
918 mode: Some(ElicitMode::Form),
919 message: message.into(),
920 requested_schema: ElicitFormSchema::new().boolean_field_with_default(
921 "confirm",
922 Some("Confirm this action"),
923 true,
924 false,
925 ),
926 meta: None,
927 };
928
929 let result = self.elicit_form(params).await?;
930 Ok(result.action == ElicitAction::Accept)
931 }
932
933 #[deprecated(
943 since = "0.13.0",
944 note = "final SEP-2663 removes tasks/list; a conforming peer answers \
945 MethodNotFound (-32601). Only useful against legacy SEP-1686 \
946 clients."
947 )]
948 pub async fn list_tasks(&self, status: Option<TaskStatus>) -> Result<ListTasksResult> {
949 let params = ListTasksParams {
950 status,
951 cursor: None,
952 meta: None,
953 };
954 let value = self
955 .request_raw("tasks/list", serde_json::to_value(¶ms)?)
956 .await?;
957 serde_json::from_value(value)
958 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/list: {e}")))
959 }
960
961 pub async fn get_task_info(&self, task_id: impl Into<String>) -> Result<TaskObject> {
966 let params = GetTaskInfoParams {
967 task_id: task_id.into(),
968 meta: None,
969 };
970 let value = self
971 .request_raw("tasks/get", serde_json::to_value(¶ms)?)
972 .await?;
973 serde_json::from_value(value)
974 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/get: {e}")))
975 }
976
977 #[deprecated(
986 since = "0.13.0",
987 note = "final SEP-2663 removes tasks/result (results are inlined in \
988 the tasks/get DetailedTask); a conforming peer answers \
989 MethodNotFound (-32601). Only useful against legacy SEP-1686 \
990 clients."
991 )]
992 pub async fn get_task_result(&self, task_id: impl Into<String>) -> Result<CallToolResult> {
993 let params = GetTaskResultParams {
994 task_id: task_id.into(),
995 meta: None,
996 };
997 let value = self
998 .request_raw("tasks/result", serde_json::to_value(¶ms)?)
999 .await?;
1000 serde_json::from_value(value)
1001 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/result: {e}")))
1002 }
1003
1004 pub async fn cancel_task(
1011 &self,
1012 task_id: impl Into<String>,
1013 reason: Option<String>,
1014 ) -> Result<()> {
1015 let params = CancelTaskParams {
1016 task_id: task_id.into(),
1017 reason,
1018 meta: None,
1019 };
1020 self.request_raw("tasks/cancel", serde_json::to_value(¶ms)?)
1021 .await?;
1022 Ok(())
1023 }
1024
1025 pub async fn request_raw(
1031 &self,
1032 method: &str,
1033 params: serde_json::Value,
1034 ) -> Result<serde_json::Value> {
1035 let requester = self.client_requester.as_ref().ok_or_else(|| {
1036 Error::Internal(
1037 "Client request not available: no client requester configured".to_string(),
1038 )
1039 })?;
1040 requester.request(method.to_string(), params).await
1041 }
1042}
1043
1044#[derive(Clone, Debug, Default)]
1050pub struct CancellationToken {
1051 inner: tokio_util::sync::CancellationToken,
1052}
1053
1054impl CancellationToken {
1055 pub fn new() -> Self {
1057 Self::default()
1058 }
1059
1060 pub fn is_cancelled(&self) -> bool {
1062 self.inner.is_cancelled()
1063 }
1064
1065 pub fn cancel(&self) {
1067 self.inner.cancel();
1068 }
1069
1070 pub async fn cancelled(&self) {
1074 self.inner.cancelled().await
1075 }
1076}
1077
1078#[derive(Default)]
1080pub struct RequestContextBuilder {
1081 request_id: Option<RequestId>,
1082 progress_token: Option<ProgressToken>,
1083 notification_tx: Option<NotificationSender>,
1084 client_requester: Option<ClientRequesterHandle>,
1085 min_log_level: Option<Arc<RwLock<LogLevel>>>,
1086}
1087
1088impl RequestContextBuilder {
1089 pub fn new() -> Self {
1091 Self::default()
1092 }
1093
1094 pub fn request_id(mut self, id: RequestId) -> Self {
1096 self.request_id = Some(id);
1097 self
1098 }
1099
1100 pub fn progress_token(mut self, token: ProgressToken) -> Self {
1102 self.progress_token = Some(token);
1103 self
1104 }
1105
1106 pub fn notification_sender(mut self, tx: NotificationSender) -> Self {
1108 self.notification_tx = Some(tx);
1109 self
1110 }
1111
1112 pub fn client_requester(mut self, requester: ClientRequesterHandle) -> Self {
1114 self.client_requester = Some(requester);
1115 self
1116 }
1117
1118 pub fn min_log_level(mut self, level: Arc<RwLock<LogLevel>>) -> Self {
1120 self.min_log_level = Some(level);
1121 self
1122 }
1123
1124 pub fn build(self) -> RequestContext {
1128 let mut ctx = RequestContext::new(self.request_id.expect("request_id is required"));
1129 if let Some(token) = self.progress_token {
1130 ctx = ctx.with_progress_token(token);
1131 }
1132 if let Some(tx) = self.notification_tx {
1133 ctx = ctx.with_notification_sender(tx);
1134 }
1135 if let Some(requester) = self.client_requester {
1136 ctx = ctx.with_client_requester(requester);
1137 }
1138 if let Some(level) = self.min_log_level {
1139 ctx = ctx.with_min_log_level(level);
1140 }
1141 ctx
1142 }
1143}
1144
1145#[cfg(test)]
1146mod tests {
1147 use super::*;
1148
1149 #[test]
1150 fn test_cancellation() {
1151 let ctx = RequestContext::new(RequestId::Number(1));
1152 assert!(!ctx.is_cancelled());
1153
1154 let token = ctx.cancellation_token();
1155 assert!(!token.is_cancelled());
1156
1157 ctx.cancel();
1158 assert!(ctx.is_cancelled());
1159 assert!(token.is_cancelled());
1160 }
1161
1162 #[tokio::test]
1163 async fn test_progress_reporting() {
1164 let (tx, mut rx) = notification_channel(10);
1165
1166 let ctx = RequestContext::new(RequestId::Number(1))
1167 .with_progress_token(ProgressToken::Number(42))
1168 .with_notification_sender(tx);
1169
1170 ctx.report_progress(50.0, Some(100.0), Some("Halfway"))
1171 .await;
1172
1173 let notification = rx.recv().await.unwrap();
1174 match notification {
1175 ServerNotification::Progress(params) => {
1176 assert_eq!(params.progress, 50.0);
1177 assert_eq!(params.total, Some(100.0));
1178 assert_eq!(params.message.as_deref(), Some("Halfway"));
1179 }
1180 _ => panic!("Expected Progress notification"),
1181 }
1182 }
1183
1184 #[tokio::test]
1185 async fn test_progress_no_token() {
1186 let (tx, mut rx) = notification_channel(10);
1187
1188 let ctx = RequestContext::new(RequestId::Number(1)).with_notification_sender(tx);
1190
1191 ctx.report_progress(50.0, Some(100.0), None).await;
1192
1193 assert!(rx.try_recv().is_err());
1195 }
1196
1197 #[test]
1198 fn test_builder() {
1199 let (tx, _rx) = notification_channel(10);
1200
1201 let ctx = RequestContextBuilder::new()
1202 .request_id(RequestId::String("req-1".to_string()))
1203 .progress_token(ProgressToken::String("prog-1".to_string()))
1204 .notification_sender(tx)
1205 .build();
1206
1207 assert_eq!(ctx.request_id(), &RequestId::String("req-1".to_string()));
1208 assert!(ctx.progress_token().is_some());
1209 }
1210
1211 #[test]
1212 fn test_can_sample_without_requester() {
1213 let ctx = RequestContext::new(RequestId::Number(1));
1214 assert!(!ctx.can_sample());
1215 }
1216
1217 #[test]
1218 fn test_can_sample_with_requester() {
1219 let (request_tx, _rx) = outgoing_request_channel(10);
1220 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1221
1222 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1223 assert!(ctx.can_sample());
1224 }
1225
1226 #[tokio::test]
1227 async fn test_sample_without_requester_fails() {
1228 use crate::protocol::{CreateMessageParams, SamplingMessage};
1229
1230 let ctx = RequestContext::new(RequestId::Number(1));
1231 let params = CreateMessageParams::new(vec![SamplingMessage::user("test")], 100);
1232
1233 let result = ctx.sample(params).await;
1234 assert!(result.is_err());
1235 assert!(
1236 result
1237 .unwrap_err()
1238 .to_string()
1239 .contains("Sampling not available")
1240 );
1241 }
1242
1243 #[test]
1244 fn test_builder_with_client_requester() {
1245 let (request_tx, _rx) = outgoing_request_channel(10);
1246 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1247
1248 let ctx = RequestContextBuilder::new()
1249 .request_id(RequestId::Number(1))
1250 .client_requester(requester)
1251 .build();
1252
1253 assert!(ctx.can_sample());
1254 }
1255
1256 #[test]
1257 fn test_can_elicit_without_requester() {
1258 let ctx = RequestContext::new(RequestId::Number(1));
1259 assert!(!ctx.can_elicit());
1260 }
1261
1262 #[test]
1263 fn test_can_elicit_with_requester() {
1264 let (request_tx, _rx) = outgoing_request_channel(10);
1265 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1266
1267 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1268 assert!(ctx.can_elicit());
1269 }
1270
1271 #[tokio::test]
1272 async fn test_elicit_form_without_requester_fails() {
1273 use crate::protocol::{ElicitFormSchema, ElicitMode};
1274
1275 let ctx = RequestContext::new(RequestId::Number(1));
1276 let params = ElicitFormParams {
1277 mode: Some(ElicitMode::Form),
1278 message: "Enter details".to_string(),
1279 requested_schema: ElicitFormSchema::new().string_field("name", None, true),
1280 meta: None,
1281 };
1282
1283 let result = ctx.elicit_form(params).await;
1284 assert!(result.is_err());
1285 assert!(
1286 result
1287 .unwrap_err()
1288 .to_string()
1289 .contains("Elicitation not available")
1290 );
1291 }
1292
1293 #[tokio::test]
1294 async fn test_elicit_url_without_requester_fails() {
1295 use crate::protocol::ElicitMode;
1296
1297 let ctx = RequestContext::new(RequestId::Number(1));
1298 let params = ElicitUrlParams {
1299 mode: Some(ElicitMode::Url),
1300 elicitation_id: "test-123".to_string(),
1301 message: "Please authorize".to_string(),
1302 url: "https://example.com/auth".to_string(),
1303 meta: None,
1304 };
1305
1306 let result = ctx.elicit_url(params).await;
1307 assert!(result.is_err());
1308 assert!(
1309 result
1310 .unwrap_err()
1311 .to_string()
1312 .contains("Elicitation not available")
1313 );
1314 }
1315
1316 #[tokio::test]
1317 async fn test_confirm_without_requester_fails() {
1318 let ctx = RequestContext::new(RequestId::Number(1));
1319
1320 let result = ctx.confirm("Are you sure?").await;
1321 assert!(result.is_err());
1322 assert!(
1323 result
1324 .unwrap_err()
1325 .to_string()
1326 .contains("Elicitation not available")
1327 );
1328 }
1329
1330 #[tokio::test]
1331 async fn test_send_log_filtered_by_level() {
1332 let (tx, mut rx) = notification_channel(10);
1333 let min_level = Arc::new(RwLock::new(LogLevel::Warning));
1334
1335 let ctx = RequestContext::new(RequestId::Number(1))
1336 .with_notification_sender(tx)
1337 .with_min_log_level(min_level.clone());
1338
1339 ctx.send_log(LoggingMessageParams::new(
1341 LogLevel::Error,
1342 serde_json::Value::Null,
1343 ));
1344 let msg = rx.try_recv();
1345 assert!(msg.is_ok(), "Error should pass through Warning filter");
1346
1347 ctx.send_log(LoggingMessageParams::new(
1349 LogLevel::Warning,
1350 serde_json::Value::Null,
1351 ));
1352 let msg = rx.try_recv();
1353 assert!(msg.is_ok(), "Warning should pass through Warning filter");
1354
1355 ctx.send_log(LoggingMessageParams::new(
1357 LogLevel::Info,
1358 serde_json::Value::Null,
1359 ));
1360 let msg = rx.try_recv();
1361 assert!(msg.is_err(), "Info should be filtered by Warning filter");
1362
1363 ctx.send_log(LoggingMessageParams::new(
1365 LogLevel::Debug,
1366 serde_json::Value::Null,
1367 ));
1368 let msg = rx.try_recv();
1369 assert!(msg.is_err(), "Debug should be filtered by Warning filter");
1370 }
1371
1372 #[tokio::test]
1373 async fn test_send_log_level_updates_dynamically() {
1374 let (tx, mut rx) = notification_channel(10);
1375 let min_level = Arc::new(RwLock::new(LogLevel::Error));
1376
1377 let ctx = RequestContext::new(RequestId::Number(1))
1378 .with_notification_sender(tx)
1379 .with_min_log_level(min_level.clone());
1380
1381 ctx.send_log(LoggingMessageParams::new(
1383 LogLevel::Info,
1384 serde_json::Value::Null,
1385 ));
1386 assert!(
1387 rx.try_recv().is_err(),
1388 "Info should be filtered at Error level"
1389 );
1390
1391 *min_level.write().unwrap() = LogLevel::Debug;
1393
1394 ctx.send_log(LoggingMessageParams::new(
1396 LogLevel::Info,
1397 serde_json::Value::Null,
1398 ));
1399 assert!(
1400 rx.try_recv().is_ok(),
1401 "Info should pass through after level changed to Debug"
1402 );
1403 }
1404
1405 #[tokio::test]
1406 async fn test_send_log_no_min_level_sends_all() {
1407 let (tx, mut rx) = notification_channel(10);
1408
1409 let ctx = RequestContext::new(RequestId::Number(1)).with_notification_sender(tx);
1411
1412 ctx.send_log(LoggingMessageParams::new(
1413 LogLevel::Debug,
1414 serde_json::Value::Null,
1415 ));
1416 assert!(
1417 rx.try_recv().is_ok(),
1418 "Debug should pass when no min level is set"
1419 );
1420 }
1421
1422 #[tokio::test]
1423 #[cfg(feature = "stateless")]
1424 async fn final_request_log_level_is_required_and_filters_per_request() {
1425 let (tx, mut rx) = notification_channel(10);
1426 let mut extensions = Extensions::new();
1427 extensions.insert(crate::stateless::StatelessRequestMeta {
1428 protocol_version: Some(crate::protocol::PROTOCOL_VERSION_2026_07_28.to_string()),
1429 client_capabilities: Some(Default::default()),
1430 ..Default::default()
1431 });
1432 let ctx = RequestContext::new(RequestId::Number(1))
1433 .with_notification_sender(tx.clone())
1434 .with_extensions(Arc::new(extensions));
1435 ctx.send_log(LoggingMessageParams::new(
1436 LogLevel::Emergency,
1437 serde_json::Value::Null,
1438 ));
1439 assert!(
1440 rx.try_recv().is_err(),
1441 "final requests without logLevel must receive no logs"
1442 );
1443
1444 let mut extensions = Extensions::new();
1445 extensions.insert(crate::stateless::StatelessRequestMeta {
1446 protocol_version: Some(crate::protocol::PROTOCOL_VERSION_2026_07_28.to_string()),
1447 client_capabilities: Some(Default::default()),
1448 log_level: Some(crate::stateless::LogLevel::Warning),
1449 ..Default::default()
1450 });
1451 let ctx = RequestContext::new(RequestId::Number(2))
1452 .with_notification_sender(tx)
1453 .with_extensions(Arc::new(extensions));
1454 ctx.send_log(LoggingMessageParams::new(
1455 LogLevel::Info,
1456 serde_json::Value::Null,
1457 ));
1458 assert!(rx.try_recv().is_err(), "Info must be filtered at Warning");
1459 ctx.send_log(LoggingMessageParams::new(
1460 LogLevel::Error,
1461 serde_json::Value::Null,
1462 ));
1463 assert!(rx.try_recv().is_ok(), "Error must pass at Warning");
1464 }
1465
1466 fn make_task_object(id: &str, status: TaskStatus) -> serde_json::Value {
1467 serde_json::json!({
1468 "taskId": id,
1469 "status": status,
1470 "createdAt": "2026-04-24T00:00:00Z",
1471 "lastUpdatedAt": "2026-04-24T00:00:00Z",
1472 "ttl": null
1473 })
1474 }
1475
1476 fn spawn_mock_client(
1477 mut rx: OutgoingRequestReceiver,
1478 responder: impl Fn(&str, serde_json::Value) -> serde_json::Value + Send + 'static,
1479 ) {
1480 tokio::spawn(async move {
1481 while let Some(req) = rx.recv().await {
1482 let response = responder(&req.method, req.params);
1483 let _ = req.response_tx.send(Ok(response));
1484 }
1485 });
1486 }
1487
1488 #[tokio::test]
1489 async fn test_get_task_info_round_trips() {
1490 let (tx, rx) = outgoing_request_channel(10);
1491 spawn_mock_client(rx, |method, params| {
1492 assert_eq!(method, "tasks/get");
1493 let task_id = params["taskId"].as_str().unwrap().to_string();
1494 make_task_object(&task_id, TaskStatus::Working)
1495 });
1496 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1497 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1498
1499 let info = ctx.get_task_info("task-123").await.unwrap();
1500 assert_eq!(info.task_id, "task-123");
1501 assert!(matches!(info.status, TaskStatus::Working));
1502 }
1503
1504 #[tokio::test]
1505 #[allow(deprecated)] async fn test_list_tasks_round_trips() {
1507 let (tx, rx) = outgoing_request_channel(10);
1508 spawn_mock_client(rx, |method, params| {
1509 assert_eq!(method, "tasks/list");
1510 assert_eq!(params["status"], serde_json::json!("working"));
1512 serde_json::json!({
1513 "tasks": [
1514 make_task_object("task-1", TaskStatus::Working),
1515 make_task_object("task-2", TaskStatus::Working),
1516 ]
1517 })
1518 });
1519 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1520 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1521
1522 let result = ctx.list_tasks(Some(TaskStatus::Working)).await.unwrap();
1523 assert_eq!(result.tasks.len(), 2);
1524 assert_eq!(result.tasks[0].task_id, "task-1");
1525 }
1526
1527 #[tokio::test]
1528 async fn test_cancel_task_forwards_reason() {
1529 let (tx, rx) = outgoing_request_channel(10);
1530 spawn_mock_client(rx, |method, params| {
1531 assert_eq!(method, "tasks/cancel");
1532 assert_eq!(params["reason"], serde_json::json!("user requested"));
1533 serde_json::json!({})
1535 });
1536 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1537 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1538
1539 ctx.cancel_task("task-99", Some("user requested".into()))
1540 .await
1541 .expect("empty ack should succeed");
1542 }
1543
1544 #[tokio::test]
1545 async fn test_cancel_task_tolerates_legacy_task_object_ack() {
1546 let (tx, rx) = outgoing_request_channel(10);
1549 spawn_mock_client(rx, |method, _params| {
1550 assert_eq!(method, "tasks/cancel");
1551 make_task_object("task-99", TaskStatus::Cancelled)
1552 });
1553 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1554 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1555
1556 ctx.cancel_task("task-99", None)
1557 .await
1558 .expect("legacy task-object ack should also succeed");
1559 }
1560
1561 #[tokio::test]
1562 async fn test_get_task_info_without_requester_fails() {
1563 let ctx = RequestContext::new(RequestId::Number(1));
1564 let result = ctx.get_task_info("task-1").await;
1565 assert!(result.is_err());
1566 assert!(
1567 result
1568 .unwrap_err()
1569 .to_string()
1570 .contains("Client request not available")
1571 );
1572 }
1573
1574 #[tokio::test]
1575 async fn test_default_request_impl_errors() {
1576 struct OnlySampleAndElicit;
1579
1580 #[async_trait]
1581 impl ClientRequester for OnlySampleAndElicit {
1582 async fn sample(&self, _: CreateMessageParams) -> Result<CreateMessageResult> {
1583 unreachable!()
1584 }
1585 async fn elicit(&self, _: ElicitRequestParams) -> Result<ElicitResult> {
1586 unreachable!()
1587 }
1588 }
1589
1590 let requester: ClientRequesterHandle = Arc::new(OnlySampleAndElicit);
1591 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1592
1593 let err = ctx.get_task_info("x").await.unwrap_err();
1594 assert!(err.to_string().contains("does not support arbitrary"));
1595 }
1596}