1use mcpkit_core::capability::{ClientCapabilities, ServerCapabilities};
47use mcpkit_core::error::McpError;
48use mcpkit_core::protocol::{Notification, ProgressToken, RequestId};
49use mcpkit_core::protocol_version::ProtocolVersion;
50use std::future::Future;
51use std::pin::Pin;
52use std::sync::Arc;
53use std::sync::atomic::{AtomicBool, Ordering};
54use std::task::{Context as TaskContext, Poll};
55
56use event_listener::Event;
57
58pub trait Peer: Send + Sync {
63 fn notify(
65 &self,
66 notification: Notification,
67 ) -> Pin<Box<dyn Future<Output = Result<(), McpError>> + Send + '_>>;
68}
69
70#[derive(Clone)]
75pub struct CancellationToken {
76 cancelled: Arc<AtomicBool>,
77 event: Arc<Event>,
78}
79
80impl CancellationToken {
81 #[must_use]
83 pub fn new() -> Self {
84 Self {
85 cancelled: Arc::new(AtomicBool::new(false)),
86 event: Arc::new(Event::new()),
87 }
88 }
89
90 #[must_use]
92 pub fn is_cancelled(&self) -> bool {
93 self.cancelled.load(Ordering::SeqCst)
94 }
95
96 pub fn cancel(&self) {
98 self.cancelled.store(true, Ordering::SeqCst);
99 self.event.notify(usize::MAX);
101 }
102
103 #[must_use]
109 pub fn cancelled(&self) -> CancelledFuture {
110 CancelledFuture::new(self.cancelled.clone(), self.event.clone())
111 }
112}
113
114impl std::fmt::Debug for CancellationToken {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.debug_struct("CancellationToken")
117 .field("cancelled", &self.is_cancelled())
118 .finish()
119 }
120}
121
122impl Default for CancellationToken {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128pub struct CancelledFuture {
133 inner: Pin<Box<dyn Future<Output = ()> + Send>>,
134}
135
136impl CancelledFuture {
137 fn new(cancelled: Arc<AtomicBool>, event: Arc<Event>) -> Self {
138 Self {
139 inner: Box::pin(async move {
140 loop {
141 if cancelled.load(Ordering::SeqCst) {
142 return;
143 }
144 let listener = event.listen();
149 if cancelled.load(Ordering::SeqCst) {
150 return;
151 }
152 listener.await;
153 }
154 }),
155 }
156 }
157}
158
159impl Future for CancelledFuture {
160 type Output = ();
161
162 fn poll(mut self: Pin<&mut Self>, cx: &mut TaskContext<'_>) -> Poll<Self::Output> {
163 self.inner.as_mut().poll(cx)
164 }
165}
166
167pub struct Context<'a> {
177 pub request_id: &'a RequestId,
179 pub progress_token: Option<&'a ProgressToken>,
181 pub client_caps: &'a ClientCapabilities,
183 pub server_caps: &'a ServerCapabilities,
185 pub protocol_version: ProtocolVersion,
190 peer: &'a dyn Peer,
192 cancel: CancellationToken,
194}
195
196impl<'a> Context<'a> {
197 #[must_use]
199 pub fn new(
200 request_id: &'a RequestId,
201 progress_token: Option<&'a ProgressToken>,
202 client_caps: &'a ClientCapabilities,
203 server_caps: &'a ServerCapabilities,
204 protocol_version: ProtocolVersion,
205 peer: &'a dyn Peer,
206 ) -> Self {
207 Self {
208 request_id,
209 progress_token,
210 client_caps,
211 server_caps,
212 protocol_version,
213 peer,
214 cancel: CancellationToken::new(),
215 }
216 }
217
218 #[must_use]
220 pub fn with_cancellation(
221 request_id: &'a RequestId,
222 progress_token: Option<&'a ProgressToken>,
223 client_caps: &'a ClientCapabilities,
224 server_caps: &'a ServerCapabilities,
225 protocol_version: ProtocolVersion,
226 peer: &'a dyn Peer,
227 cancel: CancellationToken,
228 ) -> Self {
229 Self {
230 request_id,
231 progress_token,
232 client_caps,
233 server_caps,
234 protocol_version,
235 peer,
236 cancel,
237 }
238 }
239
240 #[must_use]
242 pub fn is_cancelled(&self) -> bool {
243 self.cancel.is_cancelled()
244 }
245
246 pub fn cancelled(&self) -> impl Future<Output = ()> + '_ {
248 self.cancel.cancelled()
249 }
250
251 #[must_use]
253 pub const fn cancellation_token(&self) -> &CancellationToken {
254 &self.cancel
255 }
256
257 pub async fn notify(
268 &self,
269 method: &str,
270 params: Option<serde_json::Value>,
271 ) -> Result<(), McpError> {
272 let notification = if let Some(p) = params {
273 Notification::with_params(method.to_string(), p)
274 } else {
275 Notification::new(method.to_string())
276 };
277 self.peer.notify(notification).await
278 }
279
280 pub async fn progress(
295 &self,
296 current: u64,
297 total: Option<u64>,
298 message: Option<&str>,
299 ) -> Result<(), McpError> {
300 let Some(token) = self.progress_token else {
301 return Ok(());
303 };
304
305 let params = serde_json::json!({
306 "progressToken": token,
307 "progress": current,
308 "total": total,
309 "message": message,
310 });
311
312 self.notify("notifications/progress", Some(params)).await
313 }
314}
315
316impl std::fmt::Debug for Context<'_> {
317 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 f.debug_struct("Context")
319 .field("request_id", &self.request_id)
320 .field("progress_token", &self.progress_token)
321 .field("client_caps", &self.client_caps)
322 .field("server_caps", &self.server_caps)
323 .field("protocol_version", &self.protocol_version)
324 .field("is_cancelled", &self.is_cancelled())
325 .finish()
326 }
327}
328
329#[derive(Debug, Clone, Copy)]
333pub struct NoOpPeer;
334
335impl Peer for NoOpPeer {
336 fn notify(
337 &self,
338 _notification: Notification,
339 ) -> Pin<Box<dyn Future<Output = Result<(), McpError>> + Send + '_>> {
340 Box::pin(async { Ok(()) })
341 }
342}
343
344pub struct ContextData {
349 pub request_id: RequestId,
351 pub progress_token: Option<ProgressToken>,
353 pub client_caps: ClientCapabilities,
355 pub server_caps: ServerCapabilities,
357 pub protocol_version: ProtocolVersion,
359}
360
361impl ContextData {
362 #[must_use]
364 pub const fn new(
365 request_id: RequestId,
366 client_caps: ClientCapabilities,
367 server_caps: ServerCapabilities,
368 protocol_version: ProtocolVersion,
369 ) -> Self {
370 Self {
371 request_id,
372 progress_token: None,
373 client_caps,
374 server_caps,
375 protocol_version,
376 }
377 }
378
379 #[must_use]
381 pub fn with_progress_token(mut self, token: ProgressToken) -> Self {
382 self.progress_token = Some(token);
383 self
384 }
385
386 #[must_use]
388 pub fn to_context<'a>(&'a self, peer: &'a dyn Peer) -> Context<'a> {
389 Context::new(
390 &self.request_id,
391 self.progress_token.as_ref(),
392 &self.client_caps,
393 &self.server_caps,
394 self.protocol_version,
395 peer,
396 )
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403
404 #[test]
405 fn test_cancellation_token() {
406 let token = CancellationToken::new();
407 assert!(!token.is_cancelled());
408 token.cancel();
409 assert!(token.is_cancelled());
410 }
411
412 #[test]
417 fn cancelled_future_parks_and_wakes_on_cancel() {
418 use std::sync::atomic::AtomicUsize;
419 use std::task::{Wake, Waker};
420
421 struct CountingWaker(AtomicUsize);
422 impl Wake for CountingWaker {
423 fn wake(self: Arc<Self>) {
424 self.0.fetch_add(1, Ordering::SeqCst);
425 }
426 fn wake_by_ref(self: &Arc<Self>) {
427 self.0.fetch_add(1, Ordering::SeqCst);
428 }
429 }
430
431 let counter = Arc::new(CountingWaker(AtomicUsize::new(0)));
432 let waker = Waker::from(counter.clone());
433 let mut cx = TaskContext::from_waker(&waker);
434
435 let token = CancellationToken::new();
436 let mut fut = Box::pin(token.cancelled());
437
438 assert_eq!(fut.as_mut().poll(&mut cx), Poll::Pending);
441 assert_eq!(
442 counter.0.load(Ordering::SeqCst),
443 0,
444 "cancelled future must park, not busy-spin (no self-wake)"
445 );
446
447 token.cancel();
449 assert!(
450 counter.0.load(Ordering::SeqCst) >= 1,
451 "cancel() must wake the parked waiter"
452 );
453 assert_eq!(fut.as_mut().poll(&mut cx), Poll::Ready(()));
454 }
455
456 #[test]
459 fn cancelled_future_ready_when_already_cancelled() {
460 let waker = std::task::Waker::noop();
461 let mut cx = TaskContext::from_waker(waker);
462
463 let token = CancellationToken::new();
464 token.cancel();
465 let mut fut = Box::pin(token.cancelled());
466 assert_eq!(fut.as_mut().poll(&mut cx), Poll::Ready(()));
467 }
468
469 #[test]
470 fn test_context_creation() {
471 let request_id = RequestId::Number(1);
472 let client_caps = ClientCapabilities::default();
473 let server_caps = ServerCapabilities::default();
474 let peer = NoOpPeer;
475
476 let ctx = Context::new(
477 &request_id,
478 None,
479 &client_caps,
480 &server_caps,
481 ProtocolVersion::LATEST,
482 &peer,
483 );
484
485 assert!(!ctx.is_cancelled());
486 assert!(ctx.progress_token.is_none());
487 assert_eq!(ctx.protocol_version, ProtocolVersion::LATEST);
488 }
489
490 #[test]
491 fn test_context_with_progress_token() {
492 let request_id = RequestId::Number(1);
493 let progress_token = ProgressToken::String("token".to_string());
494 let client_caps = ClientCapabilities::default();
495 let server_caps = ServerCapabilities::default();
496 let peer = NoOpPeer;
497
498 let ctx = Context::new(
499 &request_id,
500 Some(&progress_token),
501 &client_caps,
502 &server_caps,
503 ProtocolVersion::V2025_03_26,
504 &peer,
505 );
506
507 assert!(ctx.progress_token.is_some());
508 assert_eq!(ctx.protocol_version, ProtocolVersion::V2025_03_26);
509 }
510
511 #[test]
512 fn test_context_data() {
513 let data = ContextData::new(
514 RequestId::Number(42),
515 ClientCapabilities::default(),
516 ServerCapabilities::default(),
517 ProtocolVersion::V2025_06_18,
518 )
519 .with_progress_token(ProgressToken::String("test".to_string()));
520
521 let peer = NoOpPeer;
522 let ctx = data.to_context(&peer);
523
524 assert!(ctx.progress_token.is_some());
525 assert_eq!(ctx.protocol_version, ProtocolVersion::V2025_06_18);
526 assert!(ctx.protocol_version.supports_elicitation());
528 assert!(!ctx.protocol_version.supports_tasks()); }
530}