turbomcp_client/client/operations/connection.rs
1//! Connection and utility operations for MCP client
2//!
3//! This module provides connection health checks and server configuration
4//! operations.
5
6use std::sync::atomic::Ordering;
7use turbomcp_protocol::types::{LogLevel, PingResult, SetLevelRequest, SetLevelResult};
8use turbomcp_protocol::{Error, Result};
9
10impl<T: turbomcp_transport::Transport + 'static> super::super::core::Client<T> {
11 /// Send a ping request to check server health and connectivity
12 ///
13 /// Sends a ping request to the server to verify the connection is active
14 /// and the server is responding. This is useful for health checks and
15 /// connection validation.
16 ///
17 /// # Returns
18 ///
19 /// Returns `PingResult` on successful ping.
20 ///
21 /// # Errors
22 ///
23 /// Returns an error if:
24 /// - The client is not initialized
25 /// - The server is not responding
26 /// - The connection has failed
27 ///
28 /// # Examples
29 ///
30 /// ```rust,no_run
31 /// # use turbomcp_client::Client;
32 /// # use turbomcp_transport::stdio::StdioTransport;
33 /// # async fn example() -> turbomcp_protocol::Result<()> {
34 /// let mut client = Client::new(StdioTransport::new());
35 /// client.initialize().await?;
36 ///
37 /// let result = client.ping().await?;
38 /// println!("Server is responding");
39 /// # Ok(())
40 /// # }
41 /// ```
42 pub async fn ping(&self) -> Result<PingResult> {
43 if !self.inner.initialized.load(Ordering::Relaxed) {
44 return Err(Error::invalid_request("Client not initialized"));
45 }
46
47 // Send ping request (no parameters needed)
48 let response: PingResult = self.inner.protocol.request("ping", None).await?;
49 Ok(response)
50 }
51
52 /// Set the logging level for the MCP server
53 ///
54 /// Controls the verbosity of logs sent from the server to the client.
55 /// Higher log levels provide more detailed information about server operations.
56 ///
57 /// # Arguments
58 ///
59 /// * `level` - The logging level to set (Error, Warn, Info, Debug)
60 ///
61 /// # Returns
62 ///
63 /// Returns `SetLevelResult` confirming the logging level change.
64 ///
65 /// # Errors
66 ///
67 /// Returns an error if:
68 /// - The client is not initialized
69 /// - The server doesn't support logging configuration
70 /// - The request fails
71 ///
72 /// # Examples
73 ///
74 /// ```rust,no_run
75 /// # use turbomcp_client::Client;
76 /// # use turbomcp_transport::stdio::StdioTransport;
77 /// # use turbomcp_protocol::types::LogLevel;
78 /// # async fn example() -> turbomcp_protocol::Result<()> {
79 /// let mut client = Client::new(StdioTransport::new());
80 /// client.initialize().await?;
81 ///
82 /// // Set server to debug logging
83 /// client.set_log_level(LogLevel::Debug).await?;
84 /// println!("Server logging level set to debug");
85 /// # Ok(())
86 /// # }
87 /// ```
88 pub async fn set_log_level(&self, level: LogLevel) -> Result<SetLevelResult> {
89 if !self.inner.initialized.load(Ordering::Relaxed) {
90 return Err(Error::invalid_request("Client not initialized"));
91 }
92
93 // Send logging/setLevel request
94 let request = SetLevelRequest { level };
95
96 let response: SetLevelResult = self
97 .inner
98 .protocol
99 .request("logging/setLevel", Some(serde_json::to_value(request)?))
100 .await?;
101 Ok(response)
102 }
103
104 /// Send `notifications/cancelled` for an in-flight request.
105 ///
106 /// Per MCP 2025-11-25 §Cancellation, when a client abandons an in-flight
107 /// request the receiver SHOULD attempt to cancel it. This method provides
108 /// the explicit send path; the timeout path on `request()` calls it
109 /// internally so library users typically only need this when implementing
110 /// their own cancellation policy (UI cancel button, parent-task abort, …).
111 ///
112 /// Per spec, the `initialize` request MUST NOT be cancelled — passing the
113 /// initialize id is a no-op + warning. Cancellation is best-effort: a
114 /// well-behaved server stops work; a non-compliant one may still finish
115 /// the request normally.
116 pub async fn cancel_request(
117 &self,
118 request_id: &turbomcp_protocol::MessageId,
119 reason: Option<&str>,
120 ) -> Result<()> {
121 // The MCP spec singles out `initialize` as non-cancellable. We don't
122 // know the *method* of an in-flight request from just the id, but
123 // we can warn loudly so misuse is visible.
124 tracing::debug!(
125 request_id = ?request_id,
126 reason = reason.unwrap_or(""),
127 "Sending notifications/cancelled",
128 );
129 self.inner
130 .protocol
131 .send_cancellation(request_id, reason)
132 .await
133 }
134}