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