1use std::collections::HashSet;
117use std::sync::atomic::{AtomicI64, Ordering};
118use std::sync::{Arc, RwLock};
119
120use async_trait::async_trait;
121use tokio::sync::mpsc;
122
123use crate::error::{Error, Result};
124use crate::protocol::{
125 CallToolResult, CancelTaskParams, CreateMessageParams, CreateMessageResult, ElicitFormParams,
126 ElicitRequestParams, ElicitResult, ElicitUrlParams, GetTaskInfoParams, GetTaskResultParams,
127 ListTasksParams, ListTasksResult, LogLevel, LoggingMessageParams, ProgressParams,
128 ProgressToken, RequestId, TaskObject, TaskStatus,
129};
130use crate::session::SessionState;
131
132#[derive(Debug, Clone)]
134#[non_exhaustive]
135pub enum ServerNotification {
136 Progress(ProgressParams),
138 LogMessage(LoggingMessageParams),
140 ResourceUpdated {
142 uri: String,
144 },
145 ResourcesListChanged,
147 ToolsListChanged,
149 PromptsListChanged,
151 TaskStatusChanged(crate::protocol::TaskStatusParams),
153 FinalTaskStatusChanged(crate::tasks::TaskStatusNotificationParams),
159}
160
161pub type NotificationSender = mpsc::Sender<ServerNotification>;
163
164pub type NotificationReceiver = mpsc::Receiver<ServerNotification>;
166
167pub fn notification_channel(buffer: usize) -> (NotificationSender, NotificationReceiver) {
169 mpsc::channel(buffer)
170}
171
172#[async_trait]
182pub trait ClientRequester: Send + Sync {
183 async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult>;
187
188 async fn elicit(&self, params: ElicitRequestParams) -> Result<ElicitResult>;
195
196 async fn request(
204 &self,
205 method: String,
206 params: serde_json::Value,
207 ) -> Result<serde_json::Value> {
208 let _ = (method, params);
209 Err(Error::Internal(
210 "ClientRequester does not support arbitrary requests".to_string(),
211 ))
212 }
213}
214
215pub type ClientRequesterHandle = Arc<dyn ClientRequester>;
217
218#[derive(Debug)]
220pub struct OutgoingRequest {
221 pub id: RequestId,
223 pub method: String,
225 pub params: serde_json::Value,
227 pub response_tx: tokio::sync::oneshot::Sender<Result<serde_json::Value>>,
229}
230
231pub type OutgoingRequestSender = mpsc::Sender<OutgoingRequest>;
233
234pub type OutgoingRequestReceiver = mpsc::Receiver<OutgoingRequest>;
236
237pub fn outgoing_request_channel(buffer: usize) -> (OutgoingRequestSender, OutgoingRequestReceiver) {
239 mpsc::channel(buffer)
240}
241
242#[derive(Clone)]
244pub struct ChannelClientRequester {
245 request_tx: OutgoingRequestSender,
246 next_id: Arc<AtomicI64>,
247}
248
249impl ChannelClientRequester {
250 pub fn new(request_tx: OutgoingRequestSender) -> Self {
252 Self {
253 request_tx,
254 next_id: Arc::new(AtomicI64::new(1)),
255 }
256 }
257
258 #[cfg(feature = "http")]
264 pub(crate) fn with_id_allocator(
265 request_tx: OutgoingRequestSender,
266 next_id: Arc<AtomicI64>,
267 ) -> Self {
268 Self {
269 request_tx,
270 next_id,
271 }
272 }
273
274 fn next_request_id(&self) -> RequestId {
275 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
276 RequestId::Number(id)
277 }
278}
279
280impl ChannelClientRequester {
281 async fn dispatch(&self, method: &str, params: serde_json::Value) -> Result<serde_json::Value> {
282 let id = self.next_request_id();
283 let (response_tx, response_rx) = tokio::sync::oneshot::channel();
284
285 let request = OutgoingRequest {
286 id,
287 method: method.to_string(),
288 params,
289 response_tx,
290 };
291
292 self.request_tx
293 .send(request)
294 .await
295 .map_err(|_| Error::Internal("Failed to send request: channel closed".to_string()))?;
296
297 response_rx.await.map_err(|_| {
298 Error::Internal("Failed to receive response: channel closed".to_string())
299 })?
300 }
301}
302
303#[async_trait]
304impl ClientRequester for ChannelClientRequester {
305 async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult> {
306 let params_json = serde_json::to_value(¶ms)
307 .map_err(|e| Error::Internal(format!("Failed to serialize params: {}", e)))?;
308 let response = self.dispatch("sampling/createMessage", params_json).await?;
309 serde_json::from_value(response)
310 .map_err(|e| Error::Internal(format!("Failed to deserialize response: {}", e)))
311 }
312
313 async fn elicit(&self, params: ElicitRequestParams) -> Result<ElicitResult> {
314 let params_json = serde_json::to_value(¶ms)
315 .map_err(|e| Error::Internal(format!("Failed to serialize params: {}", e)))?;
316 let response = self.dispatch("elicitation/create", params_json).await?;
317 serde_json::from_value(response)
318 .map_err(|e| Error::Internal(format!("Failed to deserialize response: {}", e)))
319 }
320
321 async fn request(
322 &self,
323 method: String,
324 params: serde_json::Value,
325 ) -> Result<serde_json::Value> {
326 self.dispatch(&method, params).await
327 }
328}
329
330#[derive(Clone)]
332pub struct RequestContext {
333 request_id: RequestId,
335 progress_token: Option<ProgressToken>,
337 cancellation: tokio_util::sync::CancellationToken,
339 notification_tx: Option<NotificationSender>,
341 client_requester: Option<ClientRequesterHandle>,
343 extensions: Arc<Extensions>,
345 session: Option<SessionState>,
350 min_log_level: Option<Arc<RwLock<LogLevel>>>,
352 resource_subscriptions: Option<Arc<RwLock<HashSet<String>>>>,
357 final_lifecycle: bool,
362}
363
364#[derive(Clone, Default)]
369pub struct Extensions {
370 map: std::collections::HashMap<std::any::TypeId, Arc<dyn std::any::Any + Send + Sync>>,
371}
372
373impl Extensions {
374 pub fn new() -> Self {
376 Self::default()
377 }
378
379 pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) {
383 self.map.insert(std::any::TypeId::of::<T>(), Arc::new(val));
384 }
385
386 pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
390 self.map
391 .get(&std::any::TypeId::of::<T>())
392 .and_then(|val| val.downcast_ref::<T>())
393 }
394
395 pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
397 self.map.contains_key(&std::any::TypeId::of::<T>())
398 }
399
400 pub fn merge(&mut self, other: &Extensions) {
404 for (k, v) in &other.map {
405 self.map.insert(*k, v.clone());
406 }
407 }
408
409 pub fn len(&self) -> usize {
411 self.map.len()
412 }
413
414 pub fn is_empty(&self) -> bool {
416 self.map.is_empty()
417 }
418}
419
420impl std::fmt::Debug for Extensions {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 f.debug_struct("Extensions")
423 .field("len", &self.map.len())
424 .finish()
425 }
426}
427
428impl std::fmt::Debug for RequestContext {
429 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
430 f.debug_struct("RequestContext")
431 .field("request_id", &self.request_id)
432 .field("progress_token", &self.progress_token)
433 .field("cancelled", &self.cancellation.is_cancelled())
434 .finish()
435 }
436}
437
438impl RequestContext {
439 pub fn new(request_id: RequestId) -> Self {
441 Self {
442 request_id,
443 progress_token: None,
444 cancellation: tokio_util::sync::CancellationToken::new(),
445 notification_tx: None,
446 client_requester: None,
447 final_lifecycle: false,
448 extensions: Arc::new(Extensions::new()),
449 session: None,
450 min_log_level: None,
451 resource_subscriptions: None,
452 }
453 }
454
455 pub fn with_progress_token(mut self, token: ProgressToken) -> Self {
457 self.progress_token = Some(token);
458 self
459 }
460
461 pub fn with_notification_sender(mut self, tx: NotificationSender) -> Self {
463 self.notification_tx = Some(tx);
464 self
465 }
466
467 pub fn with_min_log_level(mut self, level: Arc<RwLock<LogLevel>>) -> Self {
472 self.min_log_level = Some(level);
473 self
474 }
475
476 pub(crate) fn with_final_lifecycle(mut self, final_lifecycle: bool) -> Self {
484 self.final_lifecycle = final_lifecycle;
485 self
486 }
487
488 pub(crate) fn with_resource_subscriptions(
494 mut self,
495 subscriptions: Arc<RwLock<HashSet<String>>>,
496 ) -> Self {
497 self.resource_subscriptions = Some(subscriptions);
498 self
499 }
500
501 fn no_requester(&self, what: &str, replacement: &str) -> Error {
503 if self.final_lifecycle {
504 Error::Internal(format!(
505 "{what} is not available on the 2026-07-28 lifecycle: servers do not \
506 initiate JSON-RPC requests. Return {replacement} from the handler \
507 instead, so the client fulfils the request and retries (SEP-2322 \
508 Multi Round-Trip Requests)."
509 ))
510 } else {
511 Error::Internal(format!(
512 "{what} is not available: no client requester is configured. The \
513 transport must provide one; stdio, HTTP, WebSocket, and the \
514 in-process channel transport all do."
515 ))
516 }
517 }
518
519 pub fn with_client_requester(mut self, requester: ClientRequesterHandle) -> Self {
525 self.client_requester = Some(requester);
526 self
527 }
528
529 pub fn with_extensions(mut self, extensions: Arc<Extensions>) -> Self {
533 self.extensions = extensions;
534 self
535 }
536
537 pub(crate) fn with_session(mut self, session: SessionState) -> Self {
539 self.session = Some(session);
540 self
541 }
542
543 pub fn extension<T: Send + Sync + 'static>(&self) -> Option<&T> {
559 self.extensions.get::<T>()
560 }
561
562 pub fn negotiated_extensions(&self) -> Option<&crate::NegotiatedExtensions> {
567 self.extension()
568 }
569
570 pub fn extensions_mut(&mut self) -> &mut Extensions {
575 Arc::make_mut(&mut self.extensions)
576 }
577
578 pub fn extensions(&self) -> &Extensions {
580 &self.extensions
581 }
582
583 pub fn session(&self) -> Option<&SessionState> {
590 self.session.as_ref()
591 }
592
593 #[cfg(feature = "stateless")]
620 pub fn per_request_meta(&self) -> Option<&crate::stateless::StatelessRequestMeta> {
621 self.extension::<crate::stateless::StatelessRequestMeta>()
622 }
623
624 #[cfg(feature = "stateless")]
626 pub fn mrtr(&self) -> Option<&crate::mrtr::MrtrRequest> {
627 self.extension::<crate::mrtr::MrtrRequest>()
628 }
629
630 #[cfg(feature = "stateless")]
632 pub fn input_responses(&self) -> Option<&crate::protocol::InputResponses> {
633 self.mrtr()
634 .and_then(crate::mrtr::MrtrRequest::input_responses)
635 }
636
637 #[cfg(feature = "stateless")]
639 pub fn request_state(&self) -> Option<&str> {
640 self.mrtr()
641 .and_then(crate::mrtr::MrtrRequest::request_state)
642 }
643
644 #[cfg(feature = "stateless")]
646 pub fn request_state_codec(&self) -> Option<&crate::mrtr::RequestStateCodec> {
647 self.extension::<crate::mrtr::RequestStateCodec>()
648 }
649
650 pub fn request_id(&self) -> &RequestId {
652 &self.request_id
653 }
654
655 pub fn progress_token(&self) -> Option<&ProgressToken> {
657 self.progress_token.as_ref()
658 }
659
660 pub fn is_cancelled(&self) -> bool {
662 self.cancellation.is_cancelled()
663 }
664
665 pub fn cancel(&self) {
667 self.cancellation.cancel();
668 }
669
670 pub async fn cancelled(&self) {
684 self.cancellation.cancelled().await
685 }
686
687 pub fn cancellation_token(&self) -> CancellationToken {
689 CancellationToken {
690 inner: self.cancellation.clone(),
691 }
692 }
693
694 pub fn with_cancellation_token(mut self, token: CancellationToken) -> Self {
702 self.cancellation = token.inner;
703 self
704 }
705
706 pub async fn report_progress(&self, progress: f64, total: Option<f64>, message: Option<&str>) {
710 let Some(token) = &self.progress_token else {
711 return;
712 };
713 let Some(tx) = &self.notification_tx else {
714 return;
715 };
716
717 let params = ProgressParams {
718 progress_token: token.clone(),
719 progress,
720 total,
721 message: message.map(|s| s.to_string()),
722 meta: None,
723 };
724
725 let _ = tx.try_send(ServerNotification::Progress(params));
727 }
728
729 pub fn report_progress_sync(&self, progress: f64, total: Option<f64>, message: Option<&str>) {
733 let Some(token) = &self.progress_token else {
734 return;
735 };
736 let Some(tx) = &self.notification_tx else {
737 return;
738 };
739
740 let params = ProgressParams {
741 progress_token: token.clone(),
742 progress,
743 total,
744 message: message.map(|s| s.to_string()),
745 meta: None,
746 };
747
748 let _ = tx.try_send(ServerNotification::Progress(params));
749 }
750
751 pub fn notify_tools_list_changed(&self) -> bool {
753 self.notification_tx
754 .as_ref()
755 .is_some_and(|tx| tx.try_send(ServerNotification::ToolsListChanged).is_ok())
756 }
757
758 pub fn notify_prompts_list_changed(&self) -> bool {
760 self.notification_tx
761 .as_ref()
762 .is_some_and(|tx| tx.try_send(ServerNotification::PromptsListChanged).is_ok())
763 }
764
765 pub fn notify_resources_list_changed(&self) -> bool {
767 self.notification_tx.as_ref().is_some_and(|tx| {
768 tx.try_send(ServerNotification::ResourcesListChanged)
769 .is_ok()
770 })
771 }
772
773 pub fn notify_resource_updated(&self, uri: impl Into<String>) -> bool {
781 let uri = uri.into();
782 if !self.final_lifecycle
783 && let Some(subscriptions) = &self.resource_subscriptions
784 && !subscriptions
785 .read()
786 .is_ok_and(|subscribed| subscribed.contains(&uri))
787 {
788 return false;
789 }
790
791 self.notification_tx.as_ref().is_some_and(|tx| {
792 tx.try_send(ServerNotification::ResourceUpdated { uri })
793 .is_ok()
794 })
795 }
796
797 pub fn notify_task_status_changed(
802 &self,
803 params: crate::tasks::TaskStatusNotificationParams,
804 ) -> bool {
805 self.notification_tx.as_ref().is_some_and(|tx| {
806 tx.try_send(ServerNotification::FinalTaskStatusChanged(params))
807 .is_ok()
808 })
809 }
810
811 pub fn send_log(&self, params: LoggingMessageParams) {
828 let Some(tx) = &self.notification_tx else {
829 return;
830 };
831
832 #[cfg(feature = "stateless")]
835 if let Some(meta) = self.per_request_meta()
836 && meta.protocol_version.as_deref()
837 == Some(crate::protocol::PROTOCOL_VERSION_2026_07_28)
838 {
839 let Some(request_level) = meta.log_level else {
840 return;
841 };
842 let request_level = match request_level {
843 crate::stateless::LogLevel::Debug => LogLevel::Debug,
844 crate::stateless::LogLevel::Info => LogLevel::Info,
845 crate::stateless::LogLevel::Notice => LogLevel::Notice,
846 crate::stateless::LogLevel::Warning => LogLevel::Warning,
847 crate::stateless::LogLevel::Error => LogLevel::Error,
848 crate::stateless::LogLevel::Critical => LogLevel::Critical,
849 crate::stateless::LogLevel::Alert => LogLevel::Alert,
850 crate::stateless::LogLevel::Emergency => LogLevel::Emergency,
851 };
852 if params.level > request_level {
853 return;
854 }
855 let _ = tx.try_send(ServerNotification::LogMessage(params));
856 return;
857 }
858
859 if let Some(min_level) = &self.min_log_level
864 && let Ok(min) = min_level.read()
865 && params.level > *min
866 {
867 return;
868 }
869
870 let _ = tx.try_send(ServerNotification::LogMessage(params));
871 }
872
873 pub fn can_sample(&self) -> bool {
878 self.client_requester.is_some()
879 }
880
881 pub async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult> {
920 let requester = self.client_requester.as_ref().ok_or_else(|| {
921 self.no_requester(
922 "Sampling",
923 "`RequestOutcome::input_required` carrying an \
924 `InputRequest::CreateMessage`",
925 )
926 })?;
927
928 requester.sample(params).await
929 }
930
931 pub fn can_elicit(&self) -> bool {
937 self.client_requester.is_some()
938 }
939
940 pub async fn elicit_form(&self, params: ElicitFormParams) -> Result<ElicitResult> {
987 let requester = self.client_requester.as_ref().ok_or_else(|| {
988 self.no_requester(
989 "Elicitation",
990 "`RequestOutcome::input_required` carrying an `InputRequest::Elicit`",
991 )
992 })?;
993
994 requester.elicit(ElicitRequestParams::Form(params)).await
995 }
996
997 pub async fn elicit_url(&self, params: ElicitUrlParams) -> Result<ElicitResult> {
1049 let requester = self.client_requester.as_ref().ok_or_else(|| {
1050 self.no_requester(
1051 "Elicitation",
1052 "`RequestOutcome::input_required` carrying an `InputRequest::Elicit`",
1053 )
1054 })?;
1055
1056 requester.elicit(ElicitRequestParams::Url(params)).await
1057 }
1058
1059 pub async fn confirm(&self, message: impl Into<String>) -> Result<bool> {
1083 use crate::protocol::{ElicitAction, ElicitFormParams, ElicitFormSchema, ElicitMode};
1084
1085 let params = ElicitFormParams {
1086 mode: Some(ElicitMode::Form),
1087 message: message.into(),
1088 requested_schema: ElicitFormSchema::new().boolean_field_with_default(
1089 "confirm",
1090 Some("Confirm this action"),
1091 true,
1092 false,
1093 ),
1094 meta: None,
1095 };
1096
1097 let result = self.elicit_form(params).await?;
1098 Ok(result.action == ElicitAction::Accept)
1099 }
1100
1101 #[deprecated(
1111 since = "0.13.0",
1112 note = "final SEP-2663 removes tasks/list; a conforming peer answers \
1113 MethodNotFound (-32601). Only useful against legacy SEP-1686 \
1114 clients."
1115 )]
1116 pub async fn list_tasks(&self, status: Option<TaskStatus>) -> Result<ListTasksResult> {
1117 let params = ListTasksParams {
1118 status,
1119 cursor: None,
1120 meta: None,
1121 };
1122 let value = self
1123 .request_raw("tasks/list", serde_json::to_value(¶ms)?)
1124 .await?;
1125 serde_json::from_value(value)
1126 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/list: {e}")))
1127 }
1128
1129 pub async fn get_task_info(&self, task_id: impl Into<String>) -> Result<TaskObject> {
1134 let params = GetTaskInfoParams {
1135 task_id: task_id.into(),
1136 meta: None,
1137 };
1138 let value = self
1139 .request_raw("tasks/get", serde_json::to_value(¶ms)?)
1140 .await?;
1141 serde_json::from_value(value)
1142 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/get: {e}")))
1143 }
1144
1145 #[deprecated(
1154 since = "0.13.0",
1155 note = "final SEP-2663 removes tasks/result (results are inlined in \
1156 the tasks/get DetailedTask); a conforming peer answers \
1157 MethodNotFound (-32601). Only useful against legacy SEP-1686 \
1158 clients."
1159 )]
1160 pub async fn get_task_result(&self, task_id: impl Into<String>) -> Result<CallToolResult> {
1161 let params = GetTaskResultParams {
1162 task_id: task_id.into(),
1163 meta: None,
1164 };
1165 let value = self
1166 .request_raw("tasks/result", serde_json::to_value(¶ms)?)
1167 .await?;
1168 serde_json::from_value(value)
1169 .map_err(|e| Error::Internal(format!("Failed to deserialize tasks/result: {e}")))
1170 }
1171
1172 pub async fn cancel_task(
1179 &self,
1180 task_id: impl Into<String>,
1181 reason: Option<String>,
1182 ) -> Result<()> {
1183 let params = CancelTaskParams {
1184 task_id: task_id.into(),
1185 reason,
1186 meta: None,
1187 };
1188 self.request_raw("tasks/cancel", serde_json::to_value(¶ms)?)
1189 .await?;
1190 Ok(())
1191 }
1192
1193 pub async fn request_raw(
1199 &self,
1200 method: &str,
1201 params: serde_json::Value,
1202 ) -> Result<serde_json::Value> {
1203 let requester = self.client_requester.as_ref().ok_or_else(|| {
1204 self.no_requester(
1205 "A server-initiated client request",
1206 "`RequestOutcome::input_required`",
1207 )
1208 })?;
1209 requester.request(method.to_string(), params).await
1210 }
1211}
1212
1213#[derive(Clone, Debug, Default)]
1219pub struct CancellationToken {
1220 inner: tokio_util::sync::CancellationToken,
1221}
1222
1223impl CancellationToken {
1224 pub fn new() -> Self {
1226 Self::default()
1227 }
1228
1229 pub fn is_cancelled(&self) -> bool {
1231 self.inner.is_cancelled()
1232 }
1233
1234 pub fn cancel(&self) {
1236 self.inner.cancel();
1237 }
1238
1239 pub async fn cancelled(&self) {
1243 self.inner.cancelled().await
1244 }
1245}
1246
1247#[derive(Default)]
1249pub struct RequestContextBuilder {
1250 request_id: Option<RequestId>,
1251 progress_token: Option<ProgressToken>,
1252 notification_tx: Option<NotificationSender>,
1253 client_requester: Option<ClientRequesterHandle>,
1254 min_log_level: Option<Arc<RwLock<LogLevel>>>,
1255}
1256
1257impl RequestContextBuilder {
1258 pub fn new() -> Self {
1260 Self::default()
1261 }
1262
1263 pub fn request_id(mut self, id: RequestId) -> Self {
1265 self.request_id = Some(id);
1266 self
1267 }
1268
1269 pub fn progress_token(mut self, token: ProgressToken) -> Self {
1271 self.progress_token = Some(token);
1272 self
1273 }
1274
1275 pub fn notification_sender(mut self, tx: NotificationSender) -> Self {
1277 self.notification_tx = Some(tx);
1278 self
1279 }
1280
1281 pub fn client_requester(mut self, requester: ClientRequesterHandle) -> Self {
1283 self.client_requester = Some(requester);
1284 self
1285 }
1286
1287 pub fn min_log_level(mut self, level: Arc<RwLock<LogLevel>>) -> Self {
1289 self.min_log_level = Some(level);
1290 self
1291 }
1292
1293 pub fn build(self) -> RequestContext {
1297 let mut ctx = RequestContext::new(self.request_id.expect("request_id is required"));
1298 if let Some(token) = self.progress_token {
1299 ctx = ctx.with_progress_token(token);
1300 }
1301 if let Some(tx) = self.notification_tx {
1302 ctx = ctx.with_notification_sender(tx);
1303 }
1304 if let Some(requester) = self.client_requester {
1305 ctx = ctx.with_client_requester(requester);
1306 }
1307 if let Some(level) = self.min_log_level {
1308 ctx = ctx.with_min_log_level(level);
1309 }
1310 ctx
1311 }
1312}
1313
1314#[cfg(test)]
1315mod tests {
1316 use super::*;
1317
1318 #[test]
1319 fn test_cancellation() {
1320 let ctx = RequestContext::new(RequestId::Number(1));
1321 assert!(!ctx.is_cancelled());
1322
1323 let token = ctx.cancellation_token();
1324 assert!(!token.is_cancelled());
1325
1326 ctx.cancel();
1327 assert!(ctx.is_cancelled());
1328 assert!(token.is_cancelled());
1329 }
1330
1331 #[tokio::test]
1332 async fn test_progress_reporting() {
1333 let (tx, mut rx) = notification_channel(10);
1334
1335 let ctx = RequestContext::new(RequestId::Number(1))
1336 .with_progress_token(ProgressToken::Number(42))
1337 .with_notification_sender(tx);
1338
1339 ctx.report_progress(50.0, Some(100.0), Some("Halfway"))
1340 .await;
1341
1342 let notification = rx.recv().await.unwrap();
1343 match notification {
1344 ServerNotification::Progress(params) => {
1345 assert_eq!(params.progress, 50.0);
1346 assert_eq!(params.total, Some(100.0));
1347 assert_eq!(params.message.as_deref(), Some("Halfway"));
1348 }
1349 _ => panic!("Expected Progress notification"),
1350 }
1351 }
1352
1353 #[tokio::test]
1354 async fn test_progress_no_token() {
1355 let (tx, mut rx) = notification_channel(10);
1356
1357 let ctx = RequestContext::new(RequestId::Number(1)).with_notification_sender(tx);
1359
1360 ctx.report_progress(50.0, Some(100.0), None).await;
1361
1362 assert!(rx.try_recv().is_err());
1364 }
1365
1366 #[test]
1367 fn legacy_resource_update_is_sent_for_a_subscribed_uri() {
1368 let (tx, mut rx) = notification_channel(10);
1369 let subscriptions = Arc::new(RwLock::new(HashSet::from([
1370 "file:///subscribed.txt".to_string()
1371 ])));
1372 let ctx = RequestContext::new(RequestId::Number(1))
1373 .with_notification_sender(tx)
1374 .with_resource_subscriptions(subscriptions);
1375
1376 assert!(ctx.notify_resource_updated("file:///subscribed.txt"));
1377 match rx.try_recv().expect("subscribed update should be sent") {
1378 ServerNotification::ResourceUpdated { uri } => {
1379 assert_eq!(uri, "file:///subscribed.txt");
1380 }
1381 notification => panic!("expected resource update, got {notification:?}"),
1382 }
1383 }
1384
1385 #[test]
1386 fn legacy_resource_update_is_suppressed_for_an_unsubscribed_uri() {
1387 let (tx, mut rx) = notification_channel(10);
1388 let subscriptions = Arc::new(RwLock::new(HashSet::from([
1389 "file:///subscribed.txt".to_string()
1390 ])));
1391 let ctx = RequestContext::new(RequestId::Number(1))
1392 .with_notification_sender(tx)
1393 .with_resource_subscriptions(subscriptions);
1394
1395 assert!(!ctx.notify_resource_updated("file:///other.txt"));
1396 assert!(
1397 rx.try_recv().is_err(),
1398 "unsubscribed update must not be enqueued"
1399 );
1400 }
1401
1402 #[test]
1403 fn final_resource_update_bypasses_the_legacy_subscription_guard() {
1404 let (tx, mut rx) = notification_channel(10);
1405 let subscriptions = Arc::new(RwLock::new(HashSet::new()));
1406 let ctx = RequestContext::new(RequestId::Number(1))
1407 .with_notification_sender(tx)
1408 .with_resource_subscriptions(subscriptions)
1409 .with_final_lifecycle(true);
1410
1411 assert!(ctx.notify_resource_updated("file:///final.txt"));
1412 match rx.try_recv().expect("final update should be routed") {
1413 ServerNotification::ResourceUpdated { uri } => {
1414 assert_eq!(uri, "file:///final.txt");
1415 }
1416 notification => panic!("expected resource update, got {notification:?}"),
1417 }
1418 }
1419
1420 #[test]
1421 fn manually_created_context_preserves_resource_update_behavior() {
1422 let (tx, mut rx) = notification_channel(10);
1423 let ctx = RequestContext::new(RequestId::Number(1)).with_notification_sender(tx);
1424
1425 assert!(ctx.notify_resource_updated("file:///manual.txt"));
1426 assert!(matches!(
1427 rx.try_recv(),
1428 Ok(ServerNotification::ResourceUpdated { uri }) if uri == "file:///manual.txt"
1429 ));
1430 }
1431
1432 #[test]
1433 fn test_builder() {
1434 let (tx, _rx) = notification_channel(10);
1435
1436 let ctx = RequestContextBuilder::new()
1437 .request_id(RequestId::String("req-1".to_string()))
1438 .progress_token(ProgressToken::String("prog-1".to_string()))
1439 .notification_sender(tx)
1440 .build();
1441
1442 assert_eq!(ctx.request_id(), &RequestId::String("req-1".to_string()));
1443 assert!(ctx.progress_token().is_some());
1444 }
1445
1446 #[test]
1447 fn test_can_sample_without_requester() {
1448 let ctx = RequestContext::new(RequestId::Number(1));
1449 assert!(!ctx.can_sample());
1450 }
1451
1452 #[test]
1453 fn test_can_sample_with_requester() {
1454 let (request_tx, _rx) = outgoing_request_channel(10);
1455 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1456
1457 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1458 assert!(ctx.can_sample());
1459 }
1460
1461 #[tokio::test]
1462 async fn test_sample_without_requester_fails() {
1463 use crate::protocol::{CreateMessageParams, SamplingMessage};
1464
1465 let ctx = RequestContext::new(RequestId::Number(1));
1466 let params = CreateMessageParams::new(vec![SamplingMessage::user("test")], 100);
1467
1468 let result = ctx.sample(params).await;
1469 assert!(result.is_err());
1470 assert!(
1471 result
1472 .unwrap_err()
1473 .to_string()
1474 .contains("Sampling is not available: no client requester is configured")
1475 );
1476 }
1477
1478 #[test]
1479 fn test_builder_with_client_requester() {
1480 let (request_tx, _rx) = outgoing_request_channel(10);
1481 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1482
1483 let ctx = RequestContextBuilder::new()
1484 .request_id(RequestId::Number(1))
1485 .client_requester(requester)
1486 .build();
1487
1488 assert!(ctx.can_sample());
1489 }
1490
1491 #[test]
1492 fn test_can_elicit_without_requester() {
1493 let ctx = RequestContext::new(RequestId::Number(1));
1494 assert!(!ctx.can_elicit());
1495 }
1496
1497 #[test]
1498 fn test_can_elicit_with_requester() {
1499 let (request_tx, _rx) = outgoing_request_channel(10);
1500 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(request_tx));
1501
1502 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1503 assert!(ctx.can_elicit());
1504 }
1505
1506 #[tokio::test]
1507 async fn test_elicit_form_without_requester_fails() {
1508 use crate::protocol::{ElicitFormSchema, ElicitMode};
1509
1510 let ctx = RequestContext::new(RequestId::Number(1));
1511 let params = ElicitFormParams {
1512 mode: Some(ElicitMode::Form),
1513 message: "Enter details".to_string(),
1514 requested_schema: ElicitFormSchema::new().string_field("name", None, true),
1515 meta: None,
1516 };
1517
1518 let result = ctx.elicit_form(params).await;
1519 assert!(result.is_err());
1520 assert!(
1521 result
1522 .unwrap_err()
1523 .to_string()
1524 .contains("Elicitation is not available: no client requester is configured")
1525 );
1526 }
1527
1528 #[tokio::test]
1529 async fn test_elicit_url_without_requester_fails() {
1530 use crate::protocol::ElicitMode;
1531
1532 let ctx = RequestContext::new(RequestId::Number(1));
1533 let params = ElicitUrlParams {
1534 mode: Some(ElicitMode::Url),
1535 elicitation_id: "test-123".to_string(),
1536 message: "Please authorize".to_string(),
1537 url: "https://example.com/auth".to_string(),
1538 meta: None,
1539 };
1540
1541 let result = ctx.elicit_url(params).await;
1542 assert!(result.is_err());
1543 assert!(
1544 result
1545 .unwrap_err()
1546 .to_string()
1547 .contains("Elicitation is not available: no client requester is configured")
1548 );
1549 }
1550
1551 #[tokio::test]
1552 async fn test_confirm_without_requester_fails() {
1553 let ctx = RequestContext::new(RequestId::Number(1));
1554
1555 let result = ctx.confirm("Are you sure?").await;
1556 assert!(result.is_err());
1557 assert!(
1558 result
1559 .unwrap_err()
1560 .to_string()
1561 .contains("Elicitation is not available: no client requester is configured")
1562 );
1563 }
1564
1565 #[tokio::test]
1566 async fn test_send_log_filtered_by_level() {
1567 let (tx, mut rx) = notification_channel(10);
1568 let min_level = Arc::new(RwLock::new(LogLevel::Warning));
1569
1570 let ctx = RequestContext::new(RequestId::Number(1))
1571 .with_notification_sender(tx)
1572 .with_min_log_level(min_level.clone());
1573
1574 ctx.send_log(LoggingMessageParams::new(
1576 LogLevel::Error,
1577 serde_json::Value::Null,
1578 ));
1579 let msg = rx.try_recv();
1580 assert!(msg.is_ok(), "Error should pass through Warning filter");
1581
1582 ctx.send_log(LoggingMessageParams::new(
1584 LogLevel::Warning,
1585 serde_json::Value::Null,
1586 ));
1587 let msg = rx.try_recv();
1588 assert!(msg.is_ok(), "Warning should pass through Warning filter");
1589
1590 ctx.send_log(LoggingMessageParams::new(
1592 LogLevel::Info,
1593 serde_json::Value::Null,
1594 ));
1595 let msg = rx.try_recv();
1596 assert!(msg.is_err(), "Info should be filtered by Warning filter");
1597
1598 ctx.send_log(LoggingMessageParams::new(
1600 LogLevel::Debug,
1601 serde_json::Value::Null,
1602 ));
1603 let msg = rx.try_recv();
1604 assert!(msg.is_err(), "Debug should be filtered by Warning filter");
1605 }
1606
1607 #[tokio::test]
1608 async fn test_send_log_level_updates_dynamically() {
1609 let (tx, mut rx) = notification_channel(10);
1610 let min_level = Arc::new(RwLock::new(LogLevel::Error));
1611
1612 let ctx = RequestContext::new(RequestId::Number(1))
1613 .with_notification_sender(tx)
1614 .with_min_log_level(min_level.clone());
1615
1616 ctx.send_log(LoggingMessageParams::new(
1618 LogLevel::Info,
1619 serde_json::Value::Null,
1620 ));
1621 assert!(
1622 rx.try_recv().is_err(),
1623 "Info should be filtered at Error level"
1624 );
1625
1626 *min_level.write().unwrap() = LogLevel::Debug;
1628
1629 ctx.send_log(LoggingMessageParams::new(
1631 LogLevel::Info,
1632 serde_json::Value::Null,
1633 ));
1634 assert!(
1635 rx.try_recv().is_ok(),
1636 "Info should pass through after level changed to Debug"
1637 );
1638 }
1639
1640 #[tokio::test]
1641 async fn test_send_log_no_min_level_sends_all() {
1642 let (tx, mut rx) = notification_channel(10);
1643
1644 let ctx = RequestContext::new(RequestId::Number(1)).with_notification_sender(tx);
1646
1647 ctx.send_log(LoggingMessageParams::new(
1648 LogLevel::Debug,
1649 serde_json::Value::Null,
1650 ));
1651 assert!(
1652 rx.try_recv().is_ok(),
1653 "Debug should pass when no min level is set"
1654 );
1655 }
1656
1657 #[tokio::test]
1658 #[cfg(feature = "stateless")]
1659 async fn final_request_log_level_is_required_and_filters_per_request() {
1660 let (tx, mut rx) = notification_channel(10);
1661 let mut extensions = Extensions::new();
1662 extensions.insert(crate::stateless::StatelessRequestMeta {
1663 protocol_version: Some(crate::protocol::PROTOCOL_VERSION_2026_07_28.to_string()),
1664 client_capabilities: Some(Default::default()),
1665 ..Default::default()
1666 });
1667 let ctx = RequestContext::new(RequestId::Number(1))
1668 .with_notification_sender(tx.clone())
1669 .with_extensions(Arc::new(extensions));
1670 ctx.send_log(LoggingMessageParams::new(
1671 LogLevel::Emergency,
1672 serde_json::Value::Null,
1673 ));
1674 assert!(
1675 rx.try_recv().is_err(),
1676 "final requests without logLevel must receive no logs"
1677 );
1678
1679 let mut extensions = Extensions::new();
1680 extensions.insert(crate::stateless::StatelessRequestMeta {
1681 protocol_version: Some(crate::protocol::PROTOCOL_VERSION_2026_07_28.to_string()),
1682 client_capabilities: Some(Default::default()),
1683 log_level: Some(crate::stateless::LogLevel::Warning),
1684 ..Default::default()
1685 });
1686 let ctx = RequestContext::new(RequestId::Number(2))
1687 .with_notification_sender(tx)
1688 .with_extensions(Arc::new(extensions));
1689 ctx.send_log(LoggingMessageParams::new(
1690 LogLevel::Info,
1691 serde_json::Value::Null,
1692 ));
1693 assert!(rx.try_recv().is_err(), "Info must be filtered at Warning");
1694 ctx.send_log(LoggingMessageParams::new(
1695 LogLevel::Error,
1696 serde_json::Value::Null,
1697 ));
1698 assert!(rx.try_recv().is_ok(), "Error must pass at Warning");
1699 }
1700
1701 fn make_task_object(id: &str, status: TaskStatus) -> serde_json::Value {
1702 serde_json::json!({
1703 "taskId": id,
1704 "status": status,
1705 "createdAt": "2026-04-24T00:00:00Z",
1706 "lastUpdatedAt": "2026-04-24T00:00:00Z",
1707 "ttl": null
1708 })
1709 }
1710
1711 fn spawn_mock_client(
1712 mut rx: OutgoingRequestReceiver,
1713 responder: impl Fn(&str, serde_json::Value) -> serde_json::Value + Send + 'static,
1714 ) {
1715 tokio::spawn(async move {
1716 while let Some(req) = rx.recv().await {
1717 let response = responder(&req.method, req.params);
1718 let _ = req.response_tx.send(Ok(response));
1719 }
1720 });
1721 }
1722
1723 #[tokio::test]
1724 async fn test_get_task_info_round_trips() {
1725 let (tx, rx) = outgoing_request_channel(10);
1726 spawn_mock_client(rx, |method, params| {
1727 assert_eq!(method, "tasks/get");
1728 let task_id = params["taskId"].as_str().unwrap().to_string();
1729 make_task_object(&task_id, TaskStatus::Working)
1730 });
1731 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1732 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1733
1734 let info = ctx.get_task_info("task-123").await.unwrap();
1735 assert_eq!(info.task_id, "task-123");
1736 assert!(matches!(info.status, TaskStatus::Working));
1737 }
1738
1739 #[tokio::test]
1740 #[allow(deprecated)] async fn test_list_tasks_round_trips() {
1742 let (tx, rx) = outgoing_request_channel(10);
1743 spawn_mock_client(rx, |method, params| {
1744 assert_eq!(method, "tasks/list");
1745 assert_eq!(params["status"], serde_json::json!("working"));
1747 serde_json::json!({
1748 "tasks": [
1749 make_task_object("task-1", TaskStatus::Working),
1750 make_task_object("task-2", TaskStatus::Working),
1751 ]
1752 })
1753 });
1754 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1755 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1756
1757 let result = ctx.list_tasks(Some(TaskStatus::Working)).await.unwrap();
1758 assert_eq!(result.tasks.len(), 2);
1759 assert_eq!(result.tasks[0].task_id, "task-1");
1760 }
1761
1762 #[tokio::test]
1763 async fn test_cancel_task_forwards_reason() {
1764 let (tx, rx) = outgoing_request_channel(10);
1765 spawn_mock_client(rx, |method, params| {
1766 assert_eq!(method, "tasks/cancel");
1767 assert_eq!(params["reason"], serde_json::json!("user requested"));
1768 serde_json::json!({})
1770 });
1771 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1772 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1773
1774 ctx.cancel_task("task-99", Some("user requested".into()))
1775 .await
1776 .expect("empty ack should succeed");
1777 }
1778
1779 #[tokio::test]
1780 async fn test_cancel_task_tolerates_legacy_task_object_ack() {
1781 let (tx, rx) = outgoing_request_channel(10);
1784 spawn_mock_client(rx, |method, _params| {
1785 assert_eq!(method, "tasks/cancel");
1786 make_task_object("task-99", TaskStatus::Cancelled)
1787 });
1788 let requester: ClientRequesterHandle = Arc::new(ChannelClientRequester::new(tx));
1789 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1790
1791 ctx.cancel_task("task-99", None)
1792 .await
1793 .expect("legacy task-object ack should also succeed");
1794 }
1795
1796 #[tokio::test]
1797 async fn test_get_task_info_without_requester_fails() {
1798 let ctx = RequestContext::new(RequestId::Number(1));
1799 let result = ctx.get_task_info("task-1").await;
1800 assert!(result.is_err());
1801 assert!(
1802 result
1803 .unwrap_err()
1804 .to_string()
1805 .contains("no client requester is configured")
1806 );
1807 }
1808
1809 #[tokio::test]
1810 async fn test_default_request_impl_errors() {
1811 struct OnlySampleAndElicit;
1814
1815 #[async_trait]
1816 impl ClientRequester for OnlySampleAndElicit {
1817 async fn sample(&self, _: CreateMessageParams) -> Result<CreateMessageResult> {
1818 unreachable!()
1819 }
1820 async fn elicit(&self, _: ElicitRequestParams) -> Result<ElicitResult> {
1821 unreachable!()
1822 }
1823 }
1824
1825 let requester: ClientRequesterHandle = Arc::new(OnlySampleAndElicit);
1826 let ctx = RequestContext::new(RequestId::Number(1)).with_client_requester(requester);
1827
1828 let err = ctx.get_task_info("x").await.unwrap_err();
1829 assert!(err.to_string().contains("does not support arbitrary"));
1830 }
1831}
1832
1833#[cfg(test)]
1834mod final_lifecycle_diagnostics_tests {
1835 use super::*;
1836 use crate::protocol::{ElicitFormParams, ElicitFormSchema};
1837
1838 fn params() -> ElicitFormParams {
1839 ElicitFormParams {
1840 mode: None,
1841 message: "confirm?".to_string(),
1842 requested_schema: ElicitFormSchema::new(),
1843 meta: None,
1844 }
1845 }
1846
1847 fn sampling_params() -> CreateMessageParams {
1848 CreateMessageParams {
1849 messages: Vec::new(),
1850 max_tokens: 1,
1851 system_prompt: None,
1852 temperature: None,
1853 stop_sequences: Vec::new(),
1854 model_preferences: None,
1855 include_context: None,
1856 metadata: None,
1857 tools: None,
1858 tool_choice: None,
1859 task: None,
1860 meta: None,
1861 }
1862 }
1863
1864 #[tokio::test]
1869 async fn final_lifecycle_elicitation_error_names_the_replacement() {
1870 let ctx = RequestContext::new(RequestId::Number(1)).with_final_lifecycle(true);
1871
1872 let error = ctx.elicit_form(params()).await.unwrap_err().to_string();
1873 assert!(
1874 error.contains("2026-07-28"),
1875 "must name the lifecycle: {error}"
1876 );
1877 assert!(
1878 error.contains("do not \ninitiate JSON-RPC requests")
1879 || error.contains("do not initiate JSON-RPC requests"),
1880 "must explain the cause: {error}"
1881 );
1882 assert!(
1883 error.contains("RequestOutcome::input_required"),
1884 "must name the replacement API: {error}"
1885 );
1886 assert!(
1887 error.contains("SEP-2322"),
1888 "must cite the mechanism: {error}"
1889 );
1890 assert!(
1891 !error.contains("no client requester is configured"),
1892 "must not blame configuration: {error}"
1893 );
1894 }
1895
1896 #[tokio::test]
1897 async fn final_lifecycle_sampling_error_names_the_replacement() {
1898 let ctx = RequestContext::new(RequestId::Number(1)).with_final_lifecycle(true);
1899 let error = ctx.sample(sampling_params()).await.unwrap_err().to_string();
1900 assert!(error.contains("2026-07-28"), "{error}");
1901 assert!(error.contains("RequestOutcome::input_required"), "{error}");
1902 }
1903
1904 #[tokio::test]
1907 async fn legacy_lifecycle_keeps_the_configuration_error() {
1908 let ctx = RequestContext::new(RequestId::Number(1));
1909
1910 let error = ctx.elicit_form(params()).await.unwrap_err().to_string();
1911 assert!(
1912 error.contains("no client requester is configured"),
1913 "a legacy transport without a requester is misconfigured: {error}"
1914 );
1915 assert!(
1916 !error.contains("2026-07-28"),
1917 "must not blame the protocol: {error}"
1918 );
1919 }
1920
1921 #[tokio::test]
1924 async fn capability_probes_report_false_on_the_final_lifecycle() {
1925 let ctx = RequestContext::new(RequestId::Number(1)).with_final_lifecycle(true);
1926 assert!(!ctx.can_elicit());
1927 assert!(!ctx.can_sample());
1928 }
1929}