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::{
92//!     CreateMessageRequest, CreateMessageResult, Role, SamplingContent, StopReason,
93//! };
94//! use std::future::Future;
95//! use std::pin::Pin;
96//!
97//! #[derive(Debug)]
98//! struct MySamplingHandler {
99//!     // Your LLM client would go here
100//! }
101//!
102//! impl SamplingHandler for MySamplingHandler {
103//!     fn handle_create_message(
104//!         &self,
105//!         request_id: String,
106//!         request: CreateMessageRequest
107//!     ) -> Pin<Box<dyn Future<Output = Result<CreateMessageResult, Box<dyn std::error::Error + Send + Sync>>> + Send + '_>> {
108//!         Box::pin(async move {
109//!             // Forward to your LLM provider (OpenAI, Anthropic, etc.)
110//!             // Use request_id for correlation tracking
111//!             // Allows the server to request LLM sampling through the client
112//!
113//!             Ok(CreateMessageResult {
114//!                 role: Role::Assistant,
115//!                 content: SamplingContent::text("Response from LLM").into(),
116//!                 model: "gpt-4".to_string(),
117//!                 stop_reason: Some(StopReason::EndTurn.to_string()),
118//!                 meta: None,
119//!             })
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, None).await {
135//!     Ok(result) => println!("Tool result: {:?}", result),
136//!     Err(e) => eprintln!("Tool call failed: {}", e),
137//! }
138//! # Ok(())
139//! # }
140//! ```
141
142/// TurboMCP Client version from Cargo.toml
143///
144/// This constant provides easy programmatic access to the current version.
145///
146/// # Example
147///
148/// ```rust
149/// println!("TurboMCP Client version: {}", turbomcp_client::VERSION);
150/// ```
151pub const VERSION: &str = env!("CARGO_PKG_VERSION");
152
153/// TurboMCP Client crate name
154pub const CRATE_NAME: &str = env!("CARGO_PKG_NAME");
155
156pub mod client;
157pub mod handlers;
158pub mod integration;
159pub mod prelude;
160pub mod sampling;
161
162// v3.0 Tower-native middleware
163pub mod middleware;
164
165// Re-export key types for convenience
166pub use client::{ConnectionInfo, ConnectionState, ManagerConfig, ServerGroup, SessionManager};
167
168use std::sync::Arc;
169use std::time::Duration;
170
171// Re-export Transport trait for generic bounds in integrations
172pub use turbomcp_transport::Transport;
173
174// ============================================================================
175// TOP-LEVEL RE-EXPORTS FOR ERGONOMIC IMPORTS
176// ============================================================================
177
178// Result/Error types - re-export from protocol for consistency
179pub use turbomcp_protocol::{Error, Result};
180
181// Handler types (most commonly used)
182pub use handlers::{
183    // Cancellation (current MCP spec)
184    CancellationHandler,
185    CancelledNotification,
186    ElicitationAction,
187    // Elicitation
188    ElicitationHandler,
189    ElicitationRequest,
190    ElicitationResponse,
191    // Error handling
192    HandlerError,
193    HandlerResult,
194    // Logging (current MCP spec)
195    LogHandler,
196    LoggingNotification,
197    // Progress (current MCP spec)
198    ProgressHandler,
199    ProgressNotification,
200    PromptListChangedHandler,
201    // List changed handlers (current MCP spec)
202    ResourceListChangedHandler,
203    // Resource updates (current MCP spec)
204    ResourceUpdateHandler,
205    ResourceUpdatedNotification,
206    // Roots
207    RootsHandler,
208    ToolListChangedHandler,
209};
210
211// Sampling types
212pub use sampling::{SamplingHandler, ServerInfo, UserInteractionHandler};
213
214// v3.0 Tower middleware
215pub use middleware::{
216    Cache, CacheConfig, CacheLayer, CacheService, McpRequest, McpResponse, Metrics, MetricsLayer,
217    MetricsService, MetricsSnapshot, TracingLayer, TracingService,
218};
219
220// Common protocol types
221pub use turbomcp_protocol::types::{
222    // Resource content types (for processing embedded resources)
223    BlobResourceContents,
224    // Tool result types (for LLM integrations like rig)
225    CallToolResult,
226    // Core types
227    ContentBlock,
228    EmbeddedResource,
229    LogLevel,
230    Prompt,
231    Resource,
232    ResourceContent,
233    ResourceContents,
234    Role,
235    TextResourceContents,
236    Tool,
237};
238
239// Transport re-exports (with feature gates)
240#[cfg(feature = "stdio")]
241pub use turbomcp_transport::stdio::StdioTransport;
242
243#[cfg(feature = "http")]
244pub use turbomcp_transport::streamable_http_client::{
245    RetryPolicy, StreamableHttpClientConfig, StreamableHttpClientTransport,
246};
247
248#[cfg(feature = "tcp")]
249pub use turbomcp_transport::tcp::{TcpTransport, TcpTransportBuilder};
250
251#[cfg(feature = "unix")]
252pub use turbomcp_transport::unix::{UnixTransport, UnixTransportBuilder};
253
254#[cfg(feature = "websocket")]
255pub use turbomcp_transport::websocket_bidirectional::{
256    WebSocketBidirectionalConfig, WebSocketBidirectionalTransport,
257};
258
259/// Client capability configuration
260///
261/// Defines the capabilities that this client supports when connecting to MCP servers.
262/// These capabilities are sent during the initialization handshake to negotiate
263/// which features will be available during the session.
264///
265/// # Examples
266///
267/// ```
268/// use turbomcp_client::ClientCapabilities;
269///
270/// let capabilities = ClientCapabilities {
271///     tools: true,
272///     prompts: true,
273///     resources: true,
274///     sampling: false,
275///     max_concurrent_handlers: 100,
276/// };
277/// ```
278#[derive(Debug, Clone)]
279pub struct ClientCapabilities {
280    /// Whether the client supports tool calling
281    pub tools: bool,
282
283    /// Whether the client supports prompts
284    pub prompts: bool,
285
286    /// Whether the client supports resources
287    pub resources: bool,
288
289    /// Whether the client supports sampling
290    pub sampling: bool,
291
292    /// Maximum concurrent request/notification handlers (default: 100)
293    ///
294    /// This limits how many server-initiated requests/notifications can be processed simultaneously.
295    /// Provides automatic backpressure when the limit is reached.
296    ///
297    /// **Tuning Guide:**
298    /// - Low-resource clients: 50
299    /// - Standard clients: 100 (default)
300    /// - High-performance: 200-500
301    /// - Maximum recommended: 1000
302    pub max_concurrent_handlers: usize,
303}
304
305impl Default for ClientCapabilities {
306    fn default() -> Self {
307        Self {
308            tools: false,
309            prompts: false,
310            resources: false,
311            sampling: false,
312            max_concurrent_handlers: 100,
313        }
314    }
315}
316
317impl ClientCapabilities {
318    /// All capabilities enabled (tools, prompts, resources, sampling)
319    ///
320    /// This is the most comprehensive configuration, enabling full MCP protocol support.
321    ///
322    /// # Example
323    ///
324    /// ```rust
325    /// use turbomcp_client::ClientCapabilities;
326    ///
327    /// let capabilities = ClientCapabilities::all();
328    /// assert!(capabilities.tools);
329    /// assert!(capabilities.prompts);
330    /// assert!(capabilities.resources);
331    /// assert!(capabilities.sampling);
332    /// ```
333    #[must_use]
334    pub fn all() -> Self {
335        Self {
336            tools: true,
337            prompts: true,
338            resources: true,
339            sampling: true,
340            max_concurrent_handlers: 100,
341        }
342    }
343
344    /// Core capabilities without sampling (tools, prompts, resources)
345    ///
346    /// This is the recommended default for most applications. It enables
347    /// all standard MCP features except server-initiated sampling requests.
348    ///
349    /// # Example
350    ///
351    /// ```rust
352    /// use turbomcp_client::ClientCapabilities;
353    ///
354    /// let capabilities = ClientCapabilities::core();
355    /// assert!(capabilities.tools);
356    /// assert!(capabilities.prompts);
357    /// assert!(capabilities.resources);
358    /// assert!(!capabilities.sampling);
359    /// ```
360    #[must_use]
361    pub fn core() -> Self {
362        Self {
363            tools: true,
364            prompts: true,
365            resources: true,
366            sampling: false,
367            max_concurrent_handlers: 100,
368        }
369    }
370
371    /// Minimal capabilities (tools only)
372    ///
373    /// Use this for simple tool-calling clients that don't need prompts,
374    /// resources, or sampling support.
375    ///
376    /// # Example
377    ///
378    /// ```rust
379    /// use turbomcp_client::ClientCapabilities;
380    ///
381    /// let capabilities = ClientCapabilities::minimal();
382    /// assert!(capabilities.tools);
383    /// assert!(!capabilities.prompts);
384    /// assert!(!capabilities.resources);
385    /// assert!(!capabilities.sampling);
386    /// ```
387    #[must_use]
388    pub fn minimal() -> Self {
389        Self {
390            tools: true,
391            prompts: false,
392            resources: false,
393            sampling: false,
394            max_concurrent_handlers: 100,
395        }
396    }
397
398    /// Only tools enabled
399    ///
400    /// Same as `minimal()`, provided for clarity.
401    #[must_use]
402    pub fn only_tools() -> Self {
403        Self::minimal()
404    }
405
406    /// Only resources enabled
407    ///
408    /// Use this for resource-focused clients that don't need tools or prompts.
409    ///
410    /// # Example
411    ///
412    /// ```rust
413    /// use turbomcp_client::ClientCapabilities;
414    ///
415    /// let capabilities = ClientCapabilities::only_resources();
416    /// assert!(!capabilities.tools);
417    /// assert!(!capabilities.prompts);
418    /// assert!(capabilities.resources);
419    /// ```
420    #[must_use]
421    pub fn only_resources() -> Self {
422        Self {
423            tools: false,
424            prompts: false,
425            resources: true,
426            sampling: false,
427            max_concurrent_handlers: 100,
428        }
429    }
430
431    /// Only prompts enabled
432    ///
433    /// Use this for prompt-focused clients that don't need tools or resources.
434    ///
435    /// # Example
436    ///
437    /// ```rust
438    /// use turbomcp_client::ClientCapabilities;
439    ///
440    /// let capabilities = ClientCapabilities::only_prompts();
441    /// assert!(!capabilities.tools);
442    /// assert!(capabilities.prompts);
443    /// assert!(!capabilities.resources);
444    /// ```
445    #[must_use]
446    pub fn only_prompts() -> Self {
447        Self {
448            tools: false,
449            prompts: true,
450            resources: false,
451            sampling: false,
452            max_concurrent_handlers: 100,
453        }
454    }
455
456    /// Only sampling enabled
457    ///
458    /// Use this for clients that exclusively handle server-initiated sampling requests.
459    #[must_use]
460    pub fn only_sampling() -> Self {
461        Self {
462            tools: false,
463            prompts: false,
464            resources: false,
465            sampling: true,
466            max_concurrent_handlers: 100,
467        }
468    }
469}
470
471/// JSON-RPC protocol handler for MCP communication
472// Note: ProtocolClient implementation moved to client/protocol.rs for better modularity
473/// MCP client for communicating with servers
474///
475/// The `Client` struct provides an ergonomic interface for interacting with MCP servers.
476/// It handles protocol complexity internally, exposing clean, type-safe methods.
477///
478/// # Type Parameters
479///
480/// * `T` - The transport implementation used for communication
481///
482/// # Examples
483///
484/// ```rust,no_run
485/// use turbomcp_client::Client;
486/// use turbomcp_transport::stdio::StdioTransport;
487///
488/// # async fn example() -> turbomcp_protocol::Result<()> {
489/// let transport = StdioTransport::new();
490/// let mut client = Client::new(transport);
491///
492/// // Initialize and start using the client
493/// client.initialize().await?;
494/// # Ok(())
495/// # }
496/// ```
497// Re-export Client from the core module
498pub use client::core::Client;
499
500// Thread-safe wrapper for sharing Client across async tasks
501//
502// This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
503// for concurrent access to MCP client functionality. It addresses the limitations
504// identified in PR feedback where Client requires `&mut self` for all operations
505// but needs to be shared across multiple async tasks.
506//
507// # Design Rationale
508//
509// All Client methods require `&mut self` because:
510// - MCP connections maintain state (initialized flag, connection status)
511// - Request correlation tracking for JSON-RPC requires mutation
512// - Handler and plugin registries need mutable access
513//
514// Note: SharedClient has been removed in v2 - Client is now directly cloneable via Arc
515
516// ----------------------------------------------------------------------------
517// Re-exports
518// ----------------------------------------------------------------------------
519
520#[doc = "Result of client initialization"]
521#[doc = ""]
522#[doc = "Contains information about the server and the negotiated capabilities"]
523#[doc = "after a successful initialization handshake."]
524pub use client::config::InitializeResult;
525
526// ServerCapabilities is now imported from turbomcp_protocol::types
527
528/// Connection configuration for the client
529#[derive(Debug, Clone)]
530pub struct ConnectionConfig {
531    /// Request timeout in milliseconds
532    pub timeout_ms: u64,
533
534    /// Maximum number of retry attempts
535    pub max_retries: u32,
536
537    /// Retry delay in milliseconds
538    pub retry_delay_ms: u64,
539
540    /// Keep-alive interval in milliseconds
541    pub keepalive_ms: u64,
542}
543
544fn protocol_transport_config(
545    connection_config: &ConnectionConfig,
546) -> turbomcp_transport::TransportConfig {
547    let timeout = Duration::from_millis(connection_config.timeout_ms);
548
549    turbomcp_transport::TransportConfig {
550        connect_timeout: timeout,
551        keep_alive: Some(Duration::from_millis(connection_config.keepalive_ms)),
552        timeouts: turbomcp_transport::config::TimeoutConfig {
553            connect: timeout,
554            request: Some(timeout),
555            total: Some(timeout),
556            read: Some(timeout),
557        },
558        ..Default::default()
559    }
560}
561
562fn resilience_requested(builder: &ClientBuilder) -> bool {
563    builder.enable_resilience
564        || builder.retry_config.is_some()
565        || builder.circuit_breaker_config.is_some()
566        || builder.health_check_config.is_some()
567}
568
569impl Default for ConnectionConfig {
570    fn default() -> Self {
571        Self {
572            timeout_ms: 30_000,    // 30 seconds
573            max_retries: 3,        // 3 attempts
574            retry_delay_ms: 1_000, // 1 second
575            keepalive_ms: 60_000,  // 60 seconds
576        }
577    }
578}
579
580/// Builder for configuring and creating MCP clients
581///
582/// Provides a fluent interface for configuring client options before creation.
583/// The enhanced builder pattern supports comprehensive configuration including:
584/// - Protocol capabilities
585/// - Plugin registration
586/// - Handler registration
587/// - Connection settings
588/// - Resilience configuration
589///
590/// # Examples
591///
592/// Basic usage:
593/// ```rust,no_run
594/// use turbomcp_client::ClientBuilder;
595/// use turbomcp_transport::stdio::StdioTransport;
596///
597/// # async fn example() -> turbomcp_protocol::Result<()> {
598/// let client = ClientBuilder::new()
599///     .with_tools(true)
600///     .with_prompts(true)
601///     .with_resources(false)
602///     .build(StdioTransport::new());
603/// # Ok(())
604/// # }
605/// ```
606///
607/// Advanced configuration with Tower middleware:
608/// ```rust,no_run
609/// use turbomcp_client::{ClientBuilder, ConnectionConfig};
610/// use turbomcp_client::middleware::MetricsLayer;
611/// use turbomcp_transport::stdio::StdioTransport;
612/// use tower::ServiceBuilder;
613///
614/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
615/// let client = ClientBuilder::new()
616///     .with_tools(true)
617///     .with_prompts(true)
618///     .with_resources(true)
619///     .with_sampling(true)
620///     .with_connection_config(ConnectionConfig {
621///         timeout_ms: 60_000,
622///         max_retries: 5,
623///         retry_delay_ms: 2_000,
624///         keepalive_ms: 30_000,
625///     })
626///     .build(StdioTransport::new())
627///     .await?;
628/// # Ok(())
629/// # }
630/// ```
631#[derive(Debug, Default)]
632pub struct ClientBuilder {
633    capabilities: ClientCapabilities,
634    connection_config: ConnectionConfig,
635    elicitation_handler: Option<Arc<dyn crate::handlers::ElicitationHandler>>,
636    log_handler: Option<Arc<dyn crate::handlers::LogHandler>>,
637    resource_update_handler: Option<Arc<dyn crate::handlers::ResourceUpdateHandler>>,
638    progress_handler: Option<Arc<dyn crate::handlers::ProgressHandler>>,
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    /// Register a progress handler for processing progress notifications
983    ///
984    /// # Arguments
985    ///
986    /// * `handler` - The progress handler implementation
987    pub fn with_progress_handler(
988        mut self,
989        handler: Arc<dyn crate::handlers::ProgressHandler>,
990    ) -> Self {
991        self.progress_handler = Some(handler);
992        self
993    }
994
995    // ============================================================================
996    // BUILD METHODS
997    // ============================================================================
998
999    /// Build a client with the configured options
1000    ///
1001    /// Creates a new client instance with all the configured options. The client
1002    /// will be initialized with the registered plugins, handlers, and providers.
1003    ///
1004    /// # Arguments
1005    ///
1006    /// * `transport` - The transport to use for the client
1007    ///
1008    /// # Returns
1009    ///
1010    /// Returns a configured `Client` instance wrapped in a Result for async setup.
1011    ///
1012    /// # Examples
1013    ///
1014    /// ```rust,no_run
1015    /// use turbomcp_client::ClientBuilder;
1016    /// use turbomcp_transport::stdio::StdioTransport;
1017    ///
1018    /// # async fn example() -> turbomcp_protocol::Result<()> {
1019    /// let client = ClientBuilder::new()
1020    ///     .with_tools(true)
1021    ///     .with_prompts(true)
1022    ///     .build(StdioTransport::new())
1023    ///     .await?;
1024    /// # Ok(())
1025    /// # }
1026    /// ```
1027    pub async fn build<T: Transport + 'static>(self, transport: T) -> Result<Client<T>> {
1028        if resilience_requested(&self) {
1029            return Err(Error::configuration(
1030                "resilience settings require build_resilient(); build() would otherwise ignore them"
1031                    .to_string(),
1032            ));
1033        }
1034
1035        // Create base client with capabilities
1036        let client = Client::with_capabilities_and_config(
1037            transport,
1038            self.capabilities,
1039            protocol_transport_config(&self.connection_config),
1040        );
1041
1042        // Register handlers
1043        if let Some(handler) = self.elicitation_handler {
1044            client.set_elicitation_handler(handler);
1045        }
1046        if let Some(handler) = self.log_handler {
1047            client.set_log_handler(handler);
1048        }
1049        if let Some(handler) = self.resource_update_handler {
1050            client.set_resource_update_handler(handler);
1051        }
1052        if let Some(handler) = self.progress_handler {
1053            client.set_progress_handler(handler);
1054        }
1055
1056        Ok(client)
1057    }
1058
1059    /// Build a client with resilient transport (circuit breaker, retry, health checking)
1060    ///
1061    /// When resilience features are enabled via `enable_resilience()` or any resilience
1062    /// configuration method, this wraps the transport in a `TurboTransport` that provides:
1063    /// - Automatic retry with exponential backoff
1064    /// - Circuit breaker pattern for fast failure
1065    /// - Health checking and monitoring
1066    /// - Message deduplication
1067    ///
1068    /// # Arguments
1069    ///
1070    /// * `transport` - The base transport to wrap with resilience features
1071    ///
1072    /// # Returns
1073    ///
1074    /// Returns a configured `Client<TurboTransport>` instance.
1075    ///
1076    /// # Errors
1077    ///
1078    /// Returns an error if plugin initialization fails.
1079    ///
1080    /// # Examples
1081    ///
1082    /// ```rust,no_run
1083    /// use turbomcp_client::ClientBuilder;
1084    /// use turbomcp_transport::stdio::StdioTransport;
1085    /// use turbomcp_transport::resilience::{RetryConfig, CircuitBreakerConfig, HealthCheckConfig};
1086    /// use std::time::Duration;
1087    ///
1088    /// # async fn example() -> turbomcp_protocol::Result<()> {
1089    /// let client = ClientBuilder::new()
1090    ///     .with_retry_config(RetryConfig {
1091    ///         max_attempts: 5,
1092    ///         base_delay: Duration::from_millis(200),
1093    ///         ..Default::default()
1094    ///     })
1095    ///     .with_circuit_breaker_config(CircuitBreakerConfig {
1096    ///         failure_threshold: 3,
1097    ///         timeout: Duration::from_secs(30),
1098    ///         ..Default::default()
1099    ///     })
1100    ///     .with_health_check_config(HealthCheckConfig {
1101    ///         interval: Duration::from_secs(15),
1102    ///         timeout: Duration::from_secs(5),
1103    ///         ..Default::default()
1104    ///     })
1105    ///     .build_resilient(StdioTransport::new())
1106    ///     .await?;
1107    /// # Ok(())
1108    /// # }
1109    /// ```
1110    pub async fn build_resilient<T: Transport + 'static>(
1111        self,
1112        transport: T,
1113    ) -> Result<Client<turbomcp_transport::resilience::TurboTransport>> {
1114        use turbomcp_transport::resilience::TurboTransport;
1115
1116        // Get configurations or use defaults
1117        let retry_config =
1118            self.retry_config
1119                .unwrap_or_else(|| turbomcp_transport::resilience::RetryConfig {
1120                    max_attempts: self.connection_config.max_retries.max(1),
1121                    base_delay: Duration::from_millis(self.connection_config.retry_delay_ms),
1122                    ..Default::default()
1123                });
1124        let circuit_config = self.circuit_breaker_config.unwrap_or_default();
1125        let health_config = self.health_check_config.unwrap_or_else(|| {
1126            turbomcp_transport::resilience::HealthCheckConfig {
1127                timeout: Duration::from_millis(self.connection_config.timeout_ms),
1128                ..Default::default()
1129            }
1130        });
1131
1132        // Wrap transport in TurboTransport
1133        let robust_transport = TurboTransport::new(
1134            Box::new(transport),
1135            retry_config,
1136            circuit_config,
1137            health_config,
1138        );
1139
1140        // Create client with resilient transport
1141        let client = Client::with_capabilities_and_config(
1142            robust_transport,
1143            self.capabilities,
1144            protocol_transport_config(&self.connection_config),
1145        );
1146
1147        // Register handlers
1148        if let Some(handler) = self.elicitation_handler {
1149            client.set_elicitation_handler(handler);
1150        }
1151        if let Some(handler) = self.log_handler {
1152            client.set_log_handler(handler);
1153        }
1154        if let Some(handler) = self.resource_update_handler {
1155            client.set_resource_update_handler(handler);
1156        }
1157        if let Some(handler) = self.progress_handler {
1158            client.set_progress_handler(handler);
1159        }
1160
1161        Ok(client)
1162    }
1163
1164    /// Build a client synchronously with basic configuration only
1165    ///
1166    /// This is a convenience method for simple use cases.
1167    ///
1168    /// # Arguments
1169    ///
1170    /// * `transport` - The transport to use for the client
1171    ///
1172    /// # Returns
1173    ///
1174    /// Returns a configured `Client` instance.
1175    ///
1176    /// # Examples
1177    ///
1178    /// ```rust,no_run
1179    /// use turbomcp_client::ClientBuilder;
1180    /// use turbomcp_transport::stdio::StdioTransport;
1181    ///
1182    /// let client = ClientBuilder::new()
1183    ///     .with_tools(true)
1184    ///     .build_sync(StdioTransport::new());
1185    /// ```
1186    pub fn build_sync<T: Transport + 'static>(self, transport: T) -> Client<T> {
1187        assert!(
1188            !resilience_requested(&self),
1189            "resilience settings require build_resilient(); build_sync() would otherwise ignore them"
1190        );
1191
1192        let client = Client::with_capabilities_and_config(
1193            transport,
1194            self.capabilities,
1195            protocol_transport_config(&self.connection_config),
1196        );
1197
1198        // Register synchronous handlers only
1199        if let Some(handler) = self.elicitation_handler {
1200            client.set_elicitation_handler(handler);
1201        }
1202        if let Some(handler) = self.log_handler {
1203            client.set_log_handler(handler);
1204        }
1205        if let Some(handler) = self.resource_update_handler {
1206            client.set_resource_update_handler(handler);
1207        }
1208        if let Some(handler) = self.progress_handler {
1209            client.set_progress_handler(handler);
1210        }
1211
1212        client
1213    }
1214
1215    // ============================================================================
1216    // CONFIGURATION ACCESS
1217    // ============================================================================
1218
1219    /// Get the current capabilities configuration
1220    #[must_use]
1221    pub fn capabilities(&self) -> &ClientCapabilities {
1222        &self.capabilities
1223    }
1224
1225    /// Get the current connection configuration
1226    #[must_use]
1227    pub fn connection_config(&self) -> &ConnectionConfig {
1228        &self.connection_config
1229    }
1230
1231    /// Check if any handlers are registered
1232    #[must_use]
1233    pub fn has_handlers(&self) -> bool {
1234        self.elicitation_handler.is_some()
1235            || self.log_handler.is_some()
1236            || self.resource_update_handler.is_some()
1237            || self.progress_handler.is_some()
1238    }
1239}
1240
1241// Re-export types for public API
1242pub use turbomcp_protocol::types::ServerCapabilities as PublicServerCapabilities;
1243
1244#[cfg(test)]
1245mod tests {
1246    use super::*;
1247    use std::future::Future;
1248    use std::pin::Pin;
1249    use turbomcp_transport::{
1250        TransportCapabilities, TransportConfig, TransportMessage, TransportMetrics,
1251        TransportResult, TransportState, TransportType,
1252    };
1253
1254    #[derive(Debug, Default)]
1255    struct NoopTransport {
1256        capabilities: TransportCapabilities,
1257    }
1258
1259    impl Transport for NoopTransport {
1260        fn transport_type(&self) -> TransportType {
1261            TransportType::Stdio
1262        }
1263
1264        fn capabilities(&self) -> &TransportCapabilities {
1265            &self.capabilities
1266        }
1267
1268        fn state(&self) -> Pin<Box<dyn Future<Output = TransportState> + Send + '_>> {
1269            Box::pin(async { TransportState::Disconnected })
1270        }
1271
1272        fn connect(&self) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1273            Box::pin(async { Ok(()) })
1274        }
1275
1276        fn disconnect(&self) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1277            Box::pin(async { Ok(()) })
1278        }
1279
1280        fn send(
1281            &self,
1282            _message: TransportMessage,
1283        ) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1284            Box::pin(async { Ok(()) })
1285        }
1286
1287        fn receive(
1288            &self,
1289        ) -> Pin<Box<dyn Future<Output = TransportResult<Option<TransportMessage>>> + Send + '_>>
1290        {
1291            Box::pin(async { Ok(None) })
1292        }
1293
1294        fn metrics(&self) -> Pin<Box<dyn Future<Output = TransportMetrics> + Send + '_>> {
1295            Box::pin(async { TransportMetrics::default() })
1296        }
1297
1298        fn configure(
1299            &self,
1300            _config: TransportConfig,
1301        ) -> Pin<Box<dyn Future<Output = TransportResult<()>> + Send + '_>> {
1302            Box::pin(async { Ok(()) })
1303        }
1304    }
1305
1306    #[tokio::test]
1307    async fn build_rejects_resilience_flags() {
1308        let result = ClientBuilder::new()
1309            .enable_resilience()
1310            .build(NoopTransport::default())
1311            .await;
1312
1313        assert!(result.is_err());
1314        let err = match result {
1315            Ok(_) => panic!("expected build() to reject resilience settings"),
1316            Err(err) => err,
1317        };
1318        assert!(err.to_string().contains("build_resilient"));
1319    }
1320}