Skip to main content

turbomcp_client/
lib.rs

1//! # `TurboMCP` Client
2//!
3//! MCP (Model Context Protocol) client implementation for connecting to MCP servers
4//! and consuming their capabilities (tools, prompts, resources, and sampling).
5//!
6//! ## Features
7//!
8//! - Connection management with automatic reconnection
9//! - Error handling and recovery mechanisms
10//! - Support for all MCP capabilities including bidirectional sampling
11//! - Elicitation response handling for server-initiated user input requests
12//! - Transport-agnostic design (works with any `Transport` implementation)
13//! - Type-safe protocol communication
14//! - Request/response correlation tracking
15//! - Timeout and cancellation support
16//! - Automatic capability negotiation
17//! - Handler support for server-initiated requests (sampling and elicitation)
18//!
19//! ## Architecture
20//!
21//! The client follows a layered architecture:
22//!
23//! ```text
24//! Application Layer
25//!        ↓
26//! Client API (this crate)
27//!        ↓  
28//! Protocol Layer (turbomcp-protocol)
29//!        ↓
30//! Transport Layer (turbomcp-transport)
31//! ```
32//!
33//! ## Usage
34//!
35//! ```rust,no_run
36//! use turbomcp_client::{Client, ClientBuilder};
37//! use turbomcp_transport::stdio::StdioTransport;
38//!
39//! # async fn example() -> turbomcp_protocol::Result<()> {
40//! // Create a client with stdio transport
41//! let transport = StdioTransport::new();
42//! let mut client = Client::new(transport);
43//!
44//! // Initialize connection and negotiate capabilities
45//! let result = client.initialize().await?;
46//! println!("Connected to: {}", result.server_info.name);
47//!
48//! // List and call tools
49//! let tools = client.list_tools().await?;
50//! for tool in tools {
51//!     println!("Tool: {} - {}", tool.name, tool.description.as_deref().unwrap_or("No description"));
52//! }
53//!
54//! // Access resources
55//! let resources = client.list_resources().await?;
56//! for resource in resources {
57//!     println!("Resource: {} ({})", resource.name, resource.uri);
58//! }
59//! # Ok(())
60//! # }
61//! ```
62//!
63//! ## Elicitation Response Handling
64//!
65//! The client supports handling server-initiated elicitation requests:
66//!
67//! ```rust,no_run
68//! use turbomcp_client::Client;
69//! use std::collections::HashMap;
70//!
71//! // Simple elicitation handling example
72//! async fn handle_server_elicitation() {
73//!     // When server requests user input, you would:
74//!     // 1. Present the schema to the user
75//!     // 2. Collect their input  
76//!     // 3. Send response back to server
77//!     
78//!     let user_preferences: HashMap<String, String> = HashMap::new();
79//!     // Your UI/CLI interaction logic here
80//!     println!("Server requesting user preferences");
81//! }
82//! ```
83//!
84//! ## Sampling Support
85//!
86//! Handle server-initiated sampling requests for LLM capabilities:
87//!
88//! ```rust,no_run
89//! use turbomcp_client::Client;
90//! use turbomcp_client::sampling::SamplingHandler;
91//! use turbomcp_protocol::types::{ContentBlock, CreateMessageRequest, CreateMessageResult, Role, StopReason, TextContent};
92//! use std::future::Future;
93//! use std::pin::Pin;
94//!
95//! #[derive(Debug)]
96//! struct MySamplingHandler {
97//!     // Your LLM client would go here
98//! }
99//!
100//! impl SamplingHandler for MySamplingHandler {
101//!     fn handle_create_message(
102//!         &self,
103//!         request_id: String,
104//!         request: CreateMessageRequest
105//!     ) -> Pin<Box<dyn Future<Output = Result<CreateMessageResult, Box<dyn std::error::Error + Send + Sync>>> + Send + '_>> {
106//!         Box::pin(async move {
107//!             // Forward to your LLM provider (OpenAI, Anthropic, etc.)
108//!             // Use request_id for correlation tracking
109//!             // Allows the server to request LLM sampling through the client
110//!
111//!             Ok(CreateMessageResult {
112//!                 role: Role::Assistant,
113//!                 content: ContentBlock::Text(
114//!                     TextContent {
115//!                         text: "Response from LLM".to_string(),
116//!                         annotations: None,
117//!                         meta: None,
118//!                     }
119//!                 ),
120//!                 model: "gpt-4".to_string(),
121//!                 stop_reason: Some(StopReason::EndTurn),
122//!                 _meta: None,
123//!             })
124//!         })
125//!     }
126//! }
127//! ```
128//!
129//! ## Error Handling
130//!
131//! The client provides comprehensive error handling with automatic retry logic:
132//!
133//! ```rust,no_run
134//! # use turbomcp_client::Client;
135//! # use turbomcp_transport::stdio::StdioTransport;
136//! # async fn example() -> turbomcp_protocol::Result<()> {
137//! # let mut client = Client::new(StdioTransport::new());
138//! match client.call_tool("my_tool", None, None).await {
139//!     Ok(result) => println!("Tool result: {:?}", result),
140//!     Err(e) => eprintln!("Tool call failed: {}", e),
141//! }
142//! # Ok(())
143//! # }
144//! ```
145
146/// TurboMCP Client version from Cargo.toml
147///
148/// This constant provides easy programmatic access to the current version.
149///
150/// # Example
151///
152/// ```rust
153/// println!("TurboMCP Client version: {}", turbomcp_client::VERSION);
154/// ```
155pub const VERSION: &str = env!("CARGO_PKG_VERSION");
156
157/// TurboMCP Client crate name
158pub const CRATE_NAME: &str = env!("CARGO_PKG_NAME");
159
160pub mod client;
161pub mod handlers;
162pub mod integration;
163pub mod prelude;
164pub mod sampling;
165
166// v3.0 Tower-native middleware
167pub mod middleware;
168
169// Re-export key types for convenience
170pub use client::{ConnectionInfo, ConnectionState, ManagerConfig, ServerGroup, SessionManager};
171
172use std::sync::Arc;
173use std::time::Duration;
174
175// Re-export Transport trait for generic bounds in integrations
176pub use turbomcp_transport::Transport;
177
178// ============================================================================
179// TOP-LEVEL RE-EXPORTS FOR ERGONOMIC IMPORTS
180// ============================================================================
181
182// Result/Error types - re-export from protocol for consistency
183pub use turbomcp_protocol::{Error, Result};
184
185// Handler types (most commonly used)
186pub use handlers::{
187    // Cancellation (current MCP spec)
188    CancellationHandler,
189    CancelledNotification,
190    ElicitationAction,
191    // Elicitation
192    ElicitationHandler,
193    ElicitationRequest,
194    ElicitationResponse,
195    // Error handling
196    HandlerError,
197    HandlerResult,
198    // Logging (current MCP spec)
199    LogHandler,
200    LoggingNotification,
201    PromptListChangedHandler,
202    // List changed handlers (current MCP spec)
203    ResourceListChangedHandler,
204    // Resource updates (current MCP spec)
205    ResourceUpdateHandler,
206    ResourceUpdatedNotification,
207    // Roots
208    RootsHandler,
209    ToolListChangedHandler,
210};
211
212// Sampling types
213pub use sampling::{SamplingHandler, ServerInfo, UserInteractionHandler};
214
215// v3.0 Tower middleware
216pub use middleware::{
217    Cache, CacheConfig, CacheLayer, CacheService, McpRequest, McpResponse, Metrics, MetricsLayer,
218    MetricsService, MetricsSnapshot, TracingLayer, TracingService,
219};
220
221// Common protocol types
222pub use turbomcp_protocol::types::{
223    // Resource content types (for processing embedded resources)
224    BlobResourceContents,
225    // Tool result types (for LLM integrations like rig)
226    CallToolResult,
227    // Core types
228    ContentBlock,
229    EmbeddedResource,
230    LogLevel,
231    Prompt,
232    Resource,
233    ResourceContent,
234    ResourceContents,
235    Role,
236    TextResourceContents,
237    Tool,
238};
239
240// Transport re-exports (with feature gates)
241#[cfg(feature = "stdio")]
242pub use turbomcp_transport::stdio::StdioTransport;
243
244#[cfg(feature = "http")]
245pub use turbomcp_transport::streamable_http_client::{
246    RetryPolicy, StreamableHttpClientConfig, StreamableHttpClientTransport,
247};
248
249#[cfg(feature = "tcp")]
250pub use turbomcp_transport::tcp::{TcpTransport, TcpTransportBuilder};
251
252#[cfg(feature = "unix")]
253pub use turbomcp_transport::unix::{UnixTransport, UnixTransportBuilder};
254
255#[cfg(feature = "websocket")]
256pub use turbomcp_transport::websocket_bidirectional::{
257    WebSocketBidirectionalConfig, WebSocketBidirectionalTransport,
258};
259
260/// Client capability configuration
261///
262/// Defines the capabilities that this client supports when connecting to MCP servers.
263/// These capabilities are sent during the initialization handshake to negotiate
264/// which features will be available during the session.
265///
266/// # Examples
267///
268/// ```
269/// use turbomcp_client::ClientCapabilities;
270///
271/// let capabilities = ClientCapabilities {
272///     tools: true,
273///     prompts: true,
274///     resources: true,
275///     sampling: false,
276///     max_concurrent_handlers: 100,
277/// };
278/// ```
279#[derive(Debug, Clone)]
280pub struct ClientCapabilities {
281    /// Whether the client supports tool calling
282    pub tools: bool,
283
284    /// Whether the client supports prompts
285    pub prompts: bool,
286
287    /// Whether the client supports resources
288    pub resources: bool,
289
290    /// Whether the client supports sampling
291    pub sampling: bool,
292
293    /// Maximum concurrent request/notification handlers (default: 100)
294    ///
295    /// This limits how many server-initiated requests/notifications can be processed simultaneously.
296    /// Provides automatic backpressure when the limit is reached.
297    ///
298    /// **Tuning Guide:**
299    /// - Low-resource clients: 50
300    /// - Standard clients: 100 (default)
301    /// - High-performance: 200-500
302    /// - Maximum recommended: 1000
303    pub max_concurrent_handlers: usize,
304}
305
306impl Default for ClientCapabilities {
307    fn default() -> Self {
308        Self {
309            tools: false,
310            prompts: false,
311            resources: false,
312            sampling: false,
313            max_concurrent_handlers: 100,
314        }
315    }
316}
317
318impl ClientCapabilities {
319    /// All capabilities enabled (tools, prompts, resources, sampling)
320    ///
321    /// This is the most comprehensive configuration, enabling full MCP protocol support.
322    ///
323    /// # Example
324    ///
325    /// ```rust
326    /// use turbomcp_client::ClientCapabilities;
327    ///
328    /// let capabilities = ClientCapabilities::all();
329    /// assert!(capabilities.tools);
330    /// assert!(capabilities.prompts);
331    /// assert!(capabilities.resources);
332    /// assert!(capabilities.sampling);
333    /// ```
334    #[must_use]
335    pub fn all() -> Self {
336        Self {
337            tools: true,
338            prompts: true,
339            resources: true,
340            sampling: true,
341            max_concurrent_handlers: 100,
342        }
343    }
344
345    /// Core capabilities without sampling (tools, prompts, resources)
346    ///
347    /// This is the recommended default for most applications. It enables
348    /// all standard MCP features except server-initiated sampling requests.
349    ///
350    /// # Example
351    ///
352    /// ```rust
353    /// use turbomcp_client::ClientCapabilities;
354    ///
355    /// let capabilities = ClientCapabilities::core();
356    /// assert!(capabilities.tools);
357    /// assert!(capabilities.prompts);
358    /// assert!(capabilities.resources);
359    /// assert!(!capabilities.sampling);
360    /// ```
361    #[must_use]
362    pub fn core() -> Self {
363        Self {
364            tools: true,
365            prompts: true,
366            resources: true,
367            sampling: false,
368            max_concurrent_handlers: 100,
369        }
370    }
371
372    /// Minimal capabilities (tools only)
373    ///
374    /// Use this for simple tool-calling clients that don't need prompts,
375    /// resources, or sampling support.
376    ///
377    /// # Example
378    ///
379    /// ```rust
380    /// use turbomcp_client::ClientCapabilities;
381    ///
382    /// let capabilities = ClientCapabilities::minimal();
383    /// assert!(capabilities.tools);
384    /// assert!(!capabilities.prompts);
385    /// assert!(!capabilities.resources);
386    /// assert!(!capabilities.sampling);
387    /// ```
388    #[must_use]
389    pub fn minimal() -> Self {
390        Self {
391            tools: true,
392            prompts: false,
393            resources: false,
394            sampling: false,
395            max_concurrent_handlers: 100,
396        }
397    }
398
399    /// Only tools enabled
400    ///
401    /// Same as `minimal()`, provided for clarity.
402    #[must_use]
403    pub fn only_tools() -> Self {
404        Self::minimal()
405    }
406
407    /// Only resources enabled
408    ///
409    /// Use this for resource-focused clients that don't need tools or prompts.
410    ///
411    /// # Example
412    ///
413    /// ```rust
414    /// use turbomcp_client::ClientCapabilities;
415    ///
416    /// let capabilities = ClientCapabilities::only_resources();
417    /// assert!(!capabilities.tools);
418    /// assert!(!capabilities.prompts);
419    /// assert!(capabilities.resources);
420    /// ```
421    #[must_use]
422    pub fn only_resources() -> Self {
423        Self {
424            tools: false,
425            prompts: false,
426            resources: true,
427            sampling: false,
428            max_concurrent_handlers: 100,
429        }
430    }
431
432    /// Only prompts enabled
433    ///
434    /// Use this for prompt-focused clients that don't need tools or resources.
435    ///
436    /// # Example
437    ///
438    /// ```rust
439    /// use turbomcp_client::ClientCapabilities;
440    ///
441    /// let capabilities = ClientCapabilities::only_prompts();
442    /// assert!(!capabilities.tools);
443    /// assert!(capabilities.prompts);
444    /// assert!(!capabilities.resources);
445    /// ```
446    #[must_use]
447    pub fn only_prompts() -> Self {
448        Self {
449            tools: false,
450            prompts: true,
451            resources: false,
452            sampling: false,
453            max_concurrent_handlers: 100,
454        }
455    }
456
457    /// Only sampling enabled
458    ///
459    /// Use this for clients that exclusively handle server-initiated sampling requests.
460    #[must_use]
461    pub fn only_sampling() -> Self {
462        Self {
463            tools: false,
464            prompts: false,
465            resources: false,
466            sampling: true,
467            max_concurrent_handlers: 100,
468        }
469    }
470}
471
472/// JSON-RPC protocol handler for MCP communication
473// Note: ProtocolClient implementation moved to client/protocol.rs for better modularity
474/// MCP client for communicating with servers
475///
476/// The `Client` struct provides an ergonomic interface for interacting with MCP servers.
477/// It handles protocol complexity internally, exposing clean, type-safe methods.
478///
479/// # Type Parameters
480///
481/// * `T` - The transport implementation used for communication
482///
483/// # Examples
484///
485/// ```rust,no_run
486/// use turbomcp_client::Client;
487/// use turbomcp_transport::stdio::StdioTransport;
488///
489/// # async fn example() -> turbomcp_protocol::Result<()> {
490/// let transport = StdioTransport::new();
491/// let mut client = Client::new(transport);
492///
493/// // Initialize and start using the client
494/// client.initialize().await?;
495/// # Ok(())
496/// # }
497/// ```
498// Re-export Client from the core module
499pub use client::core::Client;
500
501// Thread-safe wrapper for sharing Client across async tasks
502//
503// This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
504// for concurrent access to MCP client functionality. It addresses the limitations
505// identified in PR feedback where Client requires `&mut self` for all operations
506// but needs to be shared across multiple async tasks.
507//
508// # Design Rationale
509//
510// All Client methods require `&mut self` because:
511// - MCP connections maintain state (initialized flag, connection status)
512// - Request correlation tracking for JSON-RPC requires mutation
513// - Handler and plugin registries need mutable access
514//
515// Note: SharedClient has been removed in v2 - Client is now directly cloneable via Arc
516
517// ----------------------------------------------------------------------------
518// Re-exports
519// ----------------------------------------------------------------------------
520
521#[doc = "Result of client initialization"]
522#[doc = ""]
523#[doc = "Contains information about the server and the negotiated capabilities"]
524#[doc = "after a successful initialization handshake."]
525pub use client::config::InitializeResult;
526
527// ServerCapabilities is now imported from turbomcp_protocol::types
528
529/// Connection configuration for the client
530#[derive(Debug, Clone)]
531pub struct ConnectionConfig {
532    /// Request timeout in milliseconds
533    pub timeout_ms: u64,
534
535    /// Maximum number of retry attempts
536    pub max_retries: u32,
537
538    /// Retry delay in milliseconds
539    pub retry_delay_ms: u64,
540
541    /// Keep-alive interval in milliseconds
542    pub keepalive_ms: u64,
543}
544
545fn protocol_transport_config(
546    connection_config: &ConnectionConfig,
547) -> turbomcp_transport::TransportConfig {
548    let timeout = Duration::from_millis(connection_config.timeout_ms);
549
550    turbomcp_transport::TransportConfig {
551        connect_timeout: timeout,
552        keep_alive: Some(Duration::from_millis(connection_config.keepalive_ms)),
553        timeouts: turbomcp_transport::config::TimeoutConfig {
554            connect: timeout,
555            request: Some(timeout),
556            total: Some(timeout),
557            read: Some(timeout),
558        },
559        ..Default::default()
560    }
561}
562
563fn resilience_requested(builder: &ClientBuilder) -> bool {
564    builder.enable_resilience
565        || builder.retry_config.is_some()
566        || builder.circuit_breaker_config.is_some()
567        || builder.health_check_config.is_some()
568}
569
570impl Default for ConnectionConfig {
571    fn default() -> Self {
572        Self {
573            timeout_ms: 30_000,    // 30 seconds
574            max_retries: 3,        // 3 attempts
575            retry_delay_ms: 1_000, // 1 second
576            keepalive_ms: 60_000,  // 60 seconds
577        }
578    }
579}
580
581/// Builder for configuring and creating MCP clients
582///
583/// Provides a fluent interface for configuring client options before creation.
584/// The enhanced builder pattern supports comprehensive configuration including:
585/// - Protocol capabilities
586/// - Plugin registration
587/// - Handler registration
588/// - Connection settings
589/// - Resilience configuration
590///
591/// # Examples
592///
593/// Basic usage:
594/// ```rust,no_run
595/// use turbomcp_client::ClientBuilder;
596/// use turbomcp_transport::stdio::StdioTransport;
597///
598/// # async fn example() -> turbomcp_protocol::Result<()> {
599/// let client = ClientBuilder::new()
600///     .with_tools(true)
601///     .with_prompts(true)
602///     .with_resources(false)
603///     .build(StdioTransport::new());
604/// # Ok(())
605/// # }
606/// ```
607///
608/// Advanced configuration with Tower middleware:
609/// ```rust,no_run
610/// use turbomcp_client::{ClientBuilder, ConnectionConfig};
611/// use turbomcp_client::middleware::MetricsLayer;
612/// use turbomcp_transport::stdio::StdioTransport;
613/// use tower::ServiceBuilder;
614///
615/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
616/// let client = ClientBuilder::new()
617///     .with_tools(true)
618///     .with_prompts(true)
619///     .with_resources(true)
620///     .with_sampling(true)
621///     .with_connection_config(ConnectionConfig {
622///         timeout_ms: 60_000,
623///         max_retries: 5,
624///         retry_delay_ms: 2_000,
625///         keepalive_ms: 30_000,
626///     })
627///     .build(StdioTransport::new())
628///     .await?;
629/// # Ok(())
630/// # }
631/// ```
632#[derive(Debug, Default)]
633pub struct ClientBuilder {
634    capabilities: ClientCapabilities,
635    connection_config: ConnectionConfig,
636    elicitation_handler: Option<Arc<dyn crate::handlers::ElicitationHandler>>,
637    log_handler: Option<Arc<dyn crate::handlers::LogHandler>>,
638    resource_update_handler: Option<Arc<dyn crate::handlers::ResourceUpdateHandler>>,
639    // Robustness configuration
640    enable_resilience: bool,
641    retry_config: Option<turbomcp_transport::resilience::RetryConfig>,
642    circuit_breaker_config: Option<turbomcp_transport::resilience::CircuitBreakerConfig>,
643    health_check_config: Option<turbomcp_transport::resilience::HealthCheckConfig>,
644}
645
646// Default implementation is now derived
647
648impl ClientBuilder {
649    /// Create a new client builder
650    ///
651    /// Returns a new builder with default configuration.
652    #[must_use]
653    pub fn new() -> Self {
654        Self::default()
655    }
656
657    // ============================================================================
658    // CAPABILITY CONFIGURATION
659    // ============================================================================
660
661    /// Enable or disable tool support
662    ///
663    /// # Arguments
664    ///
665    /// * `enabled` - Whether to enable tool support
666    #[must_use]
667    pub fn with_tools(mut self, enabled: bool) -> Self {
668        self.capabilities.tools = enabled;
669        self
670    }
671
672    /// Enable or disable prompt support
673    ///
674    /// # Arguments
675    ///
676    /// * `enabled` - Whether to enable prompt support
677    #[must_use]
678    pub fn with_prompts(mut self, enabled: bool) -> Self {
679        self.capabilities.prompts = enabled;
680        self
681    }
682
683    /// Enable or disable resource support
684    ///
685    /// # Arguments
686    ///
687    /// * `enabled` - Whether to enable resource support
688    #[must_use]
689    pub fn with_resources(mut self, enabled: bool) -> Self {
690        self.capabilities.resources = enabled;
691        self
692    }
693
694    /// Enable or disable sampling support
695    ///
696    /// # Arguments
697    ///
698    /// * `enabled` - Whether to enable sampling support
699    #[must_use]
700    pub fn with_sampling(mut self, enabled: bool) -> Self {
701        self.capabilities.sampling = enabled;
702        self
703    }
704
705    /// Set maximum concurrent request/notification handlers
706    ///
707    /// This limits how many server-initiated requests/notifications can be processed simultaneously.
708    /// Provides automatic backpressure when the limit is reached.
709    ///
710    /// # Arguments
711    ///
712    /// * `limit` - Maximum concurrent handlers (default: 100)
713    ///
714    /// # Tuning Guide
715    ///
716    /// - Low-resource clients: 50
717    /// - Standard clients: 100 (default)
718    /// - High-performance: 200-500
719    /// - Maximum recommended: 1000
720    ///
721    /// # Example
722    ///
723    /// ```rust,no_run
724    /// use turbomcp_client::ClientBuilder;
725    /// # use turbomcp_transport::StdioTransport;
726    ///
727    /// let builder = ClientBuilder::new()
728    ///     .with_max_concurrent_handlers(200);
729    /// ```
730    #[must_use]
731    pub fn with_max_concurrent_handlers(mut self, limit: usize) -> Self {
732        self.capabilities.max_concurrent_handlers = limit;
733        self
734    }
735
736    /// Configure all capabilities at once
737    ///
738    /// # Arguments
739    ///
740    /// * `capabilities` - The capabilities configuration
741    #[must_use]
742    pub fn with_capabilities(mut self, capabilities: ClientCapabilities) -> Self {
743        self.capabilities = capabilities;
744        self
745    }
746
747    // ============================================================================
748    // CONNECTION CONFIGURATION
749    // ============================================================================
750
751    /// Configure connection settings
752    ///
753    /// # Arguments
754    ///
755    /// * `config` - The connection configuration
756    #[must_use]
757    pub fn with_connection_config(mut self, config: ConnectionConfig) -> Self {
758        self.connection_config = config;
759        self
760    }
761
762    /// Set request timeout
763    ///
764    /// # Arguments
765    ///
766    /// * `timeout_ms` - Timeout in milliseconds
767    #[must_use]
768    pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
769        self.connection_config.timeout_ms = timeout_ms;
770        self
771    }
772
773    /// Set maximum retry attempts
774    ///
775    /// # Arguments
776    ///
777    /// * `max_retries` - Maximum number of retries
778    #[must_use]
779    pub fn with_max_retries(mut self, max_retries: u32) -> Self {
780        self.connection_config.max_retries = max_retries;
781        self
782    }
783
784    /// Set retry delay
785    ///
786    /// # Arguments
787    ///
788    /// * `delay_ms` - Retry delay in milliseconds
789    #[must_use]
790    pub fn with_retry_delay(mut self, delay_ms: u64) -> Self {
791        self.connection_config.retry_delay_ms = delay_ms;
792        self
793    }
794
795    /// Set keep-alive interval
796    ///
797    /// # Arguments
798    ///
799    /// * `interval_ms` - Keep-alive interval in milliseconds
800    #[must_use]
801    pub fn with_keepalive(mut self, interval_ms: u64) -> Self {
802        self.connection_config.keepalive_ms = interval_ms;
803        self
804    }
805
806    // ============================================================================
807    // ROBUSTNESS & RESILIENCE CONFIGURATION
808    // ============================================================================
809
810    /// Enable resilient transport with circuit breaker, retry, and health checking
811    ///
812    /// When enabled, the transport layer will automatically:
813    /// - Retry failed operations with exponential backoff
814    /// - Use circuit breaker pattern to prevent cascade failures
815    /// - Perform periodic health checks
816    /// - Deduplicate messages
817    ///
818    /// # Examples
819    ///
820    /// ```rust,no_run
821    /// use turbomcp_client::ClientBuilder;
822    /// use turbomcp_transport::stdio::StdioTransport;
823    ///
824    /// let client = ClientBuilder::new()
825    ///     .enable_resilience()
826    ///     .build(StdioTransport::new());
827    /// ```
828    #[must_use]
829    pub fn enable_resilience(mut self) -> Self {
830        self.enable_resilience = true;
831        self
832    }
833
834    /// Configure retry behavior for resilient transport
835    ///
836    /// # Arguments
837    ///
838    /// * `config` - Retry configuration
839    ///
840    /// # Examples
841    ///
842    /// ```rust,no_run
843    /// use turbomcp_client::ClientBuilder;
844    /// use turbomcp_transport::resilience::RetryConfig;
845    /// use turbomcp_transport::stdio::StdioTransport;
846    /// use std::time::Duration;
847    ///
848    /// let client = ClientBuilder::new()
849    ///     .enable_resilience()
850    ///     .with_retry_config(RetryConfig {
851    ///         max_attempts: 5,
852    ///         base_delay: Duration::from_millis(100),
853    ///         max_delay: Duration::from_secs(30),
854    ///         backoff_multiplier: 2.0,
855    ///         jitter_factor: 0.1,
856    ///         retry_on_connection_error: true,
857    ///         retry_on_timeout: true,
858    ///         custom_retry_conditions: Vec::new(),
859    ///     })
860    ///     .build(StdioTransport::new());
861    /// ```
862    #[must_use]
863    pub fn with_retry_config(
864        mut self,
865        config: turbomcp_transport::resilience::RetryConfig,
866    ) -> Self {
867        self.retry_config = Some(config);
868        self.enable_resilience = true; // Auto-enable resilience
869        self
870    }
871
872    /// Configure circuit breaker for resilient transport
873    ///
874    /// # Arguments
875    ///
876    /// * `config` - Circuit breaker configuration
877    ///
878    /// # Examples
879    ///
880    /// ```rust,no_run
881    /// use turbomcp_client::ClientBuilder;
882    /// use turbomcp_transport::resilience::CircuitBreakerConfig;
883    /// use turbomcp_transport::stdio::StdioTransport;
884    /// use std::time::Duration;
885    ///
886    /// let client = ClientBuilder::new()
887    ///     .enable_resilience()
888    ///     .with_circuit_breaker_config(CircuitBreakerConfig {
889    ///         failure_threshold: 5,
890    ///         success_threshold: 2,
891    ///         timeout: Duration::from_secs(60),
892    ///         rolling_window_size: 100,
893    ///         minimum_requests: 10,
894    ///     })
895    ///     .build(StdioTransport::new());
896    /// ```
897    #[must_use]
898    pub fn with_circuit_breaker_config(
899        mut self,
900        config: turbomcp_transport::resilience::CircuitBreakerConfig,
901    ) -> Self {
902        self.circuit_breaker_config = Some(config);
903        self.enable_resilience = true; // Auto-enable resilience
904        self
905    }
906
907    /// Configure health checking for resilient transport
908    ///
909    /// # Arguments
910    ///
911    /// * `config` - Health check configuration
912    ///
913    /// # Examples
914    ///
915    /// ```rust,no_run
916    /// use turbomcp_client::ClientBuilder;
917    /// use turbomcp_transport::resilience::HealthCheckConfig;
918    /// use turbomcp_transport::stdio::StdioTransport;
919    /// use std::time::Duration;
920    ///
921    /// let client = ClientBuilder::new()
922    ///     .enable_resilience()
923    ///     .with_health_check_config(HealthCheckConfig {
924    ///         interval: Duration::from_secs(30),
925    ///         timeout: Duration::from_secs(5),
926    ///         failure_threshold: 3,
927    ///         success_threshold: 1,
928    ///         custom_check: None,
929    ///     })
930    ///     .build(StdioTransport::new());
931    /// ```
932    #[must_use]
933    pub fn with_health_check_config(
934        mut self,
935        config: turbomcp_transport::resilience::HealthCheckConfig,
936    ) -> Self {
937        self.health_check_config = Some(config);
938        self.enable_resilience = true; // Auto-enable resilience
939        self
940    }
941
942    // ============================================================================
943    // HANDLER REGISTRATION
944    // ============================================================================
945
946    /// Register an elicitation handler for processing user input requests
947    ///
948    /// # Arguments
949    ///
950    /// * `handler` - The elicitation handler implementation
951    pub fn with_elicitation_handler(
952        mut self,
953        handler: Arc<dyn crate::handlers::ElicitationHandler>,
954    ) -> Self {
955        self.elicitation_handler = Some(handler);
956        self
957    }
958
959    /// Register a log handler for processing server log messages
960    ///
961    /// # Arguments
962    ///
963    /// * `handler` - The log handler implementation
964    pub fn with_log_handler(mut self, handler: Arc<dyn crate::handlers::LogHandler>) -> Self {
965        self.log_handler = Some(handler);
966        self
967    }
968
969    /// Register a resource update handler for processing resource change notifications
970    ///
971    /// # Arguments
972    ///
973    /// * `handler` - The resource update handler implementation
974    pub fn with_resource_update_handler(
975        mut self,
976        handler: Arc<dyn crate::handlers::ResourceUpdateHandler>,
977    ) -> Self {
978        self.resource_update_handler = Some(handler);
979        self
980    }
981
982    // ============================================================================
983    // BUILD METHODS
984    // ============================================================================
985
986    /// Build a client with the configured options
987    ///
988    /// Creates a new client instance with all the configured options. The client
989    /// will be initialized with the registered plugins, handlers, and providers.
990    ///
991    /// # Arguments
992    ///
993    /// * `transport` - The transport to use for the client
994    ///
995    /// # Returns
996    ///
997    /// Returns a configured `Client` instance wrapped in a Result for async setup.
998    ///
999    /// # Examples
1000    ///
1001    /// ```rust,no_run
1002    /// use turbomcp_client::ClientBuilder;
1003    /// use turbomcp_transport::stdio::StdioTransport;
1004    ///
1005    /// # async fn example() -> turbomcp_protocol::Result<()> {
1006    /// let client = ClientBuilder::new()
1007    ///     .with_tools(true)
1008    ///     .with_prompts(true)
1009    ///     .build(StdioTransport::new())
1010    ///     .await?;
1011    /// # Ok(())
1012    /// # }
1013    /// ```
1014    pub async fn build<T: Transport + 'static>(self, transport: T) -> Result<Client<T>> {
1015        if resilience_requested(&self) {
1016            return Err(Error::configuration(
1017                "resilience settings require build_resilient(); build() would otherwise ignore them"
1018                    .to_string(),
1019            ));
1020        }
1021
1022        // Create base client with capabilities
1023        let client = Client::with_capabilities_and_config(
1024            transport,
1025            self.capabilities,
1026            protocol_transport_config(&self.connection_config),
1027        );
1028
1029        // Register handlers
1030        if let Some(handler) = self.elicitation_handler {
1031            client.set_elicitation_handler(handler);
1032        }
1033        if let Some(handler) = self.log_handler {
1034            client.set_log_handler(handler);
1035        }
1036        if let Some(handler) = self.resource_update_handler {
1037            client.set_resource_update_handler(handler);
1038        }
1039
1040        Ok(client)
1041    }
1042
1043    /// Build a client with resilient transport (circuit breaker, retry, health checking)
1044    ///
1045    /// When resilience features are enabled via `enable_resilience()` or any resilience
1046    /// configuration method, this wraps the transport in a `TurboTransport` that provides:
1047    /// - Automatic retry with exponential backoff
1048    /// - Circuit breaker pattern for fast failure
1049    /// - Health checking and monitoring
1050    /// - Message deduplication
1051    ///
1052    /// # Arguments
1053    ///
1054    /// * `transport` - The base transport to wrap with resilience features
1055    ///
1056    /// # Returns
1057    ///
1058    /// Returns a configured `Client<TurboTransport>` instance.
1059    ///
1060    /// # Errors
1061    ///
1062    /// Returns an error if plugin initialization fails.
1063    ///
1064    /// # Examples
1065    ///
1066    /// ```rust,no_run
1067    /// use turbomcp_client::ClientBuilder;
1068    /// use turbomcp_transport::stdio::StdioTransport;
1069    /// use turbomcp_transport::resilience::{RetryConfig, CircuitBreakerConfig, HealthCheckConfig};
1070    /// use std::time::Duration;
1071    ///
1072    /// # async fn example() -> turbomcp_protocol::Result<()> {
1073    /// let client = ClientBuilder::new()
1074    ///     .with_retry_config(RetryConfig {
1075    ///         max_attempts: 5,
1076    ///         base_delay: Duration::from_millis(200),
1077    ///         ..Default::default()
1078    ///     })
1079    ///     .with_circuit_breaker_config(CircuitBreakerConfig {
1080    ///         failure_threshold: 3,
1081    ///         timeout: Duration::from_secs(30),
1082    ///         ..Default::default()
1083    ///     })
1084    ///     .with_health_check_config(HealthCheckConfig {
1085    ///         interval: Duration::from_secs(15),
1086    ///         timeout: Duration::from_secs(5),
1087    ///         ..Default::default()
1088    ///     })
1089    ///     .build_resilient(StdioTransport::new())
1090    ///     .await?;
1091    /// # Ok(())
1092    /// # }
1093    /// ```
1094    pub async fn build_resilient<T: Transport + 'static>(
1095        self,
1096        transport: T,
1097    ) -> Result<Client<turbomcp_transport::resilience::TurboTransport>> {
1098        use turbomcp_transport::resilience::TurboTransport;
1099
1100        // Get configurations or use defaults
1101        let retry_config =
1102            self.retry_config
1103                .unwrap_or_else(|| turbomcp_transport::resilience::RetryConfig {
1104                    max_attempts: self.connection_config.max_retries.max(1),
1105                    base_delay: Duration::from_millis(self.connection_config.retry_delay_ms),
1106                    ..Default::default()
1107                });
1108        let circuit_config = self.circuit_breaker_config.unwrap_or_default();
1109        let health_config = self.health_check_config.unwrap_or_else(|| {
1110            turbomcp_transport::resilience::HealthCheckConfig {
1111                timeout: Duration::from_millis(self.connection_config.timeout_ms),
1112                ..Default::default()
1113            }
1114        });
1115
1116        // Wrap transport in TurboTransport
1117        let robust_transport = TurboTransport::new(
1118            Box::new(transport),
1119            retry_config,
1120            circuit_config,
1121            health_config,
1122        );
1123
1124        // Create client with resilient transport
1125        let client = Client::with_capabilities_and_config(
1126            robust_transport,
1127            self.capabilities,
1128            protocol_transport_config(&self.connection_config),
1129        );
1130
1131        // Register handlers
1132        if let Some(handler) = self.elicitation_handler {
1133            client.set_elicitation_handler(handler);
1134        }
1135        if let Some(handler) = self.log_handler {
1136            client.set_log_handler(handler);
1137        }
1138        if let Some(handler) = self.resource_update_handler {
1139            client.set_resource_update_handler(handler);
1140        }
1141
1142        Ok(client)
1143    }
1144
1145    /// Build a client synchronously with basic configuration only
1146    ///
1147    /// This is a convenience method for simple use cases.
1148    ///
1149    /// # Arguments
1150    ///
1151    /// * `transport` - The transport to use for the client
1152    ///
1153    /// # Returns
1154    ///
1155    /// Returns a configured `Client` instance.
1156    ///
1157    /// # Examples
1158    ///
1159    /// ```rust,no_run
1160    /// use turbomcp_client::ClientBuilder;
1161    /// use turbomcp_transport::stdio::StdioTransport;
1162    ///
1163    /// let client = ClientBuilder::new()
1164    ///     .with_tools(true)
1165    ///     .build_sync(StdioTransport::new());
1166    /// ```
1167    pub fn build_sync<T: Transport + 'static>(self, transport: T) -> Client<T> {
1168        assert!(
1169            !resilience_requested(&self),
1170            "resilience settings require build_resilient(); build_sync() would otherwise ignore them"
1171        );
1172
1173        let client = Client::with_capabilities_and_config(
1174            transport,
1175            self.capabilities,
1176            protocol_transport_config(&self.connection_config),
1177        );
1178
1179        // Register synchronous handlers only
1180        if let Some(handler) = self.elicitation_handler {
1181            client.set_elicitation_handler(handler);
1182        }
1183        if let Some(handler) = self.log_handler {
1184            client.set_log_handler(handler);
1185        }
1186        if let Some(handler) = self.resource_update_handler {
1187            client.set_resource_update_handler(handler);
1188        }
1189
1190        client
1191    }
1192
1193    // ============================================================================
1194    // CONFIGURATION ACCESS
1195    // ============================================================================
1196
1197    /// Get the current capabilities configuration
1198    #[must_use]
1199    pub fn capabilities(&self) -> &ClientCapabilities {
1200        &self.capabilities
1201    }
1202
1203    /// Get the current connection configuration
1204    #[must_use]
1205    pub fn connection_config(&self) -> &ConnectionConfig {
1206        &self.connection_config
1207    }
1208
1209    /// Check if any handlers are registered
1210    #[must_use]
1211    pub fn has_handlers(&self) -> bool {
1212        self.elicitation_handler.is_some()
1213            || self.log_handler.is_some()
1214            || self.resource_update_handler.is_some()
1215    }
1216}
1217
1218// Re-export types for public API
1219pub use turbomcp_protocol::types::ServerCapabilities as PublicServerCapabilities;
1220
1221#[cfg(test)]
1222mod tests {
1223    use super::*;
1224    use std::future::Future;
1225    use std::pin::Pin;
1226    use turbomcp_transport::{
1227        TransportCapabilities, TransportConfig, TransportMessage, TransportMetrics,
1228        TransportResult, TransportState, TransportType,
1229    };
1230
1231    #[derive(Debug, Default)]
1232    struct NoopTransport {
1233        capabilities: TransportCapabilities,
1234    }
1235
1236    impl Transport for NoopTransport {
1237        fn transport_type(&self) -> TransportType {
1238            TransportType::Stdio
1239        }
1240
1241        fn capabilities(&self) -> &TransportCapabilities {
1242            &self.capabilities
1243        }
1244
1245        fn state(&self) -> Pin<Box<dyn Future<Output = TransportState> + Send + '_>> {
1246            Box::pin(async { TransportState::Disconnected })
1247        }
1248
1249        fn connect(&self) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1250            Box::pin(async { Ok(()) })
1251        }
1252
1253        fn disconnect(&self) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1254            Box::pin(async { Ok(()) })
1255        }
1256
1257        fn send(
1258            &self,
1259            _message: TransportMessage,
1260        ) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1261            Box::pin(async { Ok(()) })
1262        }
1263
1264        fn receive(
1265            &self,
1266        ) -> Pin<Box<dyn Future<Output = TransportResult<Option<TransportMessage>>> + Send + '_>>
1267        {
1268            Box::pin(async { Ok(None) })
1269        }
1270
1271        fn metrics(&self) -> Pin<Box<dyn Future<Output = TransportMetrics> + Send + '_>> {
1272            Box::pin(async { TransportMetrics::default() })
1273        }
1274
1275        fn configure(
1276            &self,
1277            _config: TransportConfig,
1278        ) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1279            Box::pin(async { Ok(()) })
1280        }
1281    }
1282
1283    #[tokio::test]
1284    async fn build_rejects_resilience_flags() {
1285        let result = ClientBuilder::new()
1286            .enable_resilience()
1287            .build(NoopTransport::default())
1288            .await;
1289
1290        assert!(result.is_err());
1291        let err = match result {
1292            Ok(_) => panic!("expected build() to reject resilience settings"),
1293            Err(err) => err,
1294        };
1295        assert!(err.to_string().contains("build_resilient"));
1296    }
1297}