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