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 final_lifecycle: bool,
350}
351
352#[derive(Clone, Default)]
357pub struct Extensions {
358 map: std::collections::HashMap<std::any::TypeId, Arc<dyn std::any::Any + Send + Sync>>,
359}
360
361impl Extensions {
362 pub fn new() -> Self {
364 Self::default()
365 }
366
367 pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) {
371 self.map.insert(std::any::TypeId::of::<T>(), Arc::new(val));
372 }
373
374 pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
378 self.map
379 .get(&std::any::TypeId::of::<T>())
380 .and_then(|val| val.downcast_ref::<T>())
381 }
382
383 pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
385 self.map.contains_key(&std::any::TypeId::of::<T>())
386 }
387
388 pub fn merge(&mut self, other: &Extensions) {
392 for (k, v) in &other.map {
393 self.map.insert(*k, v.clone());
394 }
395 }
396
397 pub fn len(&self) -> usize {
399 self.map.len()
400 }
401
402 pub fn is_empty(&self) -> bool {
404 self.map.is_empty()
405 }
406}
407
408impl std::fmt::Debug for Extensions {
409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410 f.debug_struct("Extensions")
411 .field("len", &self.map.len())
412 .finish()
413 }
414}
415
416impl std::fmt::Debug for RequestContext {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 f.debug_struct("RequestContext")
419 .field("request_id", &self.request_id)
420 .field("progress_token", &self.progress_token)
421 .field("cancelled", &self.cancellation.is_cancelled())
422 .finish()
423 }
424}
425
426impl RequestContext {
427 pub fn new(request_id: RequestId) -> Self {
429 Self {
430 request_id,
431 progress_token: None,
432 cancellation: tokio_util::sync::CancellationToken::new(),
433 notification_tx: None,
434 client_requester: None,
435 final_lifecycle: false,
436 extensions: Arc::new(Extensions::new()),
437 min_log_level: None,
438 }
439 }
440
441 pub fn with_progress_token(mut self, token: ProgressToken) -> Self {
443 self.progress_token = Some(token);
444 self
445 }
446
447 pub fn with_notification_sender(mut self, tx: NotificationSender) -> Self {
449 self.notification_tx = Some(tx);
450 self
451 }
452
453 pub fn with_min_log_level(mut self, level: Arc<RwLock<LogLevel>>) -> Self {
458 self.min_log_level = Some(level);
459 self
460 }
461
462 pub(crate) fn with_final_lifecycle(mut self, final_lifecycle: bool) -> Self {
468 self.final_lifecycle = final_lifecycle;
469 self
470 }
471
472 fn no_requester(&self, what: &str, replacement: &str) -> Error {
474 if self.final_lifecycle {
475 Error::Internal(format!(
476 "{what} is not available on the 2026-07-28 lifecycle: servers do not \
477 initiate JSON-RPC requests. Return {replacement} from the handler \
478 instead, so the client fulfils the request and retries (SEP-2322 \
479 Multi Round-Trip Requests)."
480 ))
481 } else {
482 Error::Internal(format!(
483 "{what} is not available: no client requester is configured. The \
484 transport must provide one; stdio, HTTP, WebSocket, and the \
485 in-process channel transport all do."
486 ))
487 }
488 }
489
490 pub fn with_client_requester(mut self, requester: ClientRequesterHandle) -> Self {
496 self.client_requester = Some(requester);
497 self
498 }
499
500 pub fn with_extensions(mut self, extensions: Arc<Extensions>) -> Self {
504 self.extensions = extensions;
505 self
506 }
507
508 pub fn extension<T: Send + Sync + 'static>(&self) -> Option<&T> {
524 self.extensions.get::<T>()
525 }
526
527 pub fn negotiated_extensions(&self) -> Option<&crate::NegotiatedExtensions> {
532 self.extension()
533 }
534
535 pub fn extensions_mut(&mut self) -> &mut Extensions {
540 Arc::make_mut(&mut self.extensions)
541 }
542
543 pub fn extensions(&self) -> &Extensions {
545 &self.extensions
546 }
547
548 #[cfg(feature = "stateless")]
575 pub fn per_request_meta(&self) -> Option<&crate::stateless::StatelessRequestMeta> {
576 self.extension::<crate::stateless::StatelessRequestMeta>()
577 }
578
579 #[cfg(feature = "stateless")]
581 pub fn mrtr(&self) -> Option<&crate::mrtr::MrtrRequest> {
582 self.extension::<crate::mrtr::MrtrRequest>()
583 }
584
585 #[cfg(feature = "stateless")]
587 pub fn input_responses(&self) -> Option<&crate::protocol::InputResponses> {
588 self.mrtr()
589 .and_then(crate::mrtr::MrtrRequest::input_responses)
590 }
591
592 #[cfg(feature = "stateless")]
594 pub fn request_state(&self) -> Option<&str> {
595 self.mrtr()
596 .and_then(crate::mrtr::MrtrRequest::request_state)
597 }
598
599 #[cfg(feature = "stateless")]
601 pub fn request_state_codec(&self) -> Option<&crate::mrtr::RequestStateCodec> {
602 self.extension::<crate::mrtr::RequestStateCodec>()
603 }
604
605 pub fn request_id(&self) -> &RequestId {
607 &self.request_id
608 }
609
610 pub fn progress_token(&self) -> Option<&ProgressToken> {
612 self.progress_token.as_ref()
613 }
614
615 pub fn is_cancelled(&self) -> bool {
617 self.cancellation.is_cancelled()
618 }
619
620 pub fn cancel(&self) {
622 self.cancellation.cancel();
623 }
624
625 pub async fn cancelled(&self) {
639 self.cancellation.cancelled().await
640 }
641
642 pub fn cancellation_token(&self) -> CancellationToken {
644 CancellationToken {
645 inner: self.cancellation.clone(),
646 }
647 }
648
649 pub fn with_cancellation_token(mut self, token: CancellationToken) -> Self {
657 self.cancellation = token.inner;
658 self
659 }
660
661 pub async fn report_progress(&self, progress: f64, total: Option<f64>, message: Option<&str>) {
665 let Some(token) = &self.progress_token else {
666 return;
667 };
668 let Some(tx) = &self.notification_tx else {
669 return;
670 };
671
672 let params = ProgressParams {
673 progress_token: token.clone(),
674 progress,
675 total,
676 message: message.map(|s| s.to_string()),
677 meta: None,
678 };
679
680 let _ = tx.try_send(ServerNotification::Progress(params));
682 }
683
684 pub fn report_progress_sync(&self, progress: f64, total: Option<f64>, message: Option<&str>) {
688 let Some(token) = &self.progress_token else {
689 return;
690 };
691 let Some(tx) = &self.notification_tx else {
692 return;
693 };
694
695 let params = ProgressParams {
696 progress_token: token.clone(),
697 progress,
698 total,
699 message: message.map(|s| s.to_string()),
700 meta: None,
701 };
702
703 let _ = tx.try_send(ServerNotification::Progress(params));
704 }
705
706 pub fn notify_tools_list_changed(&self) -> bool {
708 self.notification_tx
709 .as_ref()
710 .is_some_and(|tx| tx.try_send(ServerNotification::ToolsListChanged).is_ok())
711 }
712
713 pub fn notify_prompts_list_changed(&self) -> bool {
715 self.notification_tx
716 .as_ref()
717 .is_some_and(|tx| tx.try_send(ServerNotification::PromptsListChanged).is_ok())
718 }
719
720 pub fn notify_resources_list_changed(&self) -> bool {
722 self.notification_tx.as_ref().is_some_and(|tx| {
723 tx.try_send(ServerNotification::ResourcesListChanged)
724 .is_ok()
725 })
726 }
727
728 pub fn notify_resource_updated(&self, uri: impl Into<String>) -> bool {
730 self.notification_tx.as_ref().is_some_and(|tx| {
731 tx.try_send(ServerNotification::ResourceUpdated { uri: uri.into() })
732 .is_ok()
733 })
734 }
735
736 pub fn notify_task_status_changed(
741 &self,
742 params: crate::tasks::TaskStatusNotificationParams,
743 ) -> bool {
744 self.notification_tx.as_ref().is_some_and(|tx| {
745 tx.try_send(ServerNotification::FinalTaskStatusChanged(params))
746 .is_ok()
747 })
748 }
749
750 pub fn send_log(&self, params: LoggingMessageParams) {
767 let Some(tx) = &self.notification_tx else {
768 return;
769 };
770
771 #[cfg(feature = "stateless")]
774 if let Some(meta) = self.per_request_meta()
775 && meta.protocol_version.as_deref()
776 == Some(crate::protocol::PROTOCOL_VERSION_2026_07_28)
777 {
778 let Some(request_level) = meta.log_level else {
779 return;
780 };
781 let request_level = match request_level {
782 crate::stateless::LogLevel::Debug => LogLevel::Debug,
783 crate::stateless::LogLevel::Info => LogLevel::Info,
784 crate::stateless::LogLevel::Notice => LogLevel::Notice,
785 crate::stateless::LogLevel::Warning => LogLevel::Warning,
786 crate::stateless::LogLevel::Error => LogLevel::Error,
787 crate::stateless::LogLevel::Critical => LogLevel::Critical,
788 crate::stateless::LogLevel::Alert => LogLevel::Alert,
789 crate::stateless::LogLevel::Emergency => LogLevel::Emergency,
790 };
791 if params.level > request_level {
792 return;
793 }
794 let _ = tx.try_send(ServerNotification::LogMessage(params));
795 return;
796 }
797
798 if let Some(min_level) = &self.min_log_level
803 && let Ok(min) = min_level.read()
804 && params.level > *min
805 {
806 return;
807 }
808
809 let _ = tx.try_send(ServerNotification::LogMessage(params));
810 }
811
812 pub fn can_sample(&self) -> bool {
817 self.client_requester.is_some()
818 }
819
820 pub async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult> {
859 let requester = self.client_requester.as_ref().ok_or_else(|| {
860 self.no_requester(
861 "Sampling",
862 "`RequestOutcome::input_required` carrying an \
863 `InputRequest::CreateMessage`",
864 )
865 })?;
866
867 requester.sample(params).await
868 }
869
870 pub fn can_elicit(&self) -> bool {
876 self.client_requester.is_some()
877 }
878
879 pub async fn elicit_form(&self, params: ElicitFormParams) -> Result<ElicitResult> {
926 let requester = self.client_requester.as_ref().ok_or_else(|| {
927 self.no_requester(
928 "Elicitation",
929 "`RequestOutcome::input_required` carrying an `InputRequest::Elicit`",
930 )
931 })?;
932
933 requester.elicit(ElicitRequestParams::Form(params)).await
934 }
935
936 pub async fn elicit_url(&self, params: ElicitUrlParams) -> Result<ElicitResult> {
988 let requester = self.client_requester.as_ref().ok_or_else(|| {
989 self.no_requester(
990 "Elicitation",
991 "`RequestOutcome::input_required` carrying an `InputRequest::Elicit`",
992 )
993 })?;
994
995 requester.elicit(ElicitRequestParams::Url(params)).await
996 }
997
998 pub async fn confirm(&self, message: impl Into<String>) -> Result<bool> {
1022 use crate::protocol::{ElicitAction, ElicitFormParams, ElicitFormSchema, ElicitMode};
1023
1024 let params = ElicitFormParams {
1025 mode: Some(ElicitMode::Form),
1026 message: message.into(),
1027 requested_schema: ElicitFormSchema::new().boolean_field_with_default(
1028 "confirm",
1029 Some("Confirm this action"),
1030 true,
1031 false,
1032 ),
1033 meta: None,
1034 };
1035
1036 let result = self.elicit_form(params).await?;
1037 Ok(result.action == ElicitAction::Accept)
1038 }
1039
1040 #[deprecated(
1050 since = "0.13.0",
1051 note = "final SEP-2663 removes tasks/list; a conforming peer answers \
1052 MethodNotFound (-32601). Only useful against legacy SEP-1686 \
1053 clients."
1054 )]
1055 pub async fn list_tasks(&self, status: Option<TaskStatus>) -> Result<ListTasksResult> {
1056 let params = ListTasksParams {
1057 status,
1058 cursor: None,
1059 meta: None,
1060 };
1061 let value = self
1062 .request_raw("tasks/list", serde_json::to_value(¶ms)?)
1063 .await?;
1064 serde_json::from_value(value)
1065 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/list: {e}")))
1066 }
1067
1068 pub async fn get_task_info(&self, task_id: impl Into<String>) -> Result<TaskObject> {
1073 let params = GetTaskInfoParams {
1074 task_id: task_id.into(),
1075 meta: None,
1076 };
1077 let value = self
1078 .request_raw("tasks/get", serde_json::to_value(¶ms)?)
1079 .await?;
1080 serde_json::from_value(value)
1081 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/get: {e}")))
1082 }
1083
1084 #[deprecated(
1093 since = "0.13.0",
1094 note = "final SEP-2663 removes tasks/result (results are inlined in \
1095 the tasks/get DetailedTask); a conforming peer answers \
1096 MethodNotFound (-32601). Only useful against legacy SEP-1686 \
1097 clients."
1098 )]
1099 pub async fn get_task_result(&self, task_id: impl Into<String>) -> Result<CallToolResult> {
1100 let params = GetTaskResultParams {
1101 task_id: task_id.into(),
1102 meta: None,
1103 };
1104 let value = self
1105 .request_raw("tasks/result", serde_json::to_value(¶ms)?)
1106 .await?;
1107 serde_json::from_value(value)
1108 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/result: {e}")))
1109 }
1110
1111 pub async fn cancel_task(
1118 &self,
1119 task_id: impl Into<String>,
1120 reason: Option<String>,
1121 ) -> Result<()> {
1122 let params = CancelTaskParams {
1123 task_id: task_id.into(),
1124 reason,
1125 meta: None,
1126 };
1127 self.request_raw("tasks/cancel", serde_json::to_value(¶ms)?)
1128 .await?;
1129 Ok(())
1130 }
1131
1132 pub async fn request_raw(
1138 &self,
1139 method: &str,
1140 params: serde_json::Value,
1141 ) -> Result<serde_json::Value> {
1142 let requester = self.client_requester.as_ref().ok_or_else(|| {
1143 self.no_requester(
1144 "A server-initiated client request",
1145 "`RequestOutcome::input_required`",
1146 )
1147 })?;
1148 requester.request(method.to_string(), params).await
1149 }
1150}
1151
1152#[derive(Clone, Debug, Default)]
1158pub struct CancellationToken {
1159 inner: tokio_util::sync::CancellationToken,
1160}
1161
1162impl CancellationToken {
1163 pub fn new() -> Self {
1165 Self::default()
1166 }
1167
1168 pub fn is_cancelled(&self) -> bool {
1170 self.inner.is_cancelled()
1171 }
1172
1173 pub fn cancel(&self) {
1175 self.inner.cancel();
1176 }
1177
1178 pub async fn cancelled(&self) {
1182 self.inner.cancelled().await
1183 }
1184}
1185
1186#[derive(Default)]
1188pub struct RequestContextBuilder {
1189 request_id: Option<RequestId>,
1190 progress_token: Option<ProgressToken>,
1191 notification_tx: Option<NotificationSender>,
1192 client_requester: Option<ClientRequesterHandle>,
1193 min_log_level: Option<Arc<RwLock<LogLevel>>>,
1194}
1195
1196impl RequestContextBuilder {
1197 pub fn new() -> Self {
1199 Self::default()
1200 }
1201
1202 pub fn request_id(mut self, id: RequestId) -> Self {
1204 self.request_id = Some(id);
1205 self
1206 }
1207
1208 pub fn progress_token(mut self, token: ProgressToken) -> Self {
1210 self.progress_token = Some(token);
1211 self
1212 }
1213
1214 pub fn notification_sender(mut self, tx: NotificationSender) -> Self {
1216 self.notification_tx = Some(tx);
1217 self
1218 }
1219
1220 pub fn client_requester(mut self, requester: ClientRequesterHandle) -> Self {
1222 self.client_requester = Some(requester);
1223 self
1224 }
1225
1226 pub fn min_log_level(mut self, level: Arc<RwLock<LogLevel>>) -> Self {
1228 self.min_log_level = Some(level);
1229 self
1230 }
1231
1232 pub fn build(self) -> RequestContext {
1236 let mut ctx = RequestContext::new(self.request_id.expect("request_id is required"));
1237 if let Some(token) = self.progress_token {
1238 ctx = ctx.with_progress_token(token);
1239 }
1240 if let Some(tx) = self.notification_tx {
1241 ctx = ctx.with_notification_sender(tx);
1242 }
1243 if let Some(requester) = self.client_requester {
1244 ctx = ctx.with_client_requester(requester);
1245 }
1246 if let Some(level) = self.min_log_level {
1247 ctx = ctx.with_min_log_level(level);
1248 }
1249 ctx
1250 }
1251}
1252
1253#[cfg(test)]
1254mod tests {
1255 use super::*;
1256
1257 #[test]
1258 fn test_cancellation() {
1259 let ctx = RequestContext::new(RequestId::Number(1));
1260 assert!(!ctx.is_cancelled());
1261
1262 let token = ctx.cancellation_token();
1263 assert!(!token.is_cancelled());
1264
1265 ctx.cancel();
1266 assert!(ctx.is_cancelled());
1267 assert!(token.is_cancelled());
1268 }
1269
1270 #[tokio::test]
1271 async fn test_progress_reporting() {
1272 let (tx, mut rx) = notification_channel(10);
1273
1274 let ctx = RequestContext::new(RequestId::Number(1))
1275 .with_progress_token(ProgressToken::Number(42))
1276 .with_notification_sender(tx);
1277
1278 ctx.report_progress(50.0, Some(100.0), Some("Halfway"))
1279 .await;
1280
1281 let notification = rx.recv().await.unwrap();
1282 match notification {
1283 ServerNotification::Progress(params) => {
1284 assert_eq!(params.progress, 50.0);
1285 assert_eq!(params.total, Some(100.0));
1286 assert_eq!(params.message.as_deref(), Some("Halfway"));
1287 }
1288 _ => panic!("Expected Progress notification"),
1289 }
1290 }
1291
1292 #[tokio::test]
1293 async fn test_progress_no_token() {
1294 let (tx, mut rx) = notification_channel(10);
1295
1296 let ctx = RequestContext::new(RequestId::Number(1)).with_notification_sender(tx);
1298
1299 ctx.report_progress(50.0, Some(100.0), None).await;
1300
1301 assert!(rx.try_recv().is_err());
1303 }
1304
1305 #[test]
1306 fn test_builder() {
1307 let (tx, _rx) = notification_channel(10);
1308
1309 let ctx = RequestContextBuilder::new()
1310 .request_id(RequestId::String("req-1".to_string()))
1311 .progress_token(ProgressToken::String("prog-1".to_string()))
1312 .notification_sender(tx)
1313 .build();
1314
1315 assert_eq!(ctx.request_id(), &RequestId::String("req-1".to_string()));
1316 assert!(ctx.progress_token().is_some());
1317 }
1318
1319 #[test]
1320 fn test_can_sample_without_requester() {
1321 let ctx = RequestContext::new(RequestId::Number(1));
1322 assert!(!ctx.can_sample());
1323 }
1324
1325 #[test]
1326 fn test_can_sample_with_requester() {
1327 let (request_tx, _rx) = outgoing_request_channel(10);
1328 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1329
1330 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1331 assert!(ctx.can_sample());
1332 }
1333
1334 #[tokio::test]
1335 async fn test_sample_without_requester_fails() {
1336 use crate::protocol::{CreateMessageParams, SamplingMessage};
1337
1338 let ctx = RequestContext::new(RequestId::Number(1));
1339 let params = CreateMessageParams::new(vec![SamplingMessage::user("test")], 100);
1340
1341 let result = ctx.sample(params).await;
1342 assert!(result.is_err());
1343 assert!(
1344 result
1345 .unwrap_err()
1346 .to_string()
1347 .contains("Sampling is not available: no client requester is configured")
1348 );
1349 }
1350
1351 #[test]
1352 fn test_builder_with_client_requester() {
1353 let (request_tx, _rx) = outgoing_request_channel(10);
1354 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1355
1356 let ctx = RequestContextBuilder::new()
1357 .request_id(RequestId::Number(1))
1358 .client_requester(requester)
1359 .build();
1360
1361 assert!(ctx.can_sample());
1362 }
1363
1364 #[test]
1365 fn test_can_elicit_without_requester() {
1366 let ctx = RequestContext::new(RequestId::Number(1));
1367 assert!(!ctx.can_elicit());
1368 }
1369
1370 #[test]
1371 fn test_can_elicit_with_requester() {
1372 let (request_tx, _rx) = outgoing_request_channel(10);
1373 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1374
1375 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1376 assert!(ctx.can_elicit());
1377 }
1378
1379 #[tokio::test]
1380 async fn test_elicit_form_without_requester_fails() {
1381 use crate::protocol::{ElicitFormSchema, ElicitMode};
1382
1383 let ctx = RequestContext::new(RequestId::Number(1));
1384 let params = ElicitFormParams {
1385 mode: Some(ElicitMode::Form),
1386 message: "Enter details".to_string(),
1387 requested_schema: ElicitFormSchema::new().string_field("name", None, true),
1388 meta: None,
1389 };
1390
1391 let result = ctx.elicit_form(params).await;
1392 assert!(result.is_err());
1393 assert!(
1394 result
1395 .unwrap_err()
1396 .to_string()
1397 .contains("Elicitation is not available: no client requester is configured")
1398 );
1399 }
1400
1401 #[tokio::test]
1402 async fn test_elicit_url_without_requester_fails() {
1403 use crate::protocol::ElicitMode;
1404
1405 let ctx = RequestContext::new(RequestId::Number(1));
1406 let params = ElicitUrlParams {
1407 mode: Some(ElicitMode::Url),
1408 elicitation_id: "test-123".to_string(),
1409 message: "Please authorize".to_string(),
1410 url: "https://example.com/auth".to_string(),
1411 meta: None,
1412 };
1413
1414 let result = ctx.elicit_url(params).await;
1415 assert!(result.is_err());
1416 assert!(
1417 result
1418 .unwrap_err()
1419 .to_string()
1420 .contains("Elicitation is not available: no client requester is configured")
1421 );
1422 }
1423
1424 #[tokio::test]
1425 async fn test_confirm_without_requester_fails() {
1426 let ctx = RequestContext::new(RequestId::Number(1));
1427
1428 let result = ctx.confirm("Are you sure?").await;
1429 assert!(result.is_err());
1430 assert!(
1431 result
1432 .unwrap_err()
1433 .to_string()
1434 .contains("Elicitation is not available: no client requester is configured")
1435 );
1436 }
1437
1438 #[tokio::test]
1439 async fn test_send_log_filtered_by_level() {
1440 let (tx, mut rx) = notification_channel(10);
1441 let min_level = Arc::new(RwLock::new(LogLevel::Warning));
1442
1443 let ctx = RequestContext::new(RequestId::Number(1))
1444 .with_notification_sender(tx)
1445 .with_min_log_level(min_level.clone());
1446
1447 ctx.send_log(LoggingMessageParams::new(
1449 LogLevel::Error,
1450 serde_json::Value::Null,
1451 ));
1452 let msg = rx.try_recv();
1453 assert!(msg.is_ok(), "Error should pass through Warning filter");
1454
1455 ctx.send_log(LoggingMessageParams::new(
1457 LogLevel::Warning,
1458 serde_json::Value::Null,
1459 ));
1460 let msg = rx.try_recv();
1461 assert!(msg.is_ok(), "Warning should pass through Warning filter");
1462
1463 ctx.send_log(LoggingMessageParams::new(
1465 LogLevel::Info,
1466 serde_json::Value::Null,
1467 ));
1468 let msg = rx.try_recv();
1469 assert!(msg.is_err(), "Info should be filtered by Warning filter");
1470
1471 ctx.send_log(LoggingMessageParams::new(
1473 LogLevel::Debug,
1474 serde_json::Value::Null,
1475 ));
1476 let msg = rx.try_recv();
1477 assert!(msg.is_err(), "Debug should be filtered by Warning filter");
1478 }
1479
1480 #[tokio::test]
1481 async fn test_send_log_level_updates_dynamically() {
1482 let (tx, mut rx) = notification_channel(10);
1483 let min_level = Arc::new(RwLock::new(LogLevel::Error));
1484
1485 let ctx = RequestContext::new(RequestId::Number(1))
1486 .with_notification_sender(tx)
1487 .with_min_log_level(min_level.clone());
1488
1489 ctx.send_log(LoggingMessageParams::new(
1491 LogLevel::Info,
1492 serde_json::Value::Null,
1493 ));
1494 assert!(
1495 rx.try_recv().is_err(),
1496 "Info should be filtered at Error level"
1497 );
1498
1499 *min_level.write().unwrap() = LogLevel::Debug;
1501
1502 ctx.send_log(LoggingMessageParams::new(
1504 LogLevel::Info,
1505 serde_json::Value::Null,
1506 ));
1507 assert!(
1508 rx.try_recv().is_ok(),
1509 "Info should pass through after level changed to Debug"
1510 );
1511 }
1512
1513 #[tokio::test]
1514 async fn test_send_log_no_min_level_sends_all() {
1515 let (tx, mut rx) = notification_channel(10);
1516
1517 let ctx = RequestContext::new(RequestId::Number(1)).with_notification_sender(tx);
1519
1520 ctx.send_log(LoggingMessageParams::new(
1521 LogLevel::Debug,
1522 serde_json::Value::Null,
1523 ));
1524 assert!(
1525 rx.try_recv().is_ok(),
1526 "Debug should pass when no min level is set"
1527 );
1528 }
1529
1530 #[tokio::test]
1531 #[cfg(feature = "stateless")]
1532 async fn final_request_log_level_is_required_and_filters_per_request() {
1533 let (tx, mut rx) = notification_channel(10);
1534 let mut extensions = Extensions::new();
1535 extensions.insert(crate::stateless::StatelessRequestMeta {
1536 protocol_version: Some(crate::protocol::PROTOCOL_VERSION_2026_07_28.to_string()),
1537 client_capabilities: Some(Default::default()),
1538 ..Default::default()
1539 });
1540 let ctx = RequestContext::new(RequestId::Number(1))
1541 .with_notification_sender(tx.clone())
1542 .with_extensions(Arc::new(extensions));
1543 ctx.send_log(LoggingMessageParams::new(
1544 LogLevel::Emergency,
1545 serde_json::Value::Null,
1546 ));
1547 assert!(
1548 rx.try_recv().is_err(),
1549 "final requests without logLevel must receive no logs"
1550 );
1551
1552 let mut extensions = Extensions::new();
1553 extensions.insert(crate::stateless::StatelessRequestMeta {
1554 protocol_version: Some(crate::protocol::PROTOCOL_VERSION_2026_07_28.to_string()),
1555 client_capabilities: Some(Default::default()),
1556 log_level: Some(crate::stateless::LogLevel::Warning),
1557 ..Default::default()
1558 });
1559 let ctx = RequestContext::new(RequestId::Number(2))
1560 .with_notification_sender(tx)
1561 .with_extensions(Arc::new(extensions));
1562 ctx.send_log(LoggingMessageParams::new(
1563 LogLevel::Info,
1564 serde_json::Value::Null,
1565 ));
1566 assert!(rx.try_recv().is_err(), "Info must be filtered at Warning");
1567 ctx.send_log(LoggingMessageParams::new(
1568 LogLevel::Error,
1569 serde_json::Value::Null,
1570 ));
1571 assert!(rx.try_recv().is_ok(), "Error must pass at Warning");
1572 }
1573
1574 fn make_task_object(id: &str, status: TaskStatus) -> serde_json::Value {
1575 serde_json::json!({
1576 "taskId": id,
1577 "status": status,
1578 "createdAt": "2026-04-24T00:00:00Z",
1579 "lastUpdatedAt": "2026-04-24T00:00:00Z",
1580 "ttl": null
1581 })
1582 }
1583
1584 fn spawn_mock_client(
1585 mut rx: OutgoingRequestReceiver,
1586 responder: impl Fn(&str, serde_json::Value) -> serde_json::Value + Send + 'static,
1587 ) {
1588 tokio::spawn(async move {
1589 while let Some(req) = rx.recv().await {
1590 let response = responder(&req.method, req.params);
1591 let _ = req.response_tx.send(Ok(response));
1592 }
1593 });
1594 }
1595
1596 #[tokio::test]
1597 async fn test_get_task_info_round_trips() {
1598 let (tx, rx) = outgoing_request_channel(10);
1599 spawn_mock_client(rx, |method, params| {
1600 assert_eq!(method, "tasks/get");
1601 let task_id = params["taskId"].as_str().unwrap().to_string();
1602 make_task_object(&task_id, TaskStatus::Working)
1603 });
1604 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1605 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1606
1607 let info = ctx.get_task_info("task-123").await.unwrap();
1608 assert_eq!(info.task_id, "task-123");
1609 assert!(matches!(info.status, TaskStatus::Working));
1610 }
1611
1612 #[tokio::test]
1613 #[allow(deprecated)] async fn test_list_tasks_round_trips() {
1615 let (tx, rx) = outgoing_request_channel(10);
1616 spawn_mock_client(rx, |method, params| {
1617 assert_eq!(method, "tasks/list");
1618 assert_eq!(params["status"], serde_json::json!("working"));
1620 serde_json::json!({
1621 "tasks": [
1622 make_task_object("task-1", TaskStatus::Working),
1623 make_task_object("task-2", TaskStatus::Working),
1624 ]
1625 })
1626 });
1627 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1628 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1629
1630 let result = ctx.list_tasks(Some(TaskStatus::Working)).await.unwrap();
1631 assert_eq!(result.tasks.len(), 2);
1632 assert_eq!(result.tasks[0].task_id, "task-1");
1633 }
1634
1635 #[tokio::test]
1636 async fn test_cancel_task_forwards_reason() {
1637 let (tx, rx) = outgoing_request_channel(10);
1638 spawn_mock_client(rx, |method, params| {
1639 assert_eq!(method, "tasks/cancel");
1640 assert_eq!(params["reason"], serde_json::json!("user requested"));
1641 serde_json::json!({})
1643 });
1644 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1645 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1646
1647 ctx.cancel_task("task-99", Some("user requested".into()))
1648 .await
1649 .expect("empty ack should succeed");
1650 }
1651
1652 #[tokio::test]
1653 async fn test_cancel_task_tolerates_legacy_task_object_ack() {
1654 let (tx, rx) = outgoing_request_channel(10);
1657 spawn_mock_client(rx, |method, _params| {
1658 assert_eq!(method, "tasks/cancel");
1659 make_task_object("task-99", TaskStatus::Cancelled)
1660 });
1661 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1662 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1663
1664 ctx.cancel_task("task-99", None)
1665 .await
1666 .expect("legacy task-object ack should also succeed");
1667 }
1668
1669 #[tokio::test]
1670 async fn test_get_task_info_without_requester_fails() {
1671 let ctx = RequestContext::new(RequestId::Number(1));
1672 let result = ctx.get_task_info("task-1").await;
1673 assert!(result.is_err());
1674 assert!(
1675 result
1676 .unwrap_err()
1677 .to_string()
1678 .contains("no client requester is configured")
1679 );
1680 }
1681
1682 #[tokio::test]
1683 async fn test_default_request_impl_errors() {
1684 struct OnlySampleAndElicit;
1687
1688 #[async_trait]
1689 impl ClientRequester for OnlySampleAndElicit {
1690 async fn sample(&self, _: CreateMessageParams) -> Result<CreateMessageResult> {
1691 unreachable!()
1692 }
1693 async fn elicit(&self, _: ElicitRequestParams) -> Result<ElicitResult> {
1694 unreachable!()
1695 }
1696 }
1697
1698 let requester: ClientRequesterHandle = Arc::new(OnlySampleAndElicit);
1699 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1700
1701 let err = ctx.get_task_info("x").await.unwrap_err();
1702 assert!(err.to_string().contains("does not support arbitrary"));
1703 }
1704}
1705
1706#[cfg(test)]
1707mod final_lifecycle_diagnostics_tests {
1708 use super::*;
1709 use crate::protocol::{ElicitFormParams, ElicitFormSchema};
1710
1711 fn params() -> ElicitFormParams {
1712 ElicitFormParams {
1713 mode: None,
1714 message: "confirm?".to_string(),
1715 requested_schema: ElicitFormSchema::new(),
1716 meta: None,
1717 }
1718 }
1719
1720 fn sampling_params() -> CreateMessageParams {
1721 CreateMessageParams {
1722 messages: Vec::new(),
1723 max_tokens: 1,
1724 system_prompt: None,
1725 temperature: None,
1726 stop_sequences: Vec::new(),
1727 model_preferences: None,
1728 include_context: None,
1729 metadata: None,
1730 tools: None,
1731 tool_choice: None,
1732 task: None,
1733 meta: None,
1734 }
1735 }
1736
1737 #[tokio::test]
1742 async fn final_lifecycle_elicitation_error_names_the_replacement() {
1743 let ctx = RequestContext::new(RequestId::Number(1)).with_final_lifecycle(true);
1744
1745 let error = ctx.elicit_form(params()).await.unwrap_err().to_string();
1746 assert!(
1747 error.contains("2026-07-28"),
1748 "must name the lifecycle: {error}"
1749 );
1750 assert!(
1751 error.contains("do not \ninitiate JSON-RPC requests")
1752 || error.contains("do not initiate JSON-RPC requests"),
1753 "must explain the cause: {error}"
1754 );
1755 assert!(
1756 error.contains("RequestOutcome::input_required"),
1757 "must name the replacement API: {error}"
1758 );
1759 assert!(
1760 error.contains("SEP-2322"),
1761 "must cite the mechanism: {error}"
1762 );
1763 assert!(
1764 !error.contains("no client requester is configured"),
1765 "must not blame configuration: {error}"
1766 );
1767 }
1768
1769 #[tokio::test]
1770 async fn final_lifecycle_sampling_error_names_the_replacement() {
1771 let ctx = RequestContext::new(RequestId::Number(1)).with_final_lifecycle(true);
1772 let error = ctx.sample(sampling_params()).await.unwrap_err().to_string();
1773 assert!(error.contains("2026-07-28"), "{error}");
1774 assert!(error.contains("RequestOutcome::input_required"), "{error}");
1775 }
1776
1777 #[tokio::test]
1780 async fn legacy_lifecycle_keeps_the_configuration_error() {
1781 let ctx = RequestContext::new(RequestId::Number(1));
1782
1783 let error = ctx.elicit_form(params()).await.unwrap_err().to_string();
1784 assert!(
1785 error.contains("no client requester is configured"),
1786 "a legacy transport without a requester is misconfigured: {error}"
1787 );
1788 assert!(
1789 !error.contains("2026-07-28"),
1790 "must not blame the protocol: {error}"
1791 );
1792 }
1793
1794 #[tokio::test]
1797 async fn capability_probes_report_false_on_the_final_lifecycle() {
1798 let ctx = RequestContext::new(RequestId::Number(1)).with_final_lifecycle(true);
1799 assert!(!ctx.can_elicit());
1800 assert!(!ctx.can_sample());
1801 }
1802}