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);
58//! }
59//! # Ok(())
60//! # }
61//! ```
62//!
63//! ## Elicitation Response Handling (New in 1.0.3)
64//!
65//! The client now 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 (New in 1.0.3)
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::{CreateMessageRequest, CreateMessageResult, Role, Content, StopReason, TextContent};
92//! use async_trait::async_trait;
93//!
94//! #[derive(Debug)]
95//! struct MySamplingHandler {
96//!     // Your LLM client would go here
97//! }
98//!
99//! #[async_trait]
100//! impl SamplingHandler for MySamplingHandler {
101//!     async fn handle_create_message(
102//!         &self,
103//!         request: CreateMessageRequest
104//!     ) -> Result<CreateMessageResult, Box<dyn std::error::Error + Send + Sync>> {
105//!         // Forward to your LLM provider (OpenAI, Anthropic, etc.)
106//!         // Allows the server to request LLM sampling through the client
107//!         
108//!         Ok(CreateMessageResult {
109//!             role: Role::Assistant,
110//!             content: Content::Text(
111//!                 TextContent {
112//!                     text: "Response from LLM".to_string(),
113//!                     annotations: None,
114//!                     meta: None,
115//!                 }
116//!             ),
117//!              model: "gpt-4".to_string(),
118//!             stop_reason: Some(StopReason::EndTurn),
119//!             _meta: None,
120//!         })
121//!     }
122//! }
123//! ```
124//!
125//! ## Error Handling
126//!
127//! The client provides comprehensive error handling with automatic retry logic:
128//!
129//! ```rust,no_run
130//! # use turbomcp_client::Client;
131//! # use turbomcp_transport::stdio::StdioTransport;
132//! # async fn example() -> turbomcp_protocol::Result<()> {
133//! # let mut client = Client::new(StdioTransport::new());
134//! match client.call_tool("my_tool", None).await {
135//!     Ok(result) => println!("Tool result: {:?}", result),
136//!     Err(e) => eprintln!("Tool call failed: {}", e),
137//! }
138//! # Ok(())
139//! # }
140//! ```
141
142pub mod client;
143pub mod handlers;
144pub mod plugins;
145pub mod sampling;
146
147// Re-export key types for convenience
148pub use client::{ConnectionInfo, ConnectionState, ManagerConfig, ServerGroup, SessionManager};
149
150use std::sync::Arc;
151
152use turbomcp_protocol::{Error, Result};
153use turbomcp_transport::Transport;
154
155// Note: Handler types are now used only in client/operations modules
156
157/// Client capability configuration
158///
159/// Defines the capabilities that this client supports when connecting to MCP servers.
160/// These capabilities are sent during the initialization handshake to negotiate
161/// which features will be available during the session.
162///
163/// # Examples
164///
165/// ```
166/// use turbomcp_client::ClientCapabilities;
167///
168/// let capabilities = ClientCapabilities {
169///     tools: true,
170///     prompts: true,
171///     resources: true,
172///     sampling: false,
173/// };
174/// ```
175#[derive(Debug, Clone, Default)]
176pub struct ClientCapabilities {
177    /// Whether the client supports tool calling
178    pub tools: bool,
179
180    /// Whether the client supports prompts
181    pub prompts: bool,
182
183    /// Whether the client supports resources
184    pub resources: bool,
185
186    /// Whether the client supports sampling
187    pub sampling: bool,
188}
189
190/// JSON-RPC protocol handler for MCP communication
191// Note: ProtocolClient implementation moved to client/protocol.rs for better modularity
192/// MCP client for communicating with servers
193///
194/// The `Client` struct provides a beautiful, ergonomic interface for interacting with MCP servers.
195/// It handles all protocol complexity internally, exposing only clean, type-safe methods.
196///
197/// # Type Parameters
198///
199/// * `T` - The transport implementation used for communication
200///
201/// # Examples
202///
203/// ```rust,no_run
204/// use turbomcp_client::Client;
205/// use turbomcp_transport::stdio::StdioTransport;
206///
207/// # async fn example() -> turbomcp_protocol::Result<()> {
208/// let transport = StdioTransport::new();
209/// let mut client = Client::new(transport);
210///
211/// // Initialize and start using the client
212/// client.initialize().await?;
213/// # Ok(())
214/// # }
215/// ```
216// Re-export Client from the core module
217pub use client::core::Client;
218
219// Thread-safe wrapper for sharing Client across async tasks
220//
221// This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
222// for concurrent access to MCP client functionality. It addresses the limitations
223// identified in PR feedback where Client requires `&mut self` for all operations
224// but needs to be shared across multiple async tasks.
225//
226// # Design Rationale
227//
228// All Client methods require `&mut self` because:
229// - MCP connections maintain state (initialized flag, connection status)
230// - Request correlation tracking for JSON-RPC requires mutation
231// - Handler and plugin registries need mutable access
232//
233// Note: SharedClient has been removed in v2 - Client is now directly cloneable via Arc
234
235// ----------------------------------------------------------------------------
236// Re-exports
237// ----------------------------------------------------------------------------
238
239#[doc = "Result of client initialization"]
240#[doc = ""]
241#[doc = "Contains information about the server and the negotiated capabilities"]
242#[doc = "after a successful initialization handshake."]
243pub use client::config::InitializeResult;
244
245// ServerCapabilities is now imported from turbomcp_protocol::types
246
247/// Connection configuration for the client
248#[derive(Debug, Clone)]
249pub struct ConnectionConfig {
250    /// Request timeout in milliseconds
251    pub timeout_ms: u64,
252
253    /// Maximum number of retry attempts
254    pub max_retries: u32,
255
256    /// Retry delay in milliseconds
257    pub retry_delay_ms: u64,
258
259    /// Keep-alive interval in milliseconds
260    pub keepalive_ms: u64,
261}
262
263impl Default for ConnectionConfig {
264    fn default() -> Self {
265        Self {
266            timeout_ms: 30_000,    // 30 seconds
267            max_retries: 3,        // 3 attempts
268            retry_delay_ms: 1_000, // 1 second
269            keepalive_ms: 60_000,  // 60 seconds
270        }
271    }
272}
273
274/// Builder for configuring and creating MCP clients
275///
276/// Provides a fluent interface for configuring client options before creation.
277/// The enhanced builder pattern supports comprehensive configuration including:
278/// - Protocol capabilities
279/// - Plugin registration
280/// - Handler registration
281/// - Connection settings
282/// - Resilience configuration
283///
284/// # Examples
285///
286/// Basic usage:
287/// ```rust,no_run
288/// use turbomcp_client::ClientBuilder;
289/// use turbomcp_transport::stdio::StdioTransport;
290///
291/// # async fn example() -> turbomcp_protocol::Result<()> {
292/// let client = ClientBuilder::new()
293///     .with_tools(true)
294///     .with_prompts(true)
295///     .with_resources(false)
296///     .build(StdioTransport::new());
297/// # Ok(())
298/// # }
299/// ```
300///
301/// Advanced configuration:
302/// ```rust,no_run
303/// use turbomcp_client::{ClientBuilder, ConnectionConfig};
304/// use turbomcp_client::plugins::{MetricsPlugin, PluginConfig};
305/// use turbomcp_transport::stdio::StdioTransport;
306/// use std::sync::Arc;
307///
308/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
309/// let client = ClientBuilder::new()
310///     .with_tools(true)
311///     .with_prompts(true)
312///     .with_resources(true)
313///     .with_sampling(true)
314///     .with_connection_config(ConnectionConfig {
315///         timeout_ms: 60_000,
316///         max_retries: 5,
317///         retry_delay_ms: 2_000,
318///         keepalive_ms: 30_000,
319///     })
320///     .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
321///     .build(StdioTransport::new())
322///     .await?;
323/// # Ok(())
324/// # }
325/// ```
326#[derive(Debug, Default)]
327pub struct ClientBuilder {
328    capabilities: ClientCapabilities,
329    connection_config: ConnectionConfig,
330    plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>,
331    elicitation_handler: Option<Arc<dyn crate::handlers::ElicitationHandler>>,
332    progress_handler: Option<Arc<dyn crate::handlers::ProgressHandler>>,
333    log_handler: Option<Arc<dyn crate::handlers::LogHandler>>,
334    resource_update_handler: Option<Arc<dyn crate::handlers::ResourceUpdateHandler>>,
335    // Robustness configuration
336    enable_resilience: bool,
337    retry_config: Option<turbomcp_transport::resilience::RetryConfig>,
338    circuit_breaker_config: Option<turbomcp_transport::resilience::CircuitBreakerConfig>,
339    health_check_config: Option<turbomcp_transport::resilience::HealthCheckConfig>,
340}
341
342// Default implementation is now derived
343
344impl ClientBuilder {
345    /// Create a new client builder
346    ///
347    /// Returns a new builder with default configuration.
348    pub fn new() -> Self {
349        Self::default()
350    }
351
352    // ============================================================================
353    // CAPABILITY CONFIGURATION
354    // ============================================================================
355
356    /// Enable or disable tool support
357    ///
358    /// # Arguments
359    ///
360    /// * `enabled` - Whether to enable tool support
361    pub fn with_tools(mut self, enabled: bool) -> Self {
362        self.capabilities.tools = enabled;
363        self
364    }
365
366    /// Enable or disable prompt support
367    ///
368    /// # Arguments
369    ///
370    /// * `enabled` - Whether to enable prompt support
371    pub fn with_prompts(mut self, enabled: bool) -> Self {
372        self.capabilities.prompts = enabled;
373        self
374    }
375
376    /// Enable or disable resource support
377    ///
378    /// # Arguments
379    ///
380    /// * `enabled` - Whether to enable resource support
381    pub fn with_resources(mut self, enabled: bool) -> Self {
382        self.capabilities.resources = enabled;
383        self
384    }
385
386    /// Enable or disable sampling support
387    ///
388    /// # Arguments
389    ///
390    /// * `enabled` - Whether to enable sampling support
391    pub fn with_sampling(mut self, enabled: bool) -> Self {
392        self.capabilities.sampling = enabled;
393        self
394    }
395
396    /// Configure all capabilities at once
397    ///
398    /// # Arguments
399    ///
400    /// * `capabilities` - The capabilities configuration
401    pub fn with_capabilities(mut self, capabilities: ClientCapabilities) -> Self {
402        self.capabilities = capabilities;
403        self
404    }
405
406    // ============================================================================
407    // CONNECTION CONFIGURATION
408    // ============================================================================
409
410    /// Configure connection settings
411    ///
412    /// # Arguments
413    ///
414    /// * `config` - The connection configuration
415    pub fn with_connection_config(mut self, config: ConnectionConfig) -> Self {
416        self.connection_config = config;
417        self
418    }
419
420    /// Set request timeout
421    ///
422    /// # Arguments
423    ///
424    /// * `timeout_ms` - Timeout in milliseconds
425    pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
426        self.connection_config.timeout_ms = timeout_ms;
427        self
428    }
429
430    /// Set maximum retry attempts
431    ///
432    /// # Arguments
433    ///
434    /// * `max_retries` - Maximum number of retries
435    pub fn with_max_retries(mut self, max_retries: u32) -> Self {
436        self.connection_config.max_retries = max_retries;
437        self
438    }
439
440    /// Set retry delay
441    ///
442    /// # Arguments
443    ///
444    /// * `delay_ms` - Retry delay in milliseconds
445    pub fn with_retry_delay(mut self, delay_ms: u64) -> Self {
446        self.connection_config.retry_delay_ms = delay_ms;
447        self
448    }
449
450    /// Set keep-alive interval
451    ///
452    /// # Arguments
453    ///
454    /// * `interval_ms` - Keep-alive interval in milliseconds
455    pub fn with_keepalive(mut self, interval_ms: u64) -> Self {
456        self.connection_config.keepalive_ms = interval_ms;
457        self
458    }
459
460    // ============================================================================
461    // ROBUSTNESS & RESILIENCE CONFIGURATION
462    // ============================================================================
463
464    /// Enable resilient transport with circuit breaker, retry, and health checking
465    ///
466    /// When enabled, the transport layer will automatically:
467    /// - Retry failed operations with exponential backoff
468    /// - Use circuit breaker pattern to prevent cascade failures
469    /// - Perform periodic health checks
470    /// - Deduplicate messages
471    ///
472    /// # Examples
473    ///
474    /// ```rust,no_run
475    /// use turbomcp_client::ClientBuilder;
476    /// use turbomcp_transport::stdio::StdioTransport;
477    ///
478    /// let client = ClientBuilder::new()
479    ///     .enable_resilience()
480    ///     .build(StdioTransport::new());
481    /// ```
482    pub fn enable_resilience(mut self) -> Self {
483        self.enable_resilience = true;
484        self
485    }
486
487    /// Configure retry behavior for resilient transport
488    ///
489    /// # Arguments
490    ///
491    /// * `config` - Retry configuration
492    ///
493    /// # Examples
494    ///
495    /// ```rust,no_run
496    /// use turbomcp_client::ClientBuilder;
497    /// use turbomcp_transport::resilience::RetryConfig;
498    /// use turbomcp_transport::stdio::StdioTransport;
499    /// use std::time::Duration;
500    ///
501    /// let client = ClientBuilder::new()
502    ///     .enable_resilience()
503    ///     .with_retry_config(RetryConfig {
504    ///         max_attempts: 5,
505    ///         base_delay: Duration::from_millis(100),
506    ///         max_delay: Duration::from_secs(30),
507    ///         backoff_multiplier: 2.0,
508    ///         jitter_factor: 0.1,
509    ///         retry_on_connection_error: true,
510    ///         retry_on_timeout: true,
511    ///         custom_retry_conditions: Vec::new(),
512    ///     })
513    ///     .build(StdioTransport::new());
514    /// ```
515    pub fn with_retry_config(
516        mut self,
517        config: turbomcp_transport::resilience::RetryConfig,
518    ) -> Self {
519        self.retry_config = Some(config);
520        self.enable_resilience = true; // Auto-enable resilience
521        self
522    }
523
524    /// Configure circuit breaker for resilient transport
525    ///
526    /// # Arguments
527    ///
528    /// * `config` - Circuit breaker configuration
529    ///
530    /// # Examples
531    ///
532    /// ```rust,no_run
533    /// use turbomcp_client::ClientBuilder;
534    /// use turbomcp_transport::resilience::CircuitBreakerConfig;
535    /// use turbomcp_transport::stdio::StdioTransport;
536    /// use std::time::Duration;
537    ///
538    /// let client = ClientBuilder::new()
539    ///     .enable_resilience()
540    ///     .with_circuit_breaker_config(CircuitBreakerConfig {
541    ///         failure_threshold: 5,
542    ///         success_threshold: 2,
543    ///         timeout: Duration::from_secs(60),
544    ///         rolling_window_size: 100,
545    ///         minimum_requests: 10,
546    ///     })
547    ///     .build(StdioTransport::new());
548    /// ```
549    pub fn with_circuit_breaker_config(
550        mut self,
551        config: turbomcp_transport::resilience::CircuitBreakerConfig,
552    ) -> Self {
553        self.circuit_breaker_config = Some(config);
554        self.enable_resilience = true; // Auto-enable resilience
555        self
556    }
557
558    /// Configure health checking for resilient transport
559    ///
560    /// # Arguments
561    ///
562    /// * `config` - Health check configuration
563    ///
564    /// # Examples
565    ///
566    /// ```rust,no_run
567    /// use turbomcp_client::ClientBuilder;
568    /// use turbomcp_transport::resilience::HealthCheckConfig;
569    /// use turbomcp_transport::stdio::StdioTransport;
570    /// use std::time::Duration;
571    ///
572    /// let client = ClientBuilder::new()
573    ///     .enable_resilience()
574    ///     .with_health_check_config(HealthCheckConfig {
575    ///         interval: Duration::from_secs(30),
576    ///         timeout: Duration::from_secs(5),
577    ///         failure_threshold: 3,
578    ///         success_threshold: 1,
579    ///         custom_check: None,
580    ///     })
581    ///     .build(StdioTransport::new());
582    /// ```
583    pub fn with_health_check_config(
584        mut self,
585        config: turbomcp_transport::resilience::HealthCheckConfig,
586    ) -> Self {
587        self.health_check_config = Some(config);
588        self.enable_resilience = true; // Auto-enable resilience
589        self
590    }
591
592    // ============================================================================
593    // PLUGIN SYSTEM CONFIGURATION
594    // ============================================================================
595
596    /// Register a plugin with the client
597    ///
598    /// Plugins provide middleware functionality for request/response processing,
599    /// metrics collection, retry logic, caching, and other cross-cutting concerns.
600    ///
601    /// # Arguments
602    ///
603    /// * `plugin` - The plugin implementation
604    ///
605    /// # Examples
606    ///
607    /// ```rust,no_run
608    /// use turbomcp_client::{ClientBuilder, ConnectionConfig};
609    /// use turbomcp_client::plugins::{MetricsPlugin, RetryPlugin, PluginConfig, RetryConfig};
610    /// use std::sync::Arc;
611    ///
612    /// let client = ClientBuilder::new()
613    ///     .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
614    ///     .with_plugin(Arc::new(RetryPlugin::new(PluginConfig::Retry(RetryConfig {
615    ///         max_retries: 5,
616    ///         base_delay_ms: 1000,
617    ///         max_delay_ms: 30000,
618    ///         backoff_multiplier: 2.0,
619    ///         retry_on_timeout: true,
620    ///         retry_on_connection_error: true,
621    ///     }))));
622    /// ```
623    pub fn with_plugin(mut self, plugin: Arc<dyn crate::plugins::ClientPlugin>) -> Self {
624        self.plugins.push(plugin);
625        self
626    }
627
628    /// Register multiple plugins at once
629    ///
630    /// # Arguments
631    ///
632    /// * `plugins` - Vector of plugin implementations
633    pub fn with_plugins(mut self, plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>) -> Self {
634        self.plugins.extend(plugins);
635        self
636    }
637
638    // ============================================================================
639    // HANDLER REGISTRATION
640    // ============================================================================
641
642    /// Register an elicitation handler for processing user input requests
643    ///
644    /// # Arguments
645    ///
646    /// * `handler` - The elicitation handler implementation
647    pub fn with_elicitation_handler(
648        mut self,
649        handler: Arc<dyn crate::handlers::ElicitationHandler>,
650    ) -> Self {
651        self.elicitation_handler = Some(handler);
652        self
653    }
654
655    /// Register a progress handler for processing operation progress updates
656    ///
657    /// # Arguments
658    ///
659    /// * `handler` - The progress handler implementation
660    pub fn with_progress_handler(
661        mut self,
662        handler: Arc<dyn crate::handlers::ProgressHandler>,
663    ) -> Self {
664        self.progress_handler = Some(handler);
665        self
666    }
667
668    /// Register a log handler for processing server log messages
669    ///
670    /// # Arguments
671    ///
672    /// * `handler` - The log handler implementation
673    pub fn with_log_handler(mut self, handler: Arc<dyn crate::handlers::LogHandler>) -> Self {
674        self.log_handler = Some(handler);
675        self
676    }
677
678    /// Register a resource update handler for processing resource change notifications
679    ///
680    /// # Arguments
681    ///
682    /// * `handler` - The resource update handler implementation
683    pub fn with_resource_update_handler(
684        mut self,
685        handler: Arc<dyn crate::handlers::ResourceUpdateHandler>,
686    ) -> Self {
687        self.resource_update_handler = Some(handler);
688        self
689    }
690
691    // ============================================================================
692    // BUILD METHODS
693    // ============================================================================
694
695    /// Build a client with the configured options
696    ///
697    /// Creates a new client instance with all the configured options. The client
698    /// will be initialized with the registered plugins, handlers, and providers.
699    ///
700    /// # Arguments
701    ///
702    /// * `transport` - The transport to use for the client
703    ///
704    /// # Returns
705    ///
706    /// Returns a configured `Client` instance wrapped in a Result for async setup.
707    ///
708    /// # Examples
709    ///
710    /// ```rust,no_run
711    /// use turbomcp_client::ClientBuilder;
712    /// use turbomcp_transport::stdio::StdioTransport;
713    ///
714    /// # async fn example() -> turbomcp_protocol::Result<()> {
715    /// let client = ClientBuilder::new()
716    ///     .with_tools(true)
717    ///     .with_prompts(true)
718    ///     .build(StdioTransport::new())
719    ///     .await?;
720    /// # Ok(())
721    /// # }
722    /// ```
723    pub async fn build<T: Transport>(self, transport: T) -> Result<Client<T>> {
724        // Create base client with capabilities
725        let client = Client::with_capabilities(transport, self.capabilities);
726
727        // Register handlers
728        if let Some(handler) = self.elicitation_handler {
729            client.on_elicitation(handler);
730        }
731        if let Some(handler) = self.progress_handler {
732            client.on_progress(handler);
733        }
734        if let Some(handler) = self.log_handler {
735            client.on_log(handler);
736        }
737        if let Some(handler) = self.resource_update_handler {
738            client.on_resource_update(handler);
739        }
740
741        // Apply connection configuration (store for future use in actual connections)
742        // Note: The current Client doesn't expose connection config setters,
743        // so we'll store this for when the transport supports it
744
745        // Register plugins with the client
746        let has_plugins = !self.plugins.is_empty();
747        for plugin in self.plugins {
748            client.register_plugin(plugin).await.map_err(|e| {
749                Error::bad_request(format!("Failed to register plugin during build: {}", e))
750            })?;
751        }
752
753        // Initialize plugins after registration
754        if has_plugins {
755            client.initialize_plugins().await.map_err(|e| {
756                Error::bad_request(format!("Failed to initialize plugins during build: {}", e))
757            })?;
758        }
759
760        Ok(client)
761    }
762
763    /// Build a client with resilient transport (circuit breaker, retry, health checking)
764    ///
765    /// When resilience features are enabled via `enable_resilience()` or any resilience
766    /// configuration method, this wraps the transport in a `TurboTransport` that provides:
767    /// - Automatic retry with exponential backoff
768    /// - Circuit breaker pattern for fast failure
769    /// - Health checking and monitoring
770    /// - Message deduplication
771    ///
772    /// # Arguments
773    ///
774    /// * `transport` - The base transport to wrap with resilience features
775    ///
776    /// # Returns
777    ///
778    /// Returns a configured `Client<TurboTransport>` instance.
779    ///
780    /// # Errors
781    ///
782    /// Returns an error if plugin initialization fails.
783    ///
784    /// # Examples
785    ///
786    /// ```rust,no_run
787    /// use turbomcp_client::ClientBuilder;
788    /// use turbomcp_transport::stdio::StdioTransport;
789    /// use turbomcp_transport::resilience::{RetryConfig, CircuitBreakerConfig, HealthCheckConfig};
790    /// use std::time::Duration;
791    ///
792    /// # async fn example() -> turbomcp_protocol::Result<()> {
793    /// let client = ClientBuilder::new()
794    ///     .with_retry_config(RetryConfig {
795    ///         max_attempts: 5,
796    ///         base_delay: Duration::from_millis(200),
797    ///         ..Default::default()
798    ///     })
799    ///     .with_circuit_breaker_config(CircuitBreakerConfig {
800    ///         failure_threshold: 3,
801    ///         timeout: Duration::from_secs(30),
802    ///         ..Default::default()
803    ///     })
804    ///     .with_health_check_config(HealthCheckConfig {
805    ///         interval: Duration::from_secs(15),
806    ///         timeout: Duration::from_secs(5),
807    ///         ..Default::default()
808    ///     })
809    ///     .build_resilient(StdioTransport::new())
810    ///     .await?;
811    /// # Ok(())
812    /// # }
813    /// ```
814    pub async fn build_resilient<T: Transport + 'static>(
815        self,
816        transport: T,
817    ) -> Result<Client<turbomcp_transport::resilience::TurboTransport>> {
818        use turbomcp_transport::resilience::TurboTransport;
819
820        // Get configurations or use defaults
821        let retry_config = self.retry_config.unwrap_or_default();
822        let circuit_config = self.circuit_breaker_config.unwrap_or_default();
823        let health_config = self.health_check_config.unwrap_or_default();
824
825        // Wrap transport in TurboTransport
826        let robust_transport = TurboTransport::new(
827            Box::new(transport),
828            retry_config,
829            circuit_config,
830            health_config,
831        );
832
833        // Create client with resilient transport
834        let client = Client::with_capabilities(robust_transport, self.capabilities);
835
836        // Register handlers
837        if let Some(handler) = self.elicitation_handler {
838            client.on_elicitation(handler);
839        }
840        if let Some(handler) = self.progress_handler {
841            client.on_progress(handler);
842        }
843        if let Some(handler) = self.log_handler {
844            client.on_log(handler);
845        }
846        if let Some(handler) = self.resource_update_handler {
847            client.on_resource_update(handler);
848        }
849
850        // Register plugins
851        let has_plugins = !self.plugins.is_empty();
852        for plugin in self.plugins {
853            client.register_plugin(plugin).await.map_err(|e| {
854                Error::bad_request(format!("Failed to register plugin during build: {}", e))
855            })?;
856        }
857
858        if has_plugins {
859            client.initialize_plugins().await.map_err(|e| {
860                Error::bad_request(format!("Failed to initialize plugins during build: {}", e))
861            })?;
862        }
863
864        Ok(client)
865    }
866
867    /// Build a client synchronously with basic configuration only
868    ///
869    /// This is a convenience method for simple use cases where no async setup
870    /// is required. For advanced features like plugins, use `build()` instead.
871    ///
872    /// # Arguments
873    ///
874    /// * `transport` - The transport to use for the client
875    ///
876    /// # Returns
877    ///
878    /// Returns a configured `Client` instance.
879    ///
880    /// # Examples
881    ///
882    /// ```rust,no_run
883    /// use turbomcp_client::ClientBuilder;
884    /// use turbomcp_transport::stdio::StdioTransport;
885    ///
886    /// let client = ClientBuilder::new()
887    ///     .with_tools(true)
888    ///     .build_sync(StdioTransport::new());
889    /// ```
890    pub fn build_sync<T: Transport>(self, transport: T) -> Client<T> {
891        let client = Client::with_capabilities(transport, self.capabilities);
892
893        // Register synchronous handlers only
894        if let Some(handler) = self.elicitation_handler {
895            client.on_elicitation(handler);
896        }
897        if let Some(handler) = self.progress_handler {
898            client.on_progress(handler);
899        }
900        if let Some(handler) = self.log_handler {
901            client.on_log(handler);
902        }
903        if let Some(handler) = self.resource_update_handler {
904            client.on_resource_update(handler);
905        }
906
907        client
908    }
909
910    // ============================================================================
911    // CONFIGURATION ACCESS
912    // ============================================================================
913
914    /// Get the current capabilities configuration
915    pub fn capabilities(&self) -> &ClientCapabilities {
916        &self.capabilities
917    }
918
919    /// Get the current connection configuration
920    pub fn connection_config(&self) -> &ConnectionConfig {
921        &self.connection_config
922    }
923
924    /// Get the number of registered plugins
925    pub fn plugin_count(&self) -> usize {
926        self.plugins.len()
927    }
928
929    /// Check if any handlers are registered
930    pub fn has_handlers(&self) -> bool {
931        self.elicitation_handler.is_some()
932            || self.progress_handler.is_some()
933            || self.log_handler.is_some()
934            || self.resource_update_handler.is_some()
935    }
936}
937
938// Re-export types for public API
939pub use turbomcp_protocol::types::ServerCapabilities as PublicServerCapabilities;