1use crate::builder::Server;
36use crate::context::{CancellationToken, Context, ContextData, Peer};
37use crate::dispatch::{PromptSlot, ResourceSlot, TaskSlot, ToolSlot};
38use crate::handler::ServerHandler;
39use crate::router::{route_prompts, route_resources, route_tasks, route_tools};
40use futures::channel::{mpsc, oneshot};
41use mcpkit_core::capability::{ClientCapabilities, ServerCapabilities};
42use mcpkit_core::error::McpError;
43use mcpkit_core::protocol::{Message, Notification, ProgressToken, Request, RequestId, Response};
44use mcpkit_core::protocol_version::ProtocolVersion;
45use mcpkit_transport::Transport;
46use std::collections::HashMap;
47use std::sync::Arc;
48use std::sync::RwLock;
49use std::sync::atomic::{AtomicBool, Ordering};
50use std::time::Duration;
51
52pub struct ServerState {
54 pub client_caps: RwLock<ClientCapabilities>,
56 pub server_caps: ServerCapabilities,
58 pub initialized: AtomicBool,
60 pub cancellations: RwLock<HashMap<String, CancellationToken>>,
62 pub negotiated_version: RwLock<Option<ProtocolVersion>>,
67 outbound: crate::adapter_peer::SessionOutbound,
71 ambient_tx: mpsc::UnboundedSender<Notification>,
74 ambient_rx: std::sync::Mutex<Option<mpsc::UnboundedReceiver<Notification>>>,
76}
77
78impl ServerState {
79 #[must_use]
81 pub fn new(server_caps: ServerCapabilities) -> Self {
82 let (ambient_tx, ambient_rx) = mpsc::unbounded();
83 Self {
84 client_caps: RwLock::new(ClientCapabilities::default()),
85 server_caps,
86 initialized: AtomicBool::new(false),
87 cancellations: RwLock::new(HashMap::new()),
88 negotiated_version: RwLock::new(None),
89 outbound: crate::adapter_peer::SessionOutbound::new(),
90 ambient_tx,
91 ambient_rx: std::sync::Mutex::new(Some(ambient_rx)),
92 }
93 }
94
95 pub fn publish_notification(&self, notification: Notification) {
111 let _ = self.ambient_tx.unbounded_send(notification);
113 }
114
115 fn take_ambient_receiver(&self) -> Option<mpsc::UnboundedReceiver<Notification>> {
118 self.ambient_rx.lock().ok()?.take()
119 }
120
121 pub(crate) fn next_outbound_id(&self) -> RequestId {
123 self.outbound.next_id()
124 }
125
126 pub(crate) fn register_outbound(&self, id: RequestId) -> oneshot::Receiver<Response> {
129 self.outbound.register(id)
130 }
131
132 pub(crate) fn remove_outbound(&self, id: &RequestId) {
134 self.outbound.remove(id);
135 }
136
137 pub(crate) fn route_response(&self, response: Response) {
139 let id = response.id.clone();
140 if !self.outbound.resolve(response) {
141 tracing::debug!(id = %id, "response did not match a pending request");
142 }
143 }
144
145 pub(crate) fn fail_pending_requests(&self) {
148 self.outbound.fail_all();
149 }
150
151 pub fn protocol_version(&self) -> Option<ProtocolVersion> {
165 self.negotiated_version.read().ok().and_then(|guard| *guard)
166 }
167
168 pub fn set_protocol_version(&self, version: ProtocolVersion) {
172 if let Ok(mut guard) = self.negotiated_version.write() {
173 *guard = Some(version);
174 }
175 }
176
177 pub fn client_caps(&self) -> ClientCapabilities {
181 self.client_caps
182 .read()
183 .map(|guard| guard.clone())
184 .unwrap_or_default()
185 }
186
187 pub fn set_client_caps(&self, caps: ClientCapabilities) {
191 if let Ok(mut guard) = self.client_caps.write() {
192 *guard = caps;
193 }
194 }
195
196 pub fn is_initialized(&self) -> bool {
198 self.initialized.load(Ordering::Acquire)
199 }
200
201 pub fn set_initialized(&self) {
203 self.initialized.store(true, Ordering::Release);
204 }
205
206 pub fn register_cancellation(&self, request_id: &str, token: CancellationToken) {
208 if let Ok(mut cancellations) = self.cancellations.write() {
209 cancellations.insert(request_id.to_string(), token);
210 }
211 }
212
213 pub fn cancel_request(&self, request_id: &str) {
215 if let Ok(cancellations) = self.cancellations.read() {
216 if let Some(token) = cancellations.get(request_id) {
217 token.cancel();
218 }
219 }
220 }
221
222 pub fn remove_cancellation(&self, request_id: &str) {
224 if let Ok(mut cancellations) = self.cancellations.write() {
225 cancellations.remove(request_id);
226 }
227 }
228}
229
230#[derive(Clone)]
233struct OutboundCtx {
234 state: Arc<ServerState>,
235 timeout: Duration,
236}
237
238pub struct TransportPeer<T: Transport> {
244 transport: Arc<T>,
245 outbound: Option<OutboundCtx>,
246}
247
248impl<T: Transport> TransportPeer<T> {
249 pub const fn new(transport: Arc<T>) -> Self {
251 Self {
252 transport,
253 outbound: None,
254 }
255 }
256
257 pub(crate) fn with_outbound(
260 transport: Arc<T>,
261 state: Arc<ServerState>,
262 timeout: Duration,
263 ) -> Self {
264 Self {
265 transport,
266 outbound: Some(OutboundCtx { state, timeout }),
267 }
268 }
269}
270
271impl<T: Transport + 'static> Peer for TransportPeer<T>
272where
273 T::Error: Into<McpError>,
274{
275 fn notify(
276 &self,
277 notification: Notification,
278 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), McpError>> + Send + '_>>
279 {
280 let transport = self.transport.clone();
281 Box::pin(async move {
282 transport
283 .send(Message::Notification(notification))
284 .await
285 .map_err(std::convert::Into::into)
286 })
287 }
288
289 fn request(
290 &self,
291 method: std::borrow::Cow<'static, str>,
292 params: Option<serde_json::Value>,
293 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Response, McpError>> + Send + '_>>
294 {
295 let Some(outbound) = self.outbound.clone() else {
296 return Box::pin(async {
297 Err(McpError::internal(
298 "this peer does not support server-initiated requests",
299 ))
300 });
301 };
302 let transport = self.transport.clone();
303 Box::pin(async move {
304 use futures::future::{Either, select};
305
306 let id = outbound.state.next_outbound_id();
307 let rx = outbound.state.register_outbound(id.clone());
308 let request = match params {
309 Some(p) => Request::with_params(method, id.clone(), p),
310 None => Request::new(method, id.clone()),
311 };
312 transport
313 .send(Message::Request(request))
314 .await
315 .map_err(std::convert::Into::into)?;
316
317 let sleep = mcpkit_transport::runtime::sleep(outbound.timeout);
318 futures::pin_mut!(sleep);
319 match select(rx, sleep).await {
320 Either::Left((Ok(response), _)) => Ok(response),
321 Either::Left((Err(_canceled), _)) => {
322 outbound.state.remove_outbound(&id);
323 Err(McpError::internal(
324 "response channel closed before a reply arrived",
325 ))
326 }
327 Either::Right(((), _)) => {
328 outbound.state.remove_outbound(&id);
329 Err(McpError::internal(format!(
330 "server-initiated request timed out after {:?}",
331 outbound.timeout
332 )))
333 }
334 }
335 })
336 }
337}
338
339#[derive(Clone)]
359pub struct ServerNotifier {
360 peer: Arc<dyn Peer>,
361}
362
363impl ServerNotifier {
364 pub async fn notify(
370 &self,
371 method: impl Into<std::borrow::Cow<'static, str>>,
372 params: Option<serde_json::Value>,
373 ) -> Result<(), McpError> {
374 let notification = match params {
375 Some(p) => Notification::with_params(method, p),
376 None => Notification::new(method),
377 };
378 self.peer.notify(notification).await
379 }
380
381 pub async fn log(
388 &self,
389 level: mcpkit_core::types::LoggingLevel,
390 logger: Option<&str>,
391 data: serde_json::Value,
392 ) -> Result<(), McpError> {
393 let params = mcpkit_core::types::LoggingMessageNotificationParams {
394 logger: logger.map(String::from),
395 ..mcpkit_core::types::LoggingMessageNotificationParams::new(level, data)
396 };
397 self.notify(
398 crate::router::notifications::MESSAGE,
399 Some(serde_json::to_value(params)?),
400 )
401 .await
402 }
403
404 pub async fn tools_list_changed(&self) -> Result<(), McpError> {
410 self.notify(crate::router::notifications::TOOLS_LIST_CHANGED, None)
411 .await
412 }
413
414 pub async fn resources_list_changed(&self) -> Result<(), McpError> {
420 self.notify(crate::router::notifications::RESOURCES_LIST_CHANGED, None)
421 .await
422 }
423
424 pub async fn prompts_list_changed(&self) -> Result<(), McpError> {
430 self.notify(crate::router::notifications::PROMPTS_LIST_CHANGED, None)
431 .await
432 }
433
434 pub async fn resource_updated(&self, uri: impl Into<String>) -> Result<(), McpError> {
440 self.notify(
441 crate::router::notifications::RESOURCES_UPDATED,
442 Some(serde_json::json!({ "uri": uri.into() })),
443 )
444 .await
445 }
446
447 pub async fn elicitation_complete(
454 &self,
455 elicitation_id: impl Into<String>,
456 ) -> Result<(), McpError> {
457 self.notify(
458 crate::router::notifications::ELICITATION_COMPLETE,
459 Some(serde_json::json!({ "elicitationId": elicitation_id.into() })),
460 )
461 .await
462 }
463}
464
465#[derive(Debug, Clone)]
481#[non_exhaustive]
482pub struct RuntimeConfig {
483 pub auto_initialized: bool,
485 pub max_concurrent_requests: usize,
487 pub outbound_request_timeout: Duration,
490 pub default_task_ttl_ms: Option<u64>,
493 pub default_task_poll_interval_ms: Option<u64>,
498 pub task_status_notifications: bool,
503}
504
505impl Default for RuntimeConfig {
506 fn default() -> Self {
507 Self {
508 auto_initialized: true,
509 max_concurrent_requests: 100,
510 outbound_request_timeout: Duration::from_secs(60),
511 default_task_ttl_ms: Some(crate::capability::tasks::DEFAULT_TASK_TTL_MS),
512 default_task_poll_interval_ms: None,
513 task_status_notifications: true,
514 }
515 }
516}
517
518impl RuntimeConfig {
519 #[must_use]
521 pub fn new() -> Self {
522 Self::default()
523 }
524
525 #[must_use]
527 pub const fn auto_initialized(mut self, yes: bool) -> Self {
528 self.auto_initialized = yes;
529 self
530 }
531
532 #[must_use]
534 pub const fn max_concurrent_requests(mut self, max: usize) -> Self {
535 self.max_concurrent_requests = max;
536 self
537 }
538
539 #[must_use]
541 pub const fn outbound_request_timeout(mut self, timeout: Duration) -> Self {
542 self.outbound_request_timeout = timeout;
543 self
544 }
545
546 #[must_use]
549 pub const fn default_task_ttl_ms(mut self, ttl_ms: Option<u64>) -> Self {
550 self.default_task_ttl_ms = ttl_ms;
551 self
552 }
553
554 #[must_use]
556 pub const fn default_task_poll_interval_ms(mut self, poll_interval_ms: Option<u64>) -> Self {
557 self.default_task_poll_interval_ms = poll_interval_ms;
558 self
559 }
560
561 #[must_use]
563 pub const fn task_status_notifications(mut self, yes: bool) -> Self {
564 self.task_status_notifications = yes;
565 self
566 }
567}
568
569pub struct ServerRuntime<S, Tr>
574where
575 Tr: Transport,
576{
577 server: S,
578 transport: Arc<Tr>,
579 state: Arc<ServerState>,
580 task_store: Arc<crate::capability::tasks::TaskManager>,
583 config: RuntimeConfig,
585}
586
587struct BackgroundExec {
590 handle: crate::capability::tasks::TaskHandle,
591 name: String,
592 args: mcpkit_core::types::Object,
593 ctx_data: ContextData,
594 cancel: CancellationToken,
595}
596
597enum TaskBegin {
599 NotApplicable,
601 Rejected,
603 Deferred(Box<BackgroundExec>),
605}
606
607async fn drive_sets<F1, F2, F3>(
611 in_flight: &mut futures::stream::FuturesUnordered<F1>,
612 background: &mut futures::stream::FuturesUnordered<F2>,
613 notifications: &mut futures::stream::FuturesUnordered<F3>,
614) -> Option<BackgroundExec>
615where
616 F1: std::future::Future<Output = Option<BackgroundExec>>,
617 F2: std::future::Future<Output = ()>,
618 F3: std::future::Future<Output = Result<(), McpError>>,
619{
620 use futures::future::{Either, select};
621 use futures::stream::StreamExt;
622 use std::future::pending;
623 use std::pin::pin;
624
625 let requests = pin!(async {
629 if in_flight.is_empty() {
630 pending::<Option<BackgroundExec>>().await
631 } else {
632 in_flight.next().await.flatten()
633 }
634 });
635 let tasks = pin!(async {
636 if background.is_empty() {
637 pending::<()>().await;
638 } else {
639 background.next().await;
640 }
641 });
642 let notifs = pin!(async {
643 if notifications.is_empty() {
644 pending::<()>().await;
645 } else if let Some(Err(e)) = notifications.next().await {
646 tracing::error!(error = %e, "Error handling notification");
647 }
648 });
649 let unit = pin!(async {
650 let _ = select(tasks, notifs).await;
651 });
652 match select(requests, unit).await {
653 Either::Left((res, _)) => res,
654 Either::Right(((), _)) => None,
655 }
656}
657
658impl<S, Tr> ServerRuntime<S, Tr>
659where
660 S: RequestRouter + Send + Sync,
661 Tr: Transport + 'static,
662 Tr::Error: Into<McpError>,
663{
664 pub const fn state(&self) -> &Arc<ServerState> {
666 &self.state
667 }
668
669 #[must_use]
675 pub fn notifier(&self) -> ServerNotifier {
676 ServerNotifier {
677 peer: Arc::new(TransportPeer::new(self.transport.clone())),
678 }
679 }
680
681 pub async fn run(&self) -> Result<(), McpError> {
693 use futures::future::{Either, select};
694 use futures::stream::{FuturesUnordered, StreamExt};
695
696 enum Step {
699 Message(Option<Message>),
700 Progress(Option<Box<BackgroundExec>>),
703 Ambient(Notification),
705 }
706
707 async fn next_ambient(
710 slot: &mut Option<mpsc::UnboundedReceiver<Notification>>,
711 ) -> Notification {
712 loop {
713 match slot {
714 Some(rx) => match rx.next().await {
715 Some(notification) => return notification,
716 None => *slot = None,
717 },
718 None => std::future::pending::<()>().await,
719 }
720 }
721 }
722
723 let mut ambient = self.state.take_ambient_receiver();
726
727 let max = self.config.max_concurrent_requests.max(1);
728 let mut in_flight = FuturesUnordered::new();
729 let mut background = FuturesUnordered::new();
732 let mut notifications = FuturesUnordered::new();
736 let mut queued: std::collections::VecDeque<Request> = std::collections::VecDeque::new();
741
742 let outcome = loop {
743 while in_flight.len() < max {
745 let Some(request) = queued.pop_front() else {
746 break;
747 };
748 in_flight.push(self.handle_request_isolated(request));
749 }
750
751 let recv = std::pin::pin!(self.transport.recv());
757 let published = std::pin::pin!(next_ambient(&mut ambient));
758 let idle = in_flight.is_empty() && background.is_empty() && notifications.is_empty();
759 let step = if idle {
760 match select(recv, published).await {
761 Either::Left((Ok(opt), _)) => Step::Message(opt),
762 Either::Left((Err(e), _)) => break Err(e.into()),
763 Either::Right((notification, _)) => Step::Ambient(notification),
764 }
765 } else {
766 let progress = std::pin::pin!(drive_sets(
767 &mut in_flight,
768 &mut background,
769 &mut notifications
770 ));
771 match select(select(recv, progress), published).await {
772 Either::Left((Either::Left((Ok(opt), _)), _)) => Step::Message(opt),
773 Either::Left((Either::Left((Err(e), _)), _)) => break Err(e.into()),
774 Either::Left((Either::Right((maybe_exec, _)), _)) => {
775 Step::Progress(maybe_exec.map(Box::new))
776 }
777 Either::Right((notification, _)) => Step::Ambient(notification),
778 }
779 };
780
781 match step {
783 Step::Progress(Some(exec)) => {
784 background.push(self.run_task(*exec));
785 }
786 Step::Progress(None) => {}
787 Step::Ambient(notification) => {
788 if let Err(e) = self
793 .transport
794 .send(Message::Notification(notification))
795 .await
796 {
797 tracing::warn!(error = ?e, "failed to send ambient notification");
798 }
799 }
800 Step::Message(Some(Message::Request(request))) => {
801 if in_flight.len() < max {
802 in_flight.push(self.handle_request_isolated(request));
803 } else {
804 queued.push_back(request);
805 }
806 }
807 Step::Message(Some(Message::Notification(notification))) => {
808 notifications.push(self.handle_notification(notification));
812 }
813 Step::Message(Some(Message::Response(response))) => {
814 self.state.route_response(response);
816 }
817 Step::Message(None) => {
818 tracing::info!("Connection closed");
819 break Ok(());
820 }
821 }
822 };
823
824 self.state.fail_pending_requests();
829 while in_flight.next().await.is_some() {}
830 while background.next().await.is_some() {}
831 while notifications.next().await.is_some() {}
832
833 if let Err(ref err) = outcome {
834 tracing::error!(error = %err, "Transport error");
835 }
836 outcome
837 }
838
839 async fn compute_response(&self, request: &Request) -> Result<serde_json::Value, McpError> {
841 match request.method.as_ref() {
842 "initialize" => self.handle_initialize(request).await,
843 "ping" => self.route_request(request).await,
846 _ if !self.state.is_initialized() => {
847 Err(McpError::invalid_request("Server not initialized"))
848 }
849 _ => self.route_request(request).await,
850 }
851 }
852
853 async fn handle_request_isolated(&self, request: Request) -> Option<BackgroundExec> {
859 use futures::FutureExt;
860 use std::panic::AssertUnwindSafe;
861
862 let id = request.id.clone();
863 tracing::debug!(method = %request.method, id = %id, "Handling request");
864
865 match self.try_begin_task(&request).await {
868 TaskBegin::Deferred(exec) => return Some(*exec),
869 TaskBegin::Rejected => return None,
870 TaskBegin::NotApplicable => {}
871 }
872
873 let computed = AssertUnwindSafe(self.compute_response(&request))
874 .catch_unwind()
875 .await;
876
877 let response_msg = match computed {
878 Ok(Ok(result)) => Response::success(id, result),
879 Ok(Err(e)) => Response::error(id, e.into()),
880 Err(panic) => {
881 let detail = panic_message(&*panic);
882 tracing::error!(method = %request.method, panic = %detail, "Handler panicked");
883 Response::error(
884 id,
885 McpError::internal(format!("handler panicked: {detail}")).into(),
886 )
887 }
888 };
889
890 if let Err(e) = self.transport.send(Message::Response(response_msg)).await {
891 let err: McpError = e.into();
892 tracing::error!(error = %err, "Failed to send response");
893 }
894 None
895 }
896
897 async fn try_begin_task(&self, request: &Request) -> TaskBegin {
902 if request.method.as_ref() != "tools/call" {
903 return TaskBegin::NotApplicable;
904 }
905 let params = request.params.as_ref();
906 let Some(task_meta) = params.and_then(|p| p.get("task")) else {
907 return TaskBegin::NotApplicable;
908 };
909 if task_meta.is_null() {
910 return TaskBegin::NotApplicable;
911 }
912 if !self.state.is_initialized() {
914 return TaskBegin::NotApplicable;
915 }
916 let Some(name) = params
917 .and_then(|p| p.get("name"))
918 .and_then(|v| v.as_str())
919 .map(str::to_string)
920 else {
921 return TaskBegin::NotApplicable;
923 };
924 let args = match params.and_then(|p| p.get("arguments")) {
925 None => mcpkit_core::types::Object::new(),
926 Some(serde_json::Value::Object(map)) => map.clone(),
927 Some(_) => return TaskBegin::NotApplicable,
929 };
930 let ttl = task_meta.get("ttl").and_then(serde_json::Value::as_u64);
931
932 let client_caps = self.state.client_caps();
933 let protocol_version = self
934 .state
935 .protocol_version()
936 .unwrap_or(ProtocolVersion::LATEST);
937
938 let support = {
941 let peer = TransportPeer::with_outbound(
942 self.transport.clone(),
943 self.state.clone(),
944 self.config.outbound_request_timeout,
945 );
946 let ctx = Context::new(
947 &request.id,
948 None,
949 &client_caps,
950 &self.state.server_caps,
951 protocol_version,
952 &peer,
953 );
954 self.server.tool_task_support(&name, &ctx).await
955 };
956 if support == mcpkit_core::types::TaskSupport::Forbidden {
957 let err = McpError::JsonRpc(mcpkit_core::error::JsonRpcError::method_not_found(
960 format!("tool '{name}' does not support task-augmented execution"),
961 ));
962 let _ = self
963 .transport
964 .send(Message::Response(Response::error(
965 request.id.clone(),
966 err.into(),
967 )))
968 .await;
969 return TaskBegin::Rejected;
970 }
971
972 let handle = self.task_store.create(ttl);
974 let task = handle
975 .task()
976 .unwrap_or_else(|| mcpkit_core::types::Task::new(handle.id().clone()));
977 let create_result =
978 serde_json::to_value(mcpkit_core::types::CreateTaskResult { task, meta: None })
979 .unwrap_or_default();
980 if let Err(e) = self
981 .transport
982 .send(Message::Response(Response::success(
983 request.id.clone(),
984 create_result,
985 )))
986 .await
987 {
988 let err: McpError = e.into();
989 tracing::error!(error = %err, "Failed to send CreateTaskResult");
990 }
991
992 let cancel = handle.cancel_token().unwrap_or_else(CancellationToken::new);
993 let ctx_data = ContextData::new(
994 request.id.clone(),
995 client_caps,
996 self.state.server_caps.clone(),
997 protocol_version,
998 );
999 TaskBegin::Deferred(Box::new(BackgroundExec {
1000 handle,
1001 name,
1002 args,
1003 ctx_data,
1004 cancel,
1005 }))
1006 }
1007
1008 async fn run_task(&self, exec: BackgroundExec) {
1011 let BackgroundExec {
1012 handle,
1013 name,
1014 args,
1015 ctx_data,
1016 cancel,
1017 } = exec;
1018 let peer = TransportPeer::with_outbound(
1019 self.transport.clone(),
1020 self.state.clone(),
1021 self.config.outbound_request_timeout,
1022 );
1023 let ctx = Context::with_cancellation(
1024 &ctx_data.request_id,
1025 None,
1026 &ctx_data.client_caps,
1027 &ctx_data.server_caps,
1028 ctx_data.protocol_version,
1029 &peer,
1030 cancel,
1031 );
1032 match self.server.call_tool_json(&name, args, &ctx).await {
1033 Ok(payload)
1036 if payload
1037 .get("isError")
1038 .and_then(serde_json::Value::as_bool)
1039 .unwrap_or(false) =>
1040 {
1041 let _ =
1042 handle.fail_with_result(payload, Some("tool reported an error".to_string()));
1043 }
1044 Ok(payload) => {
1045 let _ = handle.complete(payload);
1046 }
1047 Err(e) => {
1050 let _ = handle.fail_with_error(e.into());
1051 }
1052 }
1053 }
1054
1055 async fn route_runtime_tasks(
1061 &self,
1062 method: &str,
1063 params: Option<&serde_json::Value>,
1064 ) -> crate::capability::tasks::TaskRoute {
1065 crate::capability::tasks::route_task_store(&self.task_store, method, params).await
1066 }
1067
1068 async fn handle_initialize(&self, request: &Request) -> Result<serde_json::Value, McpError> {
1075 if self.state.is_initialized() {
1076 return Err(McpError::invalid_request("Already initialized"));
1077 }
1078
1079 let params = request
1081 .params
1082 .as_ref()
1083 .ok_or_else(|| McpError::invalid_params("initialize", "missing params"))?;
1084
1085 let requested_version_str = params
1087 .get("protocolVersion")
1088 .and_then(|v| v.as_str())
1089 .unwrap_or("");
1090
1091 let negotiated_version =
1093 ProtocolVersion::negotiate(requested_version_str, ProtocolVersion::ALL)
1094 .unwrap_or(ProtocolVersion::LATEST);
1095
1096 if requested_version_str == negotiated_version.as_str() {
1098 tracing::debug!(
1099 version = %negotiated_version,
1100 "Protocol version negotiated successfully"
1101 );
1102 } else {
1103 tracing::info!(
1104 requested = %requested_version_str,
1105 negotiated = %negotiated_version,
1106 supported = ?ProtocolVersion::ALL.iter().map(ProtocolVersion::as_str).collect::<Vec<_>>(),
1107 "Protocol version negotiation: client requested different version"
1108 );
1109 }
1110
1111 self.state.set_protocol_version(negotiated_version);
1113
1114 if let Some(caps) = params.get("capabilities") {
1116 if let Ok(client_caps) = serde_json::from_value::<ClientCapabilities>(caps.clone()) {
1117 self.state.set_client_caps(client_caps);
1118 }
1119 }
1120
1121 let result = serde_json::json!({
1123 "protocolVersion": negotiated_version.as_str(),
1124 "serverInfo": self.server.server_info(),
1125 "capabilities": self.state.server_caps
1126 });
1127
1128 self.state.set_initialized();
1129
1130 Ok(result)
1131 }
1132
1133 async fn route_request(&self, request: &Request) -> Result<serde_json::Value, McpError> {
1135 let method = request.method.as_ref();
1136 let params = request.params.as_ref();
1137
1138 let unowned_task: Option<McpError> = match self.route_runtime_tasks(method, params).await {
1142 crate::capability::tasks::TaskRoute::Handled(result) => return result,
1143 crate::capability::tasks::TaskRoute::NotTaskMethod => None,
1144 unowned => unowned.or_unknown_task().and_then(Result::err),
1145 };
1146
1147 let progress_token = extract_progress_token(params);
1149
1150 let peer = TransportPeer::with_outbound(
1153 self.transport.clone(),
1154 self.state.clone(),
1155 self.config.outbound_request_timeout,
1156 );
1157 let client_caps = self.state.client_caps();
1158 let protocol_version = self
1159 .state
1160 .protocol_version()
1161 .unwrap_or(ProtocolVersion::LATEST);
1162
1163 let cancel = CancellationToken::new();
1167 let cancel_key = request.id.to_string();
1168 self.state
1169 .register_cancellation(&cancel_key, cancel.clone());
1170
1171 let ctx = Context::with_cancellation(
1172 &request.id,
1173 progress_token.as_ref(),
1174 &client_caps,
1175 &self.state.server_caps,
1176 protocol_version,
1177 &peer,
1178 cancel,
1179 );
1180
1181 let result = self.server.route(method, params, &ctx).await;
1183 self.state.remove_cancellation(&cancel_key);
1184
1185 if let Some(unowned) = unowned_task {
1192 if result.as_ref().err().map(McpError::code)
1193 == Some(mcpkit_core::error::codes::METHOD_NOT_FOUND)
1194 {
1195 return Err(unowned);
1196 }
1197 }
1198 result
1199 }
1200
1201 async fn handle_notification(&self, notification: Notification) -> Result<(), McpError> {
1203 let method = notification.method.as_ref();
1204
1205 tracing::debug!(method = %method, "Handling notification");
1206
1207 if method == crate::router::notifications::CANCELLED {
1210 if let Some(request_id) = notification
1211 .params
1212 .as_ref()
1213 .and_then(|p| {
1214 serde_json::from_value::<mcpkit_core::types::CancelledNotificationParams>(
1215 p.clone(),
1216 )
1217 .ok()
1218 })
1219 .and_then(|c| c.request_id)
1220 {
1221 self.state.cancel_request(&request_id.to_string());
1224 }
1225 return Ok(());
1226 }
1227
1228 let client_caps = self.state.client_caps();
1232 let protocol_version = self
1233 .state
1234 .protocol_version()
1235 .unwrap_or(ProtocolVersion::LATEST);
1236 let peer = TransportPeer::with_outbound(
1237 self.transport.clone(),
1238 self.state.clone(),
1239 self.config.outbound_request_timeout,
1240 );
1241 let ctx = Context::for_notification(
1242 &client_caps,
1243 &self.state.server_caps,
1244 protocol_version,
1245 &peer,
1246 );
1247 self.server
1248 .route_notification(method, notification.params.as_ref(), &ctx)
1249 .await;
1250 Ok(())
1251 }
1252}
1253
1254impl<H, T, R, P, K, Tr> ServerRuntime<Server<H, T, R, P, K>, Tr>
1256where
1257 H: ServerHandler + Send + Sync,
1258 T: Send + Sync,
1259 R: Send + Sync,
1260 P: Send + Sync,
1261 K: Send + Sync,
1262 Tr: Transport + 'static,
1263 Tr::Error: Into<McpError>,
1264{
1265 pub fn new(server: Server<H, T, R, P, K>, transport: Tr) -> Self {
1267 Self::with_config(server, transport, RuntimeConfig::default())
1268 }
1269
1270 pub fn with_config(
1272 server: Server<H, T, R, P, K>,
1273 transport: Tr,
1274 config: RuntimeConfig,
1275 ) -> Self {
1276 let caps = server.capabilities().clone();
1277 let task_store = Arc::new(
1278 crate::capability::tasks::TaskManager::with_default_ttl(config.default_task_ttl_ms)
1279 .with_poll_interval(config.default_task_poll_interval_ms),
1280 );
1281 let state = Arc::new(ServerState::new(caps));
1282 if config.task_status_notifications {
1283 let _ = task_store.set_observer(Arc::new(
1286 crate::capability::tasks::TaskStatusNotifier::new(state.clone()),
1287 ));
1288 }
1289 Self {
1290 server,
1291 transport: Arc::new(transport),
1292 state,
1293 task_store,
1294 config,
1295 }
1296 }
1297}
1298
1299#[allow(async_fn_in_trait)]
1304pub trait RequestRouter: Send + Sync {
1305 fn server_info(&self) -> mcpkit_core::capability::ServerInfo;
1307
1308 async fn route(
1310 &self,
1311 method: &str,
1312 params: Option<&serde_json::Value>,
1313 ctx: &Context<'_>,
1314 ) -> Result<serde_json::Value, McpError>;
1315
1316 async fn route_notification(
1321 &self,
1322 _method: &str,
1323 _params: Option<&serde_json::Value>,
1324 _ctx: &Context<'_>,
1325 ) {
1326 }
1327
1328 async fn tool_task_support(
1331 &self,
1332 _name: &str,
1333 _ctx: &Context<'_>,
1334 ) -> mcpkit_core::types::TaskSupport {
1335 mcpkit_core::types::TaskSupport::Forbidden
1336 }
1337
1338 async fn call_tool_json(
1342 &self,
1343 name: &str,
1344 _args: mcpkit_core::types::Object,
1345 _ctx: &Context<'_>,
1346 ) -> Result<serde_json::Value, McpError> {
1347 Err(McpError::method_not_found(name))
1348 }
1349}
1350
1351impl<H, T, R, P, K> Server<H, T, R, P, K>
1353where
1354 H: ServerHandler + Send + Sync + 'static,
1355 T: Send + Sync + 'static,
1356 R: Send + Sync + 'static,
1357 P: Send + Sync + 'static,
1358 K: Send + Sync + 'static,
1359 Self: RequestRouter,
1360{
1361 pub async fn serve<Tr>(self, transport: Tr) -> Result<(), McpError>
1363 where
1364 Tr: Transport + 'static,
1365 Tr::Error: Into<McpError>,
1366 {
1367 let runtime = ServerRuntime::new(self, transport);
1368 runtime.run().await
1369 }
1370}
1371
1372impl<H, T, R, P, K> RequestRouter for Server<H, T, R, P, K>
1384where
1385 H: ServerHandler + Send + Sync,
1386 T: ToolSlot,
1387 R: ResourceSlot,
1388 P: PromptSlot,
1389 K: TaskSlot,
1390{
1391 fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
1392 self.handler().server_info()
1393 }
1394
1395 async fn route_notification(
1396 &self,
1397 method: &str,
1398 _params: Option<&serde_json::Value>,
1399 ctx: &Context<'_>,
1400 ) {
1401 crate::router::dispatch_notification_hooks(self.handler(), method, ctx).await;
1402 }
1403
1404 async fn route(
1405 &self,
1406 method: &str,
1407 params: Option<&serde_json::Value>,
1408 ctx: &Context<'_>,
1409 ) -> Result<serde_json::Value, McpError> {
1410 if method == "ping" {
1411 return Ok(serde_json::json!({}));
1412 }
1413 let page_size = self.list_page_size;
1414 if let Some(handler) = self.tools.as_tool_handler() {
1415 if let Some(result) = route_tools(handler, method, params, ctx, page_size).await {
1416 return result;
1417 }
1418 }
1419 if let Some(handler) = self.resources.as_resource_handler() {
1420 if let Some(result) = route_resources(handler, method, params, ctx, page_size).await {
1421 return result;
1422 }
1423 }
1424 if let Some(handler) = self.prompts.as_prompt_handler() {
1425 if let Some(result) = route_prompts(handler, method, params, ctx, page_size).await {
1426 return result;
1427 }
1428 }
1429 if let Some(handler) = self.tasks.as_task_handler() {
1430 if let Some(result) = route_tasks(handler, method, params, ctx).await {
1431 return result;
1432 }
1433 }
1434 if let Some(result) =
1437 crate::router::route_logging(self.handler(), self.capabilities(), method, params, ctx)
1438 .await
1439 {
1440 return result;
1441 }
1442 if let Some(result) =
1445 crate::router::route_completion(self.completion.as_deref(), method, params, ctx).await
1446 {
1447 return result;
1448 }
1449 Err(McpError::method_not_found(method))
1450 }
1451
1452 async fn tool_task_support(
1453 &self,
1454 name: &str,
1455 ctx: &Context<'_>,
1456 ) -> mcpkit_core::types::TaskSupport {
1457 match self.tools.as_tool_handler() {
1458 Some(handler) => crate::router::tool_task_support(handler, name, ctx).await,
1459 None => mcpkit_core::types::TaskSupport::Forbidden,
1460 }
1461 }
1462
1463 async fn call_tool_json(
1464 &self,
1465 name: &str,
1466 args: mcpkit_core::types::Object,
1467 ctx: &Context<'_>,
1468 ) -> Result<serde_json::Value, McpError> {
1469 match self.tools.as_tool_handler() {
1470 Some(handler) => crate::router::call_tool_json(handler, name, args, ctx).await,
1471 None => Err(McpError::method_not_found(name)),
1472 }
1473 }
1474}
1475
1476fn panic_message(panic: &(dyn std::any::Any + Send)) -> String {
1498 if let Some(s) = panic.downcast_ref::<&str>() {
1499 (*s).to_string()
1500 } else if let Some(s) = panic.downcast_ref::<String>() {
1501 s.clone()
1502 } else {
1503 "unknown panic".to_string()
1504 }
1505}
1506
1507fn extract_progress_token(params: Option<&serde_json::Value>) -> Option<ProgressToken> {
1508 params.and_then(mcpkit_core::types::Meta::progress_token_from_params)
1509}
1510
1511#[cfg(test)]
1512mod tests {
1513 use super::*;
1514
1515 use mcpkit_core::capability::{ClientCapabilities, ServerInfo};
1516 use mcpkit_core::protocol::RequestId;
1517 use mcpkit_core::types::content::Role;
1518 use mcpkit_core::types::elicitation::ElicitRequest;
1519 use mcpkit_core::types::sampling::{CreateMessageRequest, CreateMessageResult};
1520 use mcpkit_transport::MemoryTransport;
1521 use std::time::Duration;
1522 use tokio::sync::Notify;
1523 use tokio::time::timeout;
1524
1525 struct PanicRouter;
1527
1528 impl RequestRouter for PanicRouter {
1529 fn server_info(&self) -> ServerInfo {
1530 ServerInfo::new("panic-test", "0.0.0")
1531 }
1532 async fn route(
1533 &self,
1534 method: &str,
1535 _params: Option<&serde_json::Value>,
1536 _ctx: &Context<'_>,
1537 ) -> Result<serde_json::Value, McpError> {
1538 match method {
1539 "panic" => panic!("boom in handler"),
1540 "ok" => Ok(serde_json::json!("ok")),
1541 other => Err(McpError::method_not_found(other)),
1542 }
1543 }
1544 }
1545
1546 struct CoordRouter {
1549 started: Arc<Notify>,
1550 release: Arc<Notify>,
1551 }
1552
1553 impl RequestRouter for CoordRouter {
1554 fn server_info(&self) -> ServerInfo {
1555 ServerInfo::new("coord-test", "0.0.0")
1556 }
1557 async fn route(
1558 &self,
1559 method: &str,
1560 _params: Option<&serde_json::Value>,
1561 _ctx: &Context<'_>,
1562 ) -> Result<serde_json::Value, McpError> {
1563 match method {
1564 "blocker" => {
1565 self.started.notify_one();
1566 self.release.notified().await;
1567 Ok(serde_json::json!("blocked-done"))
1568 }
1569 "fast" => Ok(serde_json::json!("fast-done")),
1570 other => Err(McpError::method_not_found(other)),
1571 }
1572 }
1573 }
1574
1575 struct PingRouter;
1578
1579 impl RequestRouter for PingRouter {
1580 fn server_info(&self) -> ServerInfo {
1581 ServerInfo::new("ping-test", "0.0.0")
1582 }
1583 async fn route(
1584 &self,
1585 method: &str,
1586 _params: Option<&serde_json::Value>,
1587 _ctx: &Context<'_>,
1588 ) -> Result<serde_json::Value, McpError> {
1589 match method {
1590 "ping" => Ok(serde_json::json!({})),
1591 other => Err(McpError::method_not_found(other)),
1592 }
1593 }
1594 }
1595
1596 struct CancelRouter {
1600 started: Arc<Notify>,
1601 }
1602
1603 impl RequestRouter for CancelRouter {
1604 fn server_info(&self) -> ServerInfo {
1605 ServerInfo::new("cancel-test", "0.0.0")
1606 }
1607 async fn route(
1608 &self,
1609 method: &str,
1610 _params: Option<&serde_json::Value>,
1611 ctx: &Context<'_>,
1612 ) -> Result<serde_json::Value, McpError> {
1613 match method {
1614 "wait_cancel" => {
1615 self.started.notify_one();
1616 ctx.cancelled().await;
1617 Ok(serde_json::json!(ctx.is_cancelled()))
1618 }
1619 other => Err(McpError::method_not_found(other)),
1620 }
1621 }
1622 }
1623
1624 struct OutboundRouter;
1628
1629 impl RequestRouter for OutboundRouter {
1630 fn server_info(&self) -> ServerInfo {
1631 ServerInfo::new("outbound-test", "0.0.0")
1632 }
1633 async fn route(
1634 &self,
1635 method: &str,
1636 _params: Option<&serde_json::Value>,
1637 ctx: &Context<'_>,
1638 ) -> Result<serde_json::Value, McpError> {
1639 match method {
1640 "ask" => ctx.request("ask/upstream", None).await,
1641 other => Err(McpError::method_not_found(other)),
1642 }
1643 }
1644 }
1645
1646 struct ElicitRouter;
1649
1650 impl RequestRouter for ElicitRouter {
1651 fn server_info(&self) -> ServerInfo {
1652 ServerInfo::new("elicit-test", "0.0.0")
1653 }
1654 async fn route(
1655 &self,
1656 method: &str,
1657 _params: Option<&serde_json::Value>,
1658 ctx: &Context<'_>,
1659 ) -> Result<serde_json::Value, McpError> {
1660 match method {
1661 "ask_name" => {
1662 let result = ctx
1663 .elicit(ElicitRequest::text("Your name?", "name"))
1664 .await?;
1665 Ok(serde_json::json!({
1666 "accepted": result.is_accepted(),
1667 "name": result.get_string("name"),
1668 }))
1669 }
1670 other => Err(McpError::method_not_found(other)),
1671 }
1672 }
1673 }
1674
1675 struct SampleRouter;
1678
1679 impl RequestRouter for SampleRouter {
1680 fn server_info(&self) -> ServerInfo {
1681 ServerInfo::new("sample-test", "0.0.0")
1682 }
1683 async fn route(
1684 &self,
1685 method: &str,
1686 _params: Option<&serde_json::Value>,
1687 ctx: &Context<'_>,
1688 ) -> Result<serde_json::Value, McpError> {
1689 match method {
1690 "summarize" => {
1691 let result = ctx
1692 .create_message(CreateMessageRequest::simple("hello", 100))
1693 .await?;
1694 Ok(serde_json::json!({ "text": result.as_text() }))
1695 }
1696 other => Err(McpError::method_not_found(other)),
1697 }
1698 }
1699 }
1700
1701 fn req(method: &'static str, id: u64) -> Message {
1702 Message::Request(Request::new(method, id))
1703 }
1704
1705 async fn next_response(transport: &MemoryTransport) -> Response {
1710 for _ in 0..16 {
1711 let msg = timeout(Duration::from_secs(2), transport.recv())
1712 .await
1713 .expect("no response (connection died?)")
1714 .expect("recv ok")
1715 .expect("some message");
1716 match msg {
1717 Message::Response(r) => return r,
1718 Message::Notification(_) => continue,
1719 other => panic!("expected response, got {other:?}"),
1720 }
1721 }
1722 panic!("no response after 16 messages");
1723 }
1724
1725 fn notif_msg(method: &str) -> Message {
1726 Message::Notification(Notification::with_params(
1727 method.to_string(),
1728 serde_json::json!({}),
1729 ))
1730 }
1731
1732 struct RootsHookHandler {
1735 initialized: Arc<std::sync::atomic::AtomicBool>,
1736 roots_changed: Arc<std::sync::atomic::AtomicUsize>,
1737 seen_roots: Arc<std::sync::Mutex<Vec<mcpkit_core::types::Root>>>,
1738 done: Arc<Notify>,
1739 }
1740
1741 impl crate::handler::ServerHandler for RootsHookHandler {
1742 fn server_info(&self) -> ServerInfo {
1743 ServerInfo::new("roots-test", "0.0.0")
1744 }
1745 async fn on_initialized(&self, _ctx: &Context<'_>) {
1746 self.initialized
1747 .store(true, std::sync::atomic::Ordering::SeqCst);
1748 self.done.notify_one();
1749 }
1750 async fn on_roots_list_changed(&self, ctx: &Context<'_>) {
1751 self.roots_changed
1752 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1753 if let Ok(roots) = ctx.list_roots().await {
1754 *self.seen_roots.lock().expect("lock") = roots;
1755 }
1756 self.done.notify_one();
1757 }
1758 }
1759
1760 #[tokio::test]
1761 async fn notification_hooks_fire_and_on_roots_list_changed_can_list_roots() {
1762 use crate::builder::ServerBuilder;
1763 use mcpkit_core::capability::ClientCapabilities;
1764 use mcpkit_core::types::{ListRootsResult, Root};
1765 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1766
1767 let initialized = Arc::new(AtomicBool::new(false));
1768 let roots_changed = Arc::new(AtomicUsize::new(0));
1769 let seen = Arc::new(std::sync::Mutex::new(Vec::new()));
1770 let done = Arc::new(Notify::new());
1771 let handler = RootsHookHandler {
1772 initialized: initialized.clone(),
1773 roots_changed: roots_changed.clone(),
1774 seen_roots: seen.clone(),
1775 done: done.clone(),
1776 };
1777
1778 let (client, server_tr) = MemoryTransport::pair();
1779 let runtime = ServerRuntime::new(ServerBuilder::new(handler).build(), server_tr);
1780 runtime.state().set_initialized();
1781 runtime
1782 .state()
1783 .set_client_caps(ClientCapabilities::default().with_roots());
1784 let handle = tokio::spawn(async move { runtime.run().await });
1785
1786 client
1788 .send(notif_msg("notifications/initialized"))
1789 .await
1790 .expect("send");
1791 timeout(Duration::from_secs(2), done.notified())
1792 .await
1793 .expect("on_initialized never ran");
1794 assert!(initialized.load(Ordering::SeqCst));
1795
1796 client
1799 .send(notif_msg("notifications/roots/list_changed"))
1800 .await
1801 .expect("send");
1802 let roots_req = match timeout(Duration::from_secs(2), client.recv())
1803 .await
1804 .expect("no roots/list request")
1805 .expect("recv ok")
1806 .expect("some message")
1807 {
1808 Message::Request(r) => r,
1809 other => panic!("expected roots/list, got {other:?}"),
1810 };
1811 assert_eq!(roots_req.method.as_ref(), "roots/list");
1812 let result = ListRootsResult {
1813 roots: vec![Root::new("file:///work")],
1814 meta: None,
1815 };
1816 client
1817 .send(Message::Response(Response::success(
1818 roots_req.id.clone(),
1819 serde_json::to_value(result).expect("serialize"),
1820 )))
1821 .await
1822 .expect("send");
1823
1824 timeout(Duration::from_secs(2), done.notified())
1825 .await
1826 .expect("on_roots_list_changed never finished");
1827 assert_eq!(roots_changed.load(Ordering::SeqCst), 1);
1828 assert_eq!(seen.lock().expect("lock")[0].uri, "file:///work");
1829
1830 drop(client);
1831 let _ = timeout(Duration::from_secs(2), handle).await;
1832 }
1833
1834 #[tokio::test]
1835 async fn roots_list_changed_is_ignored_without_roots_capability() {
1836 use crate::builder::ServerBuilder;
1837 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1838
1839 let roots_changed = Arc::new(AtomicUsize::new(0));
1840 let handler = RootsHookHandler {
1841 initialized: Arc::new(AtomicBool::new(false)),
1842 roots_changed: roots_changed.clone(),
1843 seen_roots: Arc::new(std::sync::Mutex::new(Vec::new())),
1844 done: Arc::new(Notify::new()),
1845 };
1846
1847 let (client, server_tr) = MemoryTransport::pair();
1848 let runtime = ServerRuntime::new(ServerBuilder::new(handler).build(), server_tr);
1850 runtime.state().set_initialized();
1851 let handle = tokio::spawn(async move { runtime.run().await });
1852
1853 client
1854 .send(notif_msg("notifications/roots/list_changed"))
1855 .await
1856 .expect("send");
1857 client.send(req("ping", 1)).await.expect("send");
1860 let resp = next_response(&client).await;
1861 assert_eq!(resp.id, RequestId::Number(1));
1862 assert_eq!(
1863 roots_changed.load(Ordering::SeqCst),
1864 0,
1865 "on_roots_list_changed must not fire without the roots capability"
1866 );
1867
1868 drop(client);
1869 let _ = timeout(Duration::from_secs(2), handle).await;
1870 }
1871
1872 #[tokio::test]
1873 async fn task_transition_publishes_status_notification() {
1874 let (client, server) = MemoryTransport::pair();
1875 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
1876 state.set_initialized();
1877 let task_store = Arc::new(crate::capability::tasks::TaskManager::new());
1878 task_store
1879 .set_observer(Arc::new(crate::capability::tasks::TaskStatusNotifier::new(
1880 state.clone(),
1881 )))
1882 .expect("install observer");
1883 let runtime = ServerRuntime {
1884 server: PingRouter,
1885 transport: Arc::new(server),
1886 state,
1887 task_store: Arc::clone(&task_store),
1888 config: RuntimeConfig::default(),
1889 };
1890 let handle = tokio::spawn(async move { runtime.run().await });
1891
1892 let task = task_store.create(None);
1895 task.complete(serde_json::json!({"ok": true}))
1896 .expect("complete");
1897
1898 let msg = timeout(Duration::from_secs(2), client.recv())
1899 .await
1900 .expect("no notification (loop never drained the queue?)")
1901 .expect("recv ok")
1902 .expect("some message");
1903 let Message::Notification(notification) = msg else {
1904 panic!("expected notification, got {msg:?}");
1905 };
1906 assert_eq!(notification.method, "notifications/tasks/status");
1907
1908 let params = notification.params.expect("params");
1909 assert_eq!(params["taskId"], task.id().as_str());
1910 assert_eq!(params["status"], "completed");
1911 assert!(
1914 params.get("_meta").is_none(),
1915 "status notification must not carry _meta: {params}"
1916 );
1917
1918 drop(client);
1919 let _ = timeout(Duration::from_secs(2), handle).await;
1920 }
1921
1922 #[tokio::test]
1923 async fn task_status_notifications_can_be_disabled() {
1924 let (client, server) = MemoryTransport::pair();
1925 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
1926 state.set_initialized();
1927 let config = RuntimeConfig::new().task_status_notifications(false);
1928 let task_store = Arc::new(crate::capability::tasks::TaskManager::new());
1930 let runtime = ServerRuntime {
1931 server: PingRouter,
1932 transport: Arc::new(server),
1933 state,
1934 task_store: Arc::clone(&task_store),
1935 config,
1936 };
1937 let handle = tokio::spawn(async move { runtime.run().await });
1938
1939 let task = task_store.create(None);
1940 task.complete(serde_json::json!({})).expect("complete");
1941
1942 client.send(req("ping", 1)).await.expect("send");
1945 let resp = next_response(&client).await;
1946 assert_eq!(resp.id, RequestId::Number(1));
1947
1948 drop(client);
1949 let _ = timeout(Duration::from_secs(2), handle).await;
1950 }
1951
1952 #[tokio::test]
1953 async fn panic_in_handler_returns_internal_error_and_keeps_connection() {
1954 let (client, server) = MemoryTransport::pair();
1955 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
1956 state.set_initialized();
1957 let runtime = ServerRuntime {
1958 server: PanicRouter,
1959 transport: Arc::new(server),
1960 state,
1961 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
1962 config: RuntimeConfig::default(),
1963 };
1964 let handle = tokio::spawn(async move { runtime.run().await });
1965
1966 client.send(req("panic", 1)).await.expect("send");
1968 let resp = next_response(&client).await;
1969 assert_eq!(resp.id, RequestId::Number(1));
1970 let err = resp.error.expect("expected error response");
1971 assert!(
1972 err.message.contains("panicked"),
1973 "unexpected error message: {}",
1974 err.message
1975 );
1976
1977 client.send(req("ok", 2)).await.expect("send");
1979 let resp = next_response(&client).await;
1980 assert_eq!(resp.id, RequestId::Number(2));
1981 assert!(
1982 resp.result.is_some(),
1983 "expected success after a prior panic"
1984 );
1985
1986 drop(client);
1987 let _ = timeout(Duration::from_secs(2), handle).await;
1988 }
1989
1990 #[tokio::test]
1991 async fn ping_is_answered_before_initialize() {
1992 let (client, server) = MemoryTransport::pair();
1993 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
1995 let runtime = ServerRuntime {
1996 server: PingRouter,
1997 transport: Arc::new(server),
1998 state,
1999 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2000 config: RuntimeConfig::default(),
2001 };
2002 let handle = tokio::spawn(async move { runtime.run().await });
2003
2004 client.send(req("ping", 1)).await.expect("send");
2006 let resp = next_response(&client).await;
2007 assert_eq!(resp.id, RequestId::Number(1));
2008 assert!(
2009 resp.error.is_none(),
2010 "ping before initialize must not error: {:?}",
2011 resp.error
2012 );
2013 assert!(resp.result.is_some(), "ping should return a result");
2014
2015 client.send(req("tools/list", 2)).await.expect("send");
2017 let resp = next_response(&client).await;
2018 assert_eq!(resp.id, RequestId::Number(2));
2019 assert!(
2020 resp.error.is_some(),
2021 "non-ping requests before initialize must still be rejected"
2022 );
2023
2024 drop(client);
2025 let _ = timeout(Duration::from_secs(2), handle).await;
2026 }
2027
2028 #[tokio::test]
2029 async fn requests_are_processed_concurrently() {
2030 let (client, server) = MemoryTransport::pair();
2031 let started = Arc::new(Notify::new());
2032 let release = Arc::new(Notify::new());
2033 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2034 state.set_initialized();
2035 let runtime = ServerRuntime {
2036 server: CoordRouter {
2037 started: started.clone(),
2038 release: release.clone(),
2039 },
2040 transport: Arc::new(server),
2041 state,
2042 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2043 config: RuntimeConfig::default(),
2044 };
2045 let handle = tokio::spawn(async move { runtime.run().await });
2046
2047 client.send(req("blocker", 1)).await.expect("send");
2048 client.send(req("fast", 2)).await.expect("send");
2049
2050 timeout(Duration::from_secs(2), started.notified())
2052 .await
2053 .expect("blocker never started");
2054
2055 let resp = next_response(&client).await;
2059 assert_eq!(
2060 resp.id,
2061 RequestId::Number(2),
2062 "fast request should finish first"
2063 );
2064
2065 release.notify_one();
2067 let resp = next_response(&client).await;
2068 assert_eq!(resp.id, RequestId::Number(1));
2069
2070 drop(client);
2071 let _ = timeout(Duration::from_secs(2), handle).await;
2072 }
2073
2074 #[tokio::test]
2075 async fn max_concurrent_requests_limits_in_flight() {
2076 let (client, server) = MemoryTransport::pair();
2079 let started = Arc::new(Notify::new());
2080 let release = Arc::new(Notify::new());
2081 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2082 state.set_initialized();
2083 let runtime = ServerRuntime {
2084 server: CoordRouter {
2085 started: started.clone(),
2086 release: release.clone(),
2087 },
2088 transport: Arc::new(server),
2089 state,
2090 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2091 config: RuntimeConfig {
2092 auto_initialized: true,
2093 max_concurrent_requests: 1,
2094 ..RuntimeConfig::default()
2095 },
2096 };
2097 let handle = tokio::spawn(async move { runtime.run().await });
2098
2099 client.send(req("blocker", 1)).await.expect("send");
2100 client.send(req("fast", 2)).await.expect("send");
2101
2102 timeout(Duration::from_secs(2), started.notified())
2103 .await
2104 .expect("blocker never started");
2105
2106 let early = timeout(Duration::from_millis(200), client.recv()).await;
2109 assert!(
2110 early.is_err(),
2111 "fast request was processed despite max_concurrent_requests = 1"
2112 );
2113
2114 release.notify_one();
2116 assert_eq!(next_response(&client).await.id, RequestId::Number(1));
2117 assert_eq!(next_response(&client).await.id, RequestId::Number(2));
2118
2119 drop(client);
2120 let _ = timeout(Duration::from_secs(2), handle).await;
2121 }
2122
2123 #[tokio::test]
2124 async fn cancelled_notification_trips_in_flight_handler() {
2125 let (client, server) = MemoryTransport::pair();
2126 let started = Arc::new(Notify::new());
2127 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2128 state.set_initialized();
2129 let runtime = ServerRuntime {
2130 server: CancelRouter {
2131 started: started.clone(),
2132 },
2133 transport: Arc::new(server),
2134 state,
2135 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2136 config: RuntimeConfig::default(),
2137 };
2138 let handle = tokio::spawn(async move { runtime.run().await });
2139
2140 client.send(req("wait_cancel", 1)).await.expect("send");
2142 timeout(Duration::from_secs(2), started.notified())
2143 .await
2144 .expect("handler never started");
2145
2146 let cancel = Message::Notification(Notification::with_params(
2149 "notifications/cancelled".to_string(),
2150 serde_json::json!({ "requestId": 1 }),
2151 ));
2152 client.send(cancel).await.expect("send cancel");
2153
2154 let resp = next_response(&client).await;
2155 assert_eq!(resp.id, RequestId::Number(1));
2156 assert_eq!(
2157 resp.result,
2158 Some(serde_json::json!(true)),
2159 "ctx.is_cancelled() should be true after notifications/cancelled"
2160 );
2161
2162 drop(client);
2163 let _ = timeout(Duration::from_secs(2), handle).await;
2164 }
2165
2166 #[tokio::test]
2167 async fn notifier_sends_list_changed_outside_request() {
2168 let (client, server) = MemoryTransport::pair();
2169 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2170 let runtime = ServerRuntime {
2171 server: PingRouter,
2172 transport: Arc::new(server),
2173 state,
2174 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2175 config: RuntimeConfig::default(),
2176 };
2177
2178 let notifier = runtime.notifier();
2181 notifier.tools_list_changed().await.expect("notify");
2182
2183 let msg = timeout(Duration::from_secs(2), client.recv())
2184 .await
2185 .expect("no notification (timed out)")
2186 .expect("recv ok")
2187 .expect("some message");
2188 match msg {
2189 Message::Notification(n) => {
2190 assert_eq!(n.method.as_ref(), "notifications/tools/list_changed");
2191 }
2192 other => panic!("expected a notification, got {other:?}"),
2193 }
2194 }
2195
2196 #[tokio::test]
2197 async fn server_initiated_request_roundtrips_at_concurrency_limit() {
2198 let (client, server) = MemoryTransport::pair();
2199 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2200 state.set_initialized();
2201 let runtime = ServerRuntime {
2202 server: OutboundRouter,
2203 transport: Arc::new(server),
2204 state,
2205 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2206 config: RuntimeConfig {
2210 auto_initialized: true,
2211 max_concurrent_requests: 1,
2212 ..RuntimeConfig::default()
2213 },
2214 };
2215 let handle = tokio::spawn(async move { runtime.run().await });
2216
2217 client.send(req("ask", 1)).await.expect("send");
2219
2220 let outbound = match timeout(Duration::from_secs(2), client.recv())
2222 .await
2223 .expect("no outbound request (timed out)")
2224 .expect("recv ok")
2225 .expect("some message")
2226 {
2227 Message::Request(r) => r,
2228 other => panic!("expected a server-initiated request, got {other:?}"),
2229 };
2230 assert_eq!(outbound.method.as_ref(), "ask/upstream");
2231
2232 client
2234 .send(Message::Response(Response::success(
2235 outbound.id.clone(),
2236 serde_json::json!({ "answer": 42 }),
2237 )))
2238 .await
2239 .expect("send response");
2240
2241 let resp = next_response(&client).await;
2242 assert_eq!(resp.id, RequestId::Number(1));
2243 assert_eq!(resp.result, Some(serde_json::json!({ "answer": 42 })));
2244
2245 drop(client);
2246 let _ = timeout(Duration::from_secs(2), handle).await;
2247 }
2248
2249 #[tokio::test]
2250 async fn server_initiated_request_times_out() {
2251 let (client, server) = MemoryTransport::pair();
2252 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2253 state.set_initialized();
2254 let runtime = ServerRuntime {
2255 server: OutboundRouter,
2256 transport: Arc::new(server),
2257 state,
2258 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2259 config: RuntimeConfig {
2260 outbound_request_timeout: Duration::from_millis(100),
2261 ..RuntimeConfig::default()
2262 },
2263 };
2264 let handle = tokio::spawn(async move { runtime.run().await });
2265
2266 client.send(req("ask", 1)).await.expect("send");
2267
2268 let _outbound = timeout(Duration::from_secs(2), client.recv())
2270 .await
2271 .expect("no outbound request")
2272 .expect("recv ok")
2273 .expect("some message");
2274
2275 let resp = next_response(&client).await;
2277 assert_eq!(resp.id, RequestId::Number(1));
2278 assert!(resp.error.is_some(), "timed-out request should error");
2279
2280 drop(client);
2281 let _ = timeout(Duration::from_secs(2), handle).await;
2282 }
2283
2284 #[tokio::test]
2285 async fn ctx_elicit_roundtrips() {
2286 let (client, server) = MemoryTransport::pair();
2287 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2288 state.set_initialized();
2289 state.set_client_caps(ClientCapabilities::default().with_elicitation());
2290 let runtime = ServerRuntime {
2291 server: ElicitRouter,
2292 transport: Arc::new(server),
2293 state,
2294 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2295 config: RuntimeConfig::default(),
2296 };
2297 let handle = tokio::spawn(async move { runtime.run().await });
2298
2299 client.send(req("ask_name", 1)).await.expect("send");
2300
2301 let elicit = match timeout(Duration::from_secs(2), client.recv())
2303 .await
2304 .expect("no elicitation request")
2305 .expect("recv ok")
2306 .expect("some message")
2307 {
2308 Message::Request(r) => r,
2309 other => panic!("expected elicitation/create, got {other:?}"),
2310 };
2311 assert_eq!(elicit.method.as_ref(), "elicitation/create");
2312 assert!(
2313 elicit
2314 .params
2315 .as_ref()
2316 .and_then(|p| p.get("requestedSchema"))
2317 .is_some(),
2318 "elicitation request should carry a requestedSchema"
2319 );
2320
2321 client
2323 .send(Message::Response(Response::success(
2324 elicit.id.clone(),
2325 serde_json::json!({ "action": "accept", "content": { "name": "Ada" } }),
2326 )))
2327 .await
2328 .expect("send response");
2329
2330 let resp = next_response(&client).await;
2331 assert_eq!(resp.id, RequestId::Number(1));
2332 assert_eq!(
2333 resp.result,
2334 Some(serde_json::json!({ "accepted": true, "name": "Ada" }))
2335 );
2336
2337 drop(client);
2338 let _ = timeout(Duration::from_secs(2), handle).await;
2339 }
2340
2341 #[tokio::test]
2342 async fn ctx_elicit_requires_client_capability() {
2343 let (client, server) = MemoryTransport::pair();
2344 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2345 state.set_initialized();
2346 let runtime = ServerRuntime {
2348 server: ElicitRouter,
2349 transport: Arc::new(server),
2350 state,
2351 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2352 config: RuntimeConfig::default(),
2353 };
2354 let handle = tokio::spawn(async move { runtime.run().await });
2355
2356 client.send(req("ask_name", 1)).await.expect("send");
2357
2358 let resp = next_response(&client).await;
2362 assert_eq!(resp.id, RequestId::Number(1));
2363 assert!(
2364 resp.error.is_some(),
2365 "elicit without client capability should error"
2366 );
2367
2368 drop(client);
2369 let _ = timeout(Duration::from_secs(2), handle).await;
2370 }
2371
2372 #[tokio::test]
2373 async fn ctx_create_message_roundtrips() {
2374 let (client, server) = MemoryTransport::pair();
2375 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2376 state.set_initialized();
2377 state.set_client_caps(ClientCapabilities::default().with_sampling());
2378 let runtime = ServerRuntime {
2379 server: SampleRouter,
2380 transport: Arc::new(server),
2381 state,
2382 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2383 config: RuntimeConfig::default(),
2384 };
2385 let handle = tokio::spawn(async move { runtime.run().await });
2386
2387 client.send(req("summarize", 1)).await.expect("send");
2388
2389 let sampling = match timeout(Duration::from_secs(2), client.recv())
2390 .await
2391 .expect("no sampling request")
2392 .expect("recv ok")
2393 .expect("some message")
2394 {
2395 Message::Request(r) => r,
2396 other => panic!("expected sampling/createMessage, got {other:?}"),
2397 };
2398 assert_eq!(sampling.method.as_ref(), "sampling/createMessage");
2399
2400 let result = CreateMessageResult {
2402 role: Role::Assistant,
2403 content: mcpkit_core::types::OneOrMany::One(mcpkit_core::types::SamplingContent::text(
2404 "a summary",
2405 )),
2406 model: "test-model".to_string(),
2407 stop_reason: None,
2408 meta: None,
2409 };
2410 client
2411 .send(Message::Response(Response::success(
2412 sampling.id.clone(),
2413 serde_json::to_value(result).expect("serialize result"),
2414 )))
2415 .await
2416 .expect("send response");
2417
2418 let resp = next_response(&client).await;
2419 assert_eq!(resp.id, RequestId::Number(1));
2420 assert_eq!(
2421 resp.result,
2422 Some(serde_json::json!({ "text": "a summary" }))
2423 );
2424
2425 drop(client);
2426 let _ = timeout(Duration::from_secs(2), handle).await;
2427 }
2428
2429 #[tokio::test]
2430 async fn ctx_create_message_requires_client_capability() {
2431 let (client, server) = MemoryTransport::pair();
2432 let state = Arc::new(ServerState::new(ServerCapabilities::default()));
2433 state.set_initialized();
2434 let runtime = ServerRuntime {
2436 server: SampleRouter,
2437 transport: Arc::new(server),
2438 state,
2439 task_store: Arc::new(crate::capability::tasks::TaskManager::new()),
2440 config: RuntimeConfig::default(),
2441 };
2442 let handle = tokio::spawn(async move { runtime.run().await });
2443
2444 client.send(req("summarize", 1)).await.expect("send");
2445
2446 let resp = next_response(&client).await;
2448 assert_eq!(resp.id, RequestId::Number(1));
2449 assert!(
2450 resp.error.is_some(),
2451 "create_message without client capability should error"
2452 );
2453
2454 drop(client);
2455 let _ = timeout(Duration::from_secs(2), handle).await;
2456 }
2457
2458 #[test]
2459 fn test_server_state_initialization() {
2460 let state = ServerState::new(ServerCapabilities::default());
2461 assert!(!state.is_initialized());
2462
2463 state.set_initialized();
2464 assert!(state.is_initialized());
2465 }
2466
2467 #[test]
2468 fn test_cancellation_management() {
2469 let state = ServerState::new(ServerCapabilities::default());
2470 let token = CancellationToken::new();
2471
2472 state.register_cancellation("req-1", token.clone());
2473 assert!(!token.is_cancelled());
2474
2475 state.cancel_request("req-1");
2476 assert!(token.is_cancelled());
2477
2478 state.remove_cancellation("req-1");
2479 }
2480
2481 #[test]
2482 fn test_runtime_config_default() {
2483 let config = RuntimeConfig::default();
2484 assert!(config.auto_initialized);
2485 assert_eq!(config.max_concurrent_requests, 100);
2486 }
2487
2488 #[test]
2489 fn test_extract_progress_token_string() -> Result<(), Box<dyn std::error::Error>> {
2490 let params = serde_json::json!({
2491 "_meta": {
2492 "progressToken": "my-token-123"
2493 },
2494 "name": "test-tool"
2495 });
2496 let token = extract_progress_token(Some(¶ms));
2497 assert!(token.is_some());
2498 assert_eq!(
2499 token.ok_or("Token not found")?,
2500 ProgressToken::String("my-token-123".to_string())
2501 );
2502
2503 Ok(())
2504 }
2505
2506 #[test]
2507 fn test_extract_progress_token_number() -> Result<(), Box<dyn std::error::Error>> {
2508 let params = serde_json::json!({
2509 "_meta": {
2510 "progressToken": 42
2511 },
2512 "arguments": {}
2513 });
2514 let token = extract_progress_token(Some(¶ms));
2515 assert!(token.is_some());
2516 assert_eq!(token.ok_or("Token not found")?, ProgressToken::Number(42));
2517
2518 Ok(())
2519 }
2520
2521 #[test]
2522 fn test_extract_progress_token_missing_meta() {
2523 let params = serde_json::json!({
2524 "name": "test-tool",
2525 "arguments": {}
2526 });
2527 let token = extract_progress_token(Some(¶ms));
2528 assert!(token.is_none());
2529 }
2530
2531 #[test]
2532 fn test_extract_progress_token_missing_token() {
2533 let params = serde_json::json!({
2534 "_meta": {},
2535 "name": "test-tool"
2536 });
2537 let token = extract_progress_token(Some(¶ms));
2538 assert!(token.is_none());
2539 }
2540
2541 #[test]
2542 fn test_extract_progress_token_none_params() {
2543 let token = extract_progress_token(None);
2544 assert!(token.is_none());
2545 }
2546
2547 #[tokio::test]
2548 async fn task_augmented_tools_call_runs_in_background() {
2549 use crate::builder::ServerBuilder;
2550 use crate::handler::{ServerHandler, ToolHandler};
2551 use mcpkit_core::protocol::Request;
2552 use mcpkit_core::types::{TaskSupport, Tool, ToolOutput};
2553
2554 struct H;
2555 impl ServerHandler for H {
2556 fn server_info(&self) -> ServerInfo {
2557 ServerInfo::new("t", "1.0.0")
2558 }
2559 }
2560 impl ToolHandler for H {
2561 async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
2562 Ok(vec![Tool::new("slow").task_support(TaskSupport::Optional)])
2563 }
2564 async fn call_tool(
2565 &self,
2566 name: &str,
2567 _args: serde_json::Map<String, serde_json::Value>,
2568 _ctx: &Context<'_>,
2569 ) -> Result<ToolOutput, McpError> {
2570 Ok(ToolOutput::text(format!("done:{name}")))
2571 }
2572 }
2573
2574 let request = |id: u64, method: &'static str, params: serde_json::Value| {
2575 Message::Request(Request {
2576 jsonrpc: "2.0".into(),
2577 id: RequestId::Number(id),
2578 method: method.into(),
2579 params: Some(params),
2580 })
2581 };
2582
2583 let (client, server_tr) = MemoryTransport::pair();
2584 let built = ServerBuilder::new(H).with_tools(H).build();
2585 let runtime = ServerRuntime::new(built, server_tr);
2586 runtime.state().set_initialized();
2587 let handle = tokio::spawn(async move { runtime.run().await });
2588
2589 client
2591 .send(request(
2592 1,
2593 "tools/call",
2594 serde_json::json!({ "name": "slow", "arguments": {}, "task": {} }),
2595 ))
2596 .await
2597 .expect("send");
2598 let resp = next_response(&client).await;
2599 assert_eq!(resp.id, RequestId::Number(1));
2600 assert!(
2601 resp.error.is_none(),
2602 "augmented call errored: {:?}",
2603 resp.error
2604 );
2605 let result = resp.result.expect("create result");
2606 assert_eq!(result["task"]["status"], "working");
2607 let task_id = result["task"]["taskId"]
2608 .as_str()
2609 .expect("taskId")
2610 .to_string();
2611
2612 let mut payload = None;
2614 for attempt in 0..100u64 {
2615 client
2616 .send(request(
2617 100 + attempt,
2618 "tasks/result",
2619 serde_json::json!({ "taskId": task_id }),
2620 ))
2621 .await
2622 .expect("send");
2623 let r = next_response(&client).await;
2624 if r.error.is_none() {
2625 payload = r.result;
2626 break;
2627 }
2628 tokio::time::sleep(Duration::from_millis(10)).await;
2629 }
2630 let payload = payload.expect("task completed with a payload");
2631 assert!(
2632 payload["content"][0]["text"]
2633 .as_str()
2634 .unwrap_or_default()
2635 .contains("done:slow"),
2636 "unexpected task payload: {payload}"
2637 );
2638
2639 client
2641 .send(request(
2642 999,
2643 "tasks/get",
2644 serde_json::json!({ "taskId": task_id }),
2645 ))
2646 .await
2647 .expect("send");
2648 let got = next_response(&client).await;
2649 assert_eq!(got.result.expect("task")["status"], "completed");
2650
2651 drop(client);
2652 let _ = timeout(Duration::from_secs(2), handle).await;
2653 }
2654
2655 #[tokio::test]
2656 async fn tasks_result_blocks_while_loop_stays_live() {
2657 use crate::builder::ServerBuilder;
2658 use crate::handler::{ServerHandler, ToolHandler};
2659 use mcpkit_core::protocol::Request;
2660 use mcpkit_core::types::{TaskSupport, Tool, ToolOutput};
2661
2662 struct H(Arc<tokio::sync::Notify>);
2666 impl ServerHandler for H {
2667 fn server_info(&self) -> ServerInfo {
2668 ServerInfo::new("t", "1.0.0")
2669 }
2670 }
2671 impl ToolHandler for H {
2672 async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
2673 Ok(vec![Tool::new("gated").task_support(TaskSupport::Optional)])
2674 }
2675 async fn call_tool(
2676 &self,
2677 _name: &str,
2678 _args: serde_json::Map<String, serde_json::Value>,
2679 _ctx: &Context<'_>,
2680 ) -> Result<ToolOutput, McpError> {
2681 self.0.notified().await;
2682 Ok(ToolOutput::text("released"))
2683 }
2684 }
2685
2686 let request = |id: u64, method: &'static str, params: serde_json::Value| {
2687 Message::Request(Request {
2688 jsonrpc: "2.0".into(),
2689 id: RequestId::Number(id),
2690 method: method.into(),
2691 params: Some(params),
2692 })
2693 };
2694
2695 let release = Arc::new(tokio::sync::Notify::new());
2696 let (client, server_tr) = MemoryTransport::pair();
2697 let built = ServerBuilder::new(H(release.clone()))
2698 .with_tools(H(release.clone()))
2699 .build();
2700 let runtime = ServerRuntime::new(built, server_tr);
2701 runtime.state().set_initialized();
2702 let handle = tokio::spawn(async move { runtime.run().await });
2703
2704 client
2705 .send(request(
2706 1,
2707 "tools/call",
2708 serde_json::json!({ "name": "gated", "arguments": {}, "task": {} }),
2709 ))
2710 .await
2711 .expect("send");
2712 let resp = next_response(&client).await;
2713 let task_id = resp.result.expect("create result")["task"]["taskId"]
2714 .as_str()
2715 .expect("taskId")
2716 .to_string();
2717
2718 client
2720 .send(request(
2721 2,
2722 "tasks/result",
2723 serde_json::json!({ "taskId": task_id }),
2724 ))
2725 .await
2726 .expect("send");
2727
2728 client
2731 .send(request(3, "tools/list", serde_json::json!({})))
2732 .await
2733 .expect("send");
2734 let live = timeout(Duration::from_secs(2), next_response(&client))
2735 .await
2736 .expect("loop stalled while tasks/result was blocking");
2737 assert_eq!(live.id, RequestId::Number(3), "expected tools/list reply");
2738
2739 release.notify_one();
2741 let result = timeout(Duration::from_secs(2), next_response(&client))
2742 .await
2743 .expect("blocked tasks/result never completed");
2744 assert_eq!(result.id, RequestId::Number(2));
2745 let payload = result.result.expect("payload");
2746 assert!(
2747 payload["content"][0]["text"]
2748 .as_str()
2749 .unwrap_or_default()
2750 .contains("released"),
2751 "unexpected payload: {payload}"
2752 );
2753 assert_eq!(
2755 payload["_meta"]["io.modelcontextprotocol/related-task"]["taskId"],
2756 task_id.as_str()
2757 );
2758
2759 drop(client);
2760 let _ = timeout(Duration::from_secs(2), handle).await;
2761 }
2762
2763 #[tokio::test]
2764 async fn task_augmented_call_on_forbidden_tool_is_rejected() {
2765 use crate::builder::ServerBuilder;
2766 use crate::handler::{ServerHandler, ToolHandler};
2767 use mcpkit_core::protocol::Request;
2768 use mcpkit_core::types::{Tool, ToolOutput};
2769
2770 struct H;
2771 impl ServerHandler for H {
2772 fn server_info(&self) -> ServerInfo {
2773 ServerInfo::new("t", "1.0.0")
2774 }
2775 }
2776 impl ToolHandler for H {
2777 async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
2778 Ok(vec![Tool::new("plain")])
2780 }
2781 async fn call_tool(
2782 &self,
2783 _name: &str,
2784 _args: serde_json::Map<String, serde_json::Value>,
2785 _ctx: &Context<'_>,
2786 ) -> Result<ToolOutput, McpError> {
2787 Ok(ToolOutput::text("ok"))
2788 }
2789 }
2790
2791 let (client, server_tr) = MemoryTransport::pair();
2792 let runtime = ServerRuntime::new(ServerBuilder::new(H).with_tools(H).build(), server_tr);
2793 runtime.state().set_initialized();
2794 let handle = tokio::spawn(async move { runtime.run().await });
2795
2796 client
2797 .send(Message::Request(Request {
2798 jsonrpc: "2.0".into(),
2799 id: RequestId::Number(1),
2800 method: "tools/call".into(),
2801 params: Some(serde_json::json!({ "name": "plain", "task": {} })),
2802 }))
2803 .await
2804 .expect("send");
2805 let resp = next_response(&client).await;
2806 let err = resp
2807 .error
2808 .expect("a forbidden tool must reject task augmentation");
2809 assert_eq!(err.code, -32601, "wrong rejection code: {err:?}");
2811
2812 drop(client);
2813 let _ = timeout(Duration::from_secs(2), handle).await;
2814 }
2815
2816 #[cfg(feature = "schema-validation")]
2822 #[tokio::test]
2823 async fn task_path_validates_input_via_decorator() {
2824 use crate::builder::ServerBuilder;
2825 use crate::handler::{ServerHandler, ToolHandler};
2826 use mcpkit_core::protocol::Request;
2827 use mcpkit_core::types::{TaskSupport, Tool, ToolOutput};
2828
2829 struct H;
2830 impl ServerHandler for H {
2831 fn server_info(&self) -> ServerInfo {
2832 ServerInfo::new("t", "1.0.0")
2833 }
2834 }
2835 impl ToolHandler for H {
2836 async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
2837 Ok(vec![
2838 Tool::new("slow")
2839 .task_support(TaskSupport::Optional)
2840 .input_schema(serde_json::json!({
2841 "type": "object",
2842 "properties": { "n": { "type": "number" } },
2843 "required": ["n"]
2844 })),
2845 ])
2846 }
2847 async fn call_tool(
2848 &self,
2849 name: &str,
2850 _args: serde_json::Map<String, serde_json::Value>,
2851 _ctx: &Context<'_>,
2852 ) -> Result<ToolOutput, McpError> {
2853 Ok(ToolOutput::text(format!("done:{name}")))
2854 }
2855 }
2856
2857 let request = |id: u64, method: &'static str, params: serde_json::Value| {
2858 Message::Request(Request {
2859 jsonrpc: "2.0".into(),
2860 id: RequestId::Number(id),
2861 method: method.into(),
2862 params: Some(params),
2863 })
2864 };
2865
2866 let (client, server_tr) = MemoryTransport::pair();
2867 let built = ServerBuilder::new(H)
2870 .with_tools(H)
2871 .validate_tool_io()
2872 .build();
2873 let runtime = ServerRuntime::new(built, server_tr);
2874 runtime.state().set_initialized();
2875 let handle = tokio::spawn(async move { runtime.run().await });
2876
2877 client
2879 .send(request(
2880 1,
2881 "tools/call",
2882 serde_json::json!({ "name": "slow", "arguments": {}, "task": {} }),
2883 ))
2884 .await
2885 .expect("send");
2886 let resp = next_response(&client).await;
2887 let task_id = resp.result.expect("create result")["task"]["taskId"]
2888 .as_str()
2889 .expect("taskId")
2890 .to_string();
2891
2892 let mut payload = None;
2893 for attempt in 0..100u64 {
2894 client
2895 .send(request(
2896 100 + attempt,
2897 "tasks/result",
2898 serde_json::json!({ "taskId": task_id }),
2899 ))
2900 .await
2901 .expect("send");
2902 let r = next_response(&client).await;
2903 if r.error.is_none() {
2904 payload = r.result;
2905 break;
2906 }
2907 tokio::time::sleep(Duration::from_millis(10)).await;
2908 }
2909 let payload = payload.expect("task completed with a payload");
2910 assert_eq!(
2911 payload["isError"],
2912 serde_json::json!(true),
2913 "task path must validate input: {payload}"
2914 );
2915 assert!(
2916 !payload["content"][0]["text"]
2917 .as_str()
2918 .unwrap_or_default()
2919 .contains("done:slow"),
2920 "the tool body must not have run: {payload}"
2921 );
2922
2923 drop(client);
2924 let _ = timeout(Duration::from_secs(2), handle).await;
2925 }
2926
2927 #[tokio::test]
2928 async fn logging_set_level_dispatches_when_advertised_else_method_not_found() {
2929 use crate::builder::ServerBuilder;
2930 use crate::context::NoOpPeer;
2931 use crate::handler::ServerHandler;
2932 use mcpkit_core::capability::{ClientCapabilities, ServerCapabilities};
2933 use mcpkit_core::protocol::RequestId;
2934 use mcpkit_core::protocol_version::ProtocolVersion;
2935 use mcpkit_core::types::LoggingLevel;
2936 use std::sync::Mutex;
2937
2938 struct H(Arc<Mutex<Option<LoggingLevel>>>);
2939 impl ServerHandler for H {
2940 fn server_info(&self) -> ServerInfo {
2941 ServerInfo::new("t", "1.0.0")
2942 }
2943 async fn set_log_level(
2944 &self,
2945 level: LoggingLevel,
2946 _ctx: &Context<'_>,
2947 ) -> Result<(), McpError> {
2948 *self.0.lock().unwrap() = Some(level);
2949 Ok(())
2950 }
2951 }
2952
2953 let request_id = RequestId::Number(1);
2954 let client_caps = ClientCapabilities::default();
2955 let server_caps = ServerCapabilities::default();
2956 let peer = NoOpPeer;
2957 let ctx = Context::new(
2958 &request_id,
2959 None,
2960 &client_caps,
2961 &server_caps,
2962 ProtocolVersion::LATEST,
2963 &peer,
2964 );
2965
2966 let seen = Arc::new(Mutex::new(None));
2968 let server = ServerBuilder::new(H(Arc::clone(&seen)))
2969 .capabilities(ServerCapabilities::new().with_logging())
2970 .build();
2971 let out = server
2972 .route(
2973 "logging/setLevel",
2974 Some(&serde_json::json!({ "level": "warning" })),
2975 &ctx,
2976 )
2977 .await
2978 .expect("setLevel dispatched");
2979 assert_eq!(out, serde_json::json!({}));
2980 assert_eq!(*seen.lock().unwrap(), Some(LoggingLevel::Warning));
2981
2982 assert!(
2984 server
2985 .route(
2986 "logging/setLevel",
2987 Some(&serde_json::json!({ "level": "loud" })),
2988 &ctx,
2989 )
2990 .await
2991 .is_err()
2992 );
2993
2994 let plain = ServerBuilder::new(H(Arc::new(Mutex::new(None)))).build();
2996 let err = plain
2997 .route(
2998 "logging/setLevel",
2999 Some(&serde_json::json!({ "level": "info" })),
3000 &ctx,
3001 )
3002 .await
3003 .expect_err("no logging capability -> method not found");
3004 assert!(matches!(err, McpError::MethodNotFound { .. }));
3005 }
3006
3007 #[tokio::test]
3008 async fn context_log_emits_message_notification() {
3009 use crate::context::Peer;
3010 use mcpkit_core::capability::{ClientCapabilities, ServerCapabilities};
3011 use mcpkit_core::protocol::RequestId;
3012 use mcpkit_core::protocol_version::ProtocolVersion;
3013 use mcpkit_core::types::LoggingLevel;
3014 use std::pin::Pin;
3015 use std::sync::Mutex;
3016
3017 struct RecPeer(Arc<Mutex<Vec<Notification>>>);
3018 impl Peer for RecPeer {
3019 fn notify(
3020 &self,
3021 notification: Notification,
3022 ) -> Pin<Box<dyn std::future::Future<Output = Result<(), McpError>> + Send + '_>>
3023 {
3024 self.0.lock().unwrap().push(notification);
3025 Box::pin(async { Ok(()) })
3026 }
3027 }
3028
3029 let seen = Arc::new(Mutex::new(Vec::new()));
3030 let peer = RecPeer(Arc::clone(&seen));
3031 let request_id = RequestId::Number(1);
3032 let client_caps = ClientCapabilities::default();
3033 let server_caps = ServerCapabilities::default();
3034 let ctx = Context::new(
3035 &request_id,
3036 None,
3037 &client_caps,
3038 &server_caps,
3039 ProtocolVersion::LATEST,
3040 &peer,
3041 );
3042
3043 ctx.log(LoggingLevel::Error, Some("db"), serde_json::json!("boom"))
3044 .await
3045 .expect("log sent");
3046
3047 let seen = seen.lock().unwrap();
3048 assert_eq!(seen.len(), 1);
3049 assert_eq!(seen[0].method.as_ref(), "notifications/message");
3050 let params = seen[0].params.as_ref().expect("params");
3051 assert_eq!(params["level"], serde_json::json!("error"));
3052 assert_eq!(params["logger"], serde_json::json!("db"));
3053 assert_eq!(params["data"], serde_json::json!("boom"));
3054 }
3055}