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 (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_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: "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/// };
256/// ```
257#[derive(Debug, Clone, Default)]
258pub struct ClientCapabilities {
259 /// Whether the client supports tool calling
260 pub tools: bool,
261
262 /// Whether the client supports prompts
263 pub prompts: bool,
264
265 /// Whether the client supports resources
266 pub resources: bool,
267
268 /// Whether the client supports sampling
269 pub sampling: bool,
270}
271
272impl ClientCapabilities {
273 /// All capabilities enabled (tools, prompts, resources, sampling)
274 ///
275 /// This is the most comprehensive configuration, enabling full MCP protocol support.
276 ///
277 /// # Example
278 ///
279 /// ```rust
280 /// use turbomcp_client::ClientCapabilities;
281 ///
282 /// let capabilities = ClientCapabilities::all();
283 /// assert!(capabilities.tools);
284 /// assert!(capabilities.prompts);
285 /// assert!(capabilities.resources);
286 /// assert!(capabilities.sampling);
287 /// ```
288 #[must_use]
289 pub fn all() -> Self {
290 Self {
291 tools: true,
292 prompts: true,
293 resources: true,
294 sampling: true,
295 }
296 }
297
298 /// Core capabilities without sampling (tools, prompts, resources)
299 ///
300 /// This is the recommended default for most applications. It enables
301 /// all standard MCP features except server-initiated sampling requests.
302 ///
303 /// # Example
304 ///
305 /// ```rust
306 /// use turbomcp_client::ClientCapabilities;
307 ///
308 /// let capabilities = ClientCapabilities::core();
309 /// assert!(capabilities.tools);
310 /// assert!(capabilities.prompts);
311 /// assert!(capabilities.resources);
312 /// assert!(!capabilities.sampling);
313 /// ```
314 #[must_use]
315 pub fn core() -> Self {
316 Self {
317 tools: true,
318 prompts: true,
319 resources: true,
320 sampling: false,
321 }
322 }
323
324 /// Minimal capabilities (tools only)
325 ///
326 /// Use this for simple tool-calling clients that don't need prompts,
327 /// resources, or sampling support.
328 ///
329 /// # Example
330 ///
331 /// ```rust
332 /// use turbomcp_client::ClientCapabilities;
333 ///
334 /// let capabilities = ClientCapabilities::minimal();
335 /// assert!(capabilities.tools);
336 /// assert!(!capabilities.prompts);
337 /// assert!(!capabilities.resources);
338 /// assert!(!capabilities.sampling);
339 /// ```
340 #[must_use]
341 pub fn minimal() -> Self {
342 Self {
343 tools: true,
344 prompts: false,
345 resources: false,
346 sampling: false,
347 }
348 }
349
350 /// Only tools enabled
351 ///
352 /// Same as `minimal()`, provided for clarity.
353 #[must_use]
354 pub fn only_tools() -> Self {
355 Self::minimal()
356 }
357
358 /// Only resources enabled
359 ///
360 /// Use this for resource-focused clients that don't need tools or prompts.
361 ///
362 /// # Example
363 ///
364 /// ```rust
365 /// use turbomcp_client::ClientCapabilities;
366 ///
367 /// let capabilities = ClientCapabilities::only_resources();
368 /// assert!(!capabilities.tools);
369 /// assert!(!capabilities.prompts);
370 /// assert!(capabilities.resources);
371 /// ```
372 #[must_use]
373 pub fn only_resources() -> Self {
374 Self {
375 tools: false,
376 prompts: false,
377 resources: true,
378 sampling: false,
379 }
380 }
381
382 /// Only prompts enabled
383 ///
384 /// Use this for prompt-focused clients that don't need tools or resources.
385 ///
386 /// # Example
387 ///
388 /// ```rust
389 /// use turbomcp_client::ClientCapabilities;
390 ///
391 /// let capabilities = ClientCapabilities::only_prompts();
392 /// assert!(!capabilities.tools);
393 /// assert!(capabilities.prompts);
394 /// assert!(!capabilities.resources);
395 /// ```
396 #[must_use]
397 pub fn only_prompts() -> Self {
398 Self {
399 tools: false,
400 prompts: true,
401 resources: false,
402 sampling: false,
403 }
404 }
405
406 /// Only sampling enabled
407 ///
408 /// Use this for clients that exclusively handle server-initiated sampling requests.
409 #[must_use]
410 pub fn only_sampling() -> Self {
411 Self {
412 tools: false,
413 prompts: false,
414 resources: false,
415 sampling: true,
416 }
417 }
418}
419
420/// JSON-RPC protocol handler for MCP communication
421// Note: ProtocolClient implementation moved to client/protocol.rs for better modularity
422/// MCP client for communicating with servers
423///
424/// The `Client` struct provides a beautiful, ergonomic interface for interacting with MCP servers.
425/// It handles all protocol complexity internally, exposing only clean, type-safe methods.
426///
427/// # Type Parameters
428///
429/// * `T` - The transport implementation used for communication
430///
431/// # Examples
432///
433/// ```rust,no_run
434/// use turbomcp_client::Client;
435/// use turbomcp_transport::stdio::StdioTransport;
436///
437/// # async fn example() -> turbomcp_protocol::Result<()> {
438/// let transport = StdioTransport::new();
439/// let mut client = Client::new(transport);
440///
441/// // Initialize and start using the client
442/// client.initialize().await?;
443/// # Ok(())
444/// # }
445/// ```
446// Re-export Client from the core module
447pub use client::core::Client;
448
449// Thread-safe wrapper for sharing Client across async tasks
450//
451// This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
452// for concurrent access to MCP client functionality. It addresses the limitations
453// identified in PR feedback where Client requires `&mut self` for all operations
454// but needs to be shared across multiple async tasks.
455//
456// # Design Rationale
457//
458// All Client methods require `&mut self` because:
459// - MCP connections maintain state (initialized flag, connection status)
460// - Request correlation tracking for JSON-RPC requires mutation
461// - Handler and plugin registries need mutable access
462//
463// Note: SharedClient has been removed in v2 - Client is now directly cloneable via Arc
464
465// ----------------------------------------------------------------------------
466// Re-exports
467// ----------------------------------------------------------------------------
468
469#[doc = "Result of client initialization"]
470#[doc = ""]
471#[doc = "Contains information about the server and the negotiated capabilities"]
472#[doc = "after a successful initialization handshake."]
473pub use client::config::InitializeResult;
474
475// ServerCapabilities is now imported from turbomcp_protocol::types
476
477/// Connection configuration for the client
478#[derive(Debug, Clone)]
479pub struct ConnectionConfig {
480 /// Request timeout in milliseconds
481 pub timeout_ms: u64,
482
483 /// Maximum number of retry attempts
484 pub max_retries: u32,
485
486 /// Retry delay in milliseconds
487 pub retry_delay_ms: u64,
488
489 /// Keep-alive interval in milliseconds
490 pub keepalive_ms: u64,
491}
492
493impl Default for ConnectionConfig {
494 fn default() -> Self {
495 Self {
496 timeout_ms: 30_000, // 30 seconds
497 max_retries: 3, // 3 attempts
498 retry_delay_ms: 1_000, // 1 second
499 keepalive_ms: 60_000, // 60 seconds
500 }
501 }
502}
503
504/// Builder for configuring and creating MCP clients
505///
506/// Provides a fluent interface for configuring client options before creation.
507/// The enhanced builder pattern supports comprehensive configuration including:
508/// - Protocol capabilities
509/// - Plugin registration
510/// - Handler registration
511/// - Connection settings
512/// - Resilience configuration
513///
514/// # Examples
515///
516/// Basic usage:
517/// ```rust,no_run
518/// use turbomcp_client::ClientBuilder;
519/// use turbomcp_transport::stdio::StdioTransport;
520///
521/// # async fn example() -> turbomcp_protocol::Result<()> {
522/// let client = ClientBuilder::new()
523/// .with_tools(true)
524/// .with_prompts(true)
525/// .with_resources(false)
526/// .build(StdioTransport::new());
527/// # Ok(())
528/// # }
529/// ```
530///
531/// Advanced configuration:
532/// ```rust,no_run
533/// use turbomcp_client::{ClientBuilder, ConnectionConfig};
534/// use turbomcp_client::plugins::{MetricsPlugin, PluginConfig};
535/// use turbomcp_transport::stdio::StdioTransport;
536/// use std::sync::Arc;
537///
538/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
539/// let client = ClientBuilder::new()
540/// .with_tools(true)
541/// .with_prompts(true)
542/// .with_resources(true)
543/// .with_sampling(true)
544/// .with_connection_config(ConnectionConfig {
545/// timeout_ms: 60_000,
546/// max_retries: 5,
547/// retry_delay_ms: 2_000,
548/// keepalive_ms: 30_000,
549/// })
550/// .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
551/// .build(StdioTransport::new())
552/// .await?;
553/// # Ok(())
554/// # }
555/// ```
556#[derive(Debug, Default)]
557pub struct ClientBuilder {
558 capabilities: ClientCapabilities,
559 connection_config: ConnectionConfig,
560 plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>,
561 elicitation_handler: Option<Arc<dyn crate::handlers::ElicitationHandler>>,
562 log_handler: Option<Arc<dyn crate::handlers::LogHandler>>,
563 resource_update_handler: Option<Arc<dyn crate::handlers::ResourceUpdateHandler>>,
564 // Robustness configuration
565 enable_resilience: bool,
566 retry_config: Option<turbomcp_transport::resilience::RetryConfig>,
567 circuit_breaker_config: Option<turbomcp_transport::resilience::CircuitBreakerConfig>,
568 health_check_config: Option<turbomcp_transport::resilience::HealthCheckConfig>,
569}
570
571// Default implementation is now derived
572
573impl ClientBuilder {
574 /// Create a new client builder
575 ///
576 /// Returns a new builder with default configuration.
577 #[must_use]
578 pub fn new() -> Self {
579 Self::default()
580 }
581
582 // ============================================================================
583 // CAPABILITY CONFIGURATION
584 // ============================================================================
585
586 /// Enable or disable tool support
587 ///
588 /// # Arguments
589 ///
590 /// * `enabled` - Whether to enable tool support
591 #[must_use]
592 pub fn with_tools(mut self, enabled: bool) -> Self {
593 self.capabilities.tools = enabled;
594 self
595 }
596
597 /// Enable or disable prompt support
598 ///
599 /// # Arguments
600 ///
601 /// * `enabled` - Whether to enable prompt support
602 #[must_use]
603 pub fn with_prompts(mut self, enabled: bool) -> Self {
604 self.capabilities.prompts = enabled;
605 self
606 }
607
608 /// Enable or disable resource support
609 ///
610 /// # Arguments
611 ///
612 /// * `enabled` - Whether to enable resource support
613 #[must_use]
614 pub fn with_resources(mut self, enabled: bool) -> Self {
615 self.capabilities.resources = enabled;
616 self
617 }
618
619 /// Enable or disable sampling support
620 ///
621 /// # Arguments
622 ///
623 /// * `enabled` - Whether to enable sampling support
624 #[must_use]
625 pub fn with_sampling(mut self, enabled: bool) -> Self {
626 self.capabilities.sampling = enabled;
627 self
628 }
629
630 /// Configure all capabilities at once
631 ///
632 /// # Arguments
633 ///
634 /// * `capabilities` - The capabilities configuration
635 #[must_use]
636 pub fn with_capabilities(mut self, capabilities: ClientCapabilities) -> Self {
637 self.capabilities = capabilities;
638 self
639 }
640
641 // ============================================================================
642 // CONNECTION CONFIGURATION
643 // ============================================================================
644
645 /// Configure connection settings
646 ///
647 /// # Arguments
648 ///
649 /// * `config` - The connection configuration
650 #[must_use]
651 pub fn with_connection_config(mut self, config: ConnectionConfig) -> Self {
652 self.connection_config = config;
653 self
654 }
655
656 /// Set request timeout
657 ///
658 /// # Arguments
659 ///
660 /// * `timeout_ms` - Timeout in milliseconds
661 #[must_use]
662 pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
663 self.connection_config.timeout_ms = timeout_ms;
664 self
665 }
666
667 /// Set maximum retry attempts
668 ///
669 /// # Arguments
670 ///
671 /// * `max_retries` - Maximum number of retries
672 #[must_use]
673 pub fn with_max_retries(mut self, max_retries: u32) -> Self {
674 self.connection_config.max_retries = max_retries;
675 self
676 }
677
678 /// Set retry delay
679 ///
680 /// # Arguments
681 ///
682 /// * `delay_ms` - Retry delay in milliseconds
683 #[must_use]
684 pub fn with_retry_delay(mut self, delay_ms: u64) -> Self {
685 self.connection_config.retry_delay_ms = delay_ms;
686 self
687 }
688
689 /// Set keep-alive interval
690 ///
691 /// # Arguments
692 ///
693 /// * `interval_ms` - Keep-alive interval in milliseconds
694 #[must_use]
695 pub fn with_keepalive(mut self, interval_ms: u64) -> Self {
696 self.connection_config.keepalive_ms = interval_ms;
697 self
698 }
699
700 // ============================================================================
701 // ROBUSTNESS & RESILIENCE CONFIGURATION
702 // ============================================================================
703
704 /// Enable resilient transport with circuit breaker, retry, and health checking
705 ///
706 /// When enabled, the transport layer will automatically:
707 /// - Retry failed operations with exponential backoff
708 /// - Use circuit breaker pattern to prevent cascade failures
709 /// - Perform periodic health checks
710 /// - Deduplicate messages
711 ///
712 /// # Examples
713 ///
714 /// ```rust,no_run
715 /// use turbomcp_client::ClientBuilder;
716 /// use turbomcp_transport::stdio::StdioTransport;
717 ///
718 /// let client = ClientBuilder::new()
719 /// .enable_resilience()
720 /// .build(StdioTransport::new());
721 /// ```
722 #[must_use]
723 pub fn enable_resilience(mut self) -> Self {
724 self.enable_resilience = true;
725 self
726 }
727
728 /// Configure retry behavior for resilient transport
729 ///
730 /// # Arguments
731 ///
732 /// * `config` - Retry configuration
733 ///
734 /// # Examples
735 ///
736 /// ```rust,no_run
737 /// use turbomcp_client::ClientBuilder;
738 /// use turbomcp_transport::resilience::RetryConfig;
739 /// use turbomcp_transport::stdio::StdioTransport;
740 /// use std::time::Duration;
741 ///
742 /// let client = ClientBuilder::new()
743 /// .enable_resilience()
744 /// .with_retry_config(RetryConfig {
745 /// max_attempts: 5,
746 /// base_delay: Duration::from_millis(100),
747 /// max_delay: Duration::from_secs(30),
748 /// backoff_multiplier: 2.0,
749 /// jitter_factor: 0.1,
750 /// retry_on_connection_error: true,
751 /// retry_on_timeout: true,
752 /// custom_retry_conditions: Vec::new(),
753 /// })
754 /// .build(StdioTransport::new());
755 /// ```
756 #[must_use]
757 pub fn with_retry_config(
758 mut self,
759 config: turbomcp_transport::resilience::RetryConfig,
760 ) -> Self {
761 self.retry_config = Some(config);
762 self.enable_resilience = true; // Auto-enable resilience
763 self
764 }
765
766 /// Configure circuit breaker for resilient transport
767 ///
768 /// # Arguments
769 ///
770 /// * `config` - Circuit breaker configuration
771 ///
772 /// # Examples
773 ///
774 /// ```rust,no_run
775 /// use turbomcp_client::ClientBuilder;
776 /// use turbomcp_transport::resilience::CircuitBreakerConfig;
777 /// use turbomcp_transport::stdio::StdioTransport;
778 /// use std::time::Duration;
779 ///
780 /// let client = ClientBuilder::new()
781 /// .enable_resilience()
782 /// .with_circuit_breaker_config(CircuitBreakerConfig {
783 /// failure_threshold: 5,
784 /// success_threshold: 2,
785 /// timeout: Duration::from_secs(60),
786 /// rolling_window_size: 100,
787 /// minimum_requests: 10,
788 /// })
789 /// .build(StdioTransport::new());
790 /// ```
791 #[must_use]
792 pub fn with_circuit_breaker_config(
793 mut self,
794 config: turbomcp_transport::resilience::CircuitBreakerConfig,
795 ) -> Self {
796 self.circuit_breaker_config = Some(config);
797 self.enable_resilience = true; // Auto-enable resilience
798 self
799 }
800
801 /// Configure health checking for resilient transport
802 ///
803 /// # Arguments
804 ///
805 /// * `config` - Health check configuration
806 ///
807 /// # Examples
808 ///
809 /// ```rust,no_run
810 /// use turbomcp_client::ClientBuilder;
811 /// use turbomcp_transport::resilience::HealthCheckConfig;
812 /// use turbomcp_transport::stdio::StdioTransport;
813 /// use std::time::Duration;
814 ///
815 /// let client = ClientBuilder::new()
816 /// .enable_resilience()
817 /// .with_health_check_config(HealthCheckConfig {
818 /// interval: Duration::from_secs(30),
819 /// timeout: Duration::from_secs(5),
820 /// failure_threshold: 3,
821 /// success_threshold: 1,
822 /// custom_check: None,
823 /// })
824 /// .build(StdioTransport::new());
825 /// ```
826 #[must_use]
827 pub fn with_health_check_config(
828 mut self,
829 config: turbomcp_transport::resilience::HealthCheckConfig,
830 ) -> Self {
831 self.health_check_config = Some(config);
832 self.enable_resilience = true; // Auto-enable resilience
833 self
834 }
835
836 // ============================================================================
837 // PLUGIN SYSTEM CONFIGURATION
838 // ============================================================================
839
840 /// Register a plugin with the client
841 ///
842 /// Plugins provide middleware functionality for request/response processing,
843 /// metrics collection, retry logic, caching, and other cross-cutting concerns.
844 ///
845 /// # Arguments
846 ///
847 /// * `plugin` - The plugin implementation
848 ///
849 /// # Examples
850 ///
851 /// ```rust,no_run
852 /// use turbomcp_client::{ClientBuilder, ConnectionConfig};
853 /// use turbomcp_client::plugins::{MetricsPlugin, RetryPlugin, PluginConfig, RetryConfig};
854 /// use std::sync::Arc;
855 ///
856 /// let client = ClientBuilder::new()
857 /// .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
858 /// .with_plugin(Arc::new(RetryPlugin::new(PluginConfig::Retry(RetryConfig {
859 /// max_retries: 5,
860 /// base_delay_ms: 1000,
861 /// max_delay_ms: 30000,
862 /// backoff_multiplier: 2.0,
863 /// retry_on_timeout: true,
864 /// retry_on_connection_error: true,
865 /// }))));
866 /// ```
867 pub fn with_plugin(mut self, plugin: Arc<dyn crate::plugins::ClientPlugin>) -> Self {
868 self.plugins.push(plugin);
869 self
870 }
871
872 /// Register multiple plugins at once
873 ///
874 /// # Arguments
875 ///
876 /// * `plugins` - Vector of plugin implementations
877 #[must_use]
878 pub fn with_plugins(mut self, plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>) -> Self {
879 self.plugins.extend(plugins);
880 self
881 }
882
883 // ============================================================================
884 // HANDLER REGISTRATION
885 // ============================================================================
886
887 /// Register an elicitation handler for processing user input requests
888 ///
889 /// # Arguments
890 ///
891 /// * `handler` - The elicitation handler implementation
892 pub fn with_elicitation_handler(
893 mut self,
894 handler: Arc<dyn crate::handlers::ElicitationHandler>,
895 ) -> Self {
896 self.elicitation_handler = Some(handler);
897 self
898 }
899
900 /// Register a log handler for processing server log messages
901 ///
902 /// # Arguments
903 ///
904 /// * `handler` - The log handler implementation
905 pub fn with_log_handler(mut self, handler: Arc<dyn crate::handlers::LogHandler>) -> Self {
906 self.log_handler = Some(handler);
907 self
908 }
909
910 /// Register a resource update handler for processing resource change notifications
911 ///
912 /// # Arguments
913 ///
914 /// * `handler` - The resource update handler implementation
915 pub fn with_resource_update_handler(
916 mut self,
917 handler: Arc<dyn crate::handlers::ResourceUpdateHandler>,
918 ) -> Self {
919 self.resource_update_handler = Some(handler);
920 self
921 }
922
923 // ============================================================================
924 // BUILD METHODS
925 // ============================================================================
926
927 /// Build a client with the configured options
928 ///
929 /// Creates a new client instance with all the configured options. The client
930 /// will be initialized with the registered plugins, handlers, and providers.
931 ///
932 /// # Arguments
933 ///
934 /// * `transport` - The transport to use for the client
935 ///
936 /// # Returns
937 ///
938 /// Returns a configured `Client` instance wrapped in a Result for async setup.
939 ///
940 /// # Examples
941 ///
942 /// ```rust,no_run
943 /// use turbomcp_client::ClientBuilder;
944 /// use turbomcp_transport::stdio::StdioTransport;
945 ///
946 /// # async fn example() -> turbomcp_protocol::Result<()> {
947 /// let client = ClientBuilder::new()
948 /// .with_tools(true)
949 /// .with_prompts(true)
950 /// .build(StdioTransport::new())
951 /// .await?;
952 /// # Ok(())
953 /// # }
954 /// ```
955 pub async fn build<T: Transport + 'static>(self, transport: T) -> Result<Client<T>> {
956 // Create base client with capabilities
957 let client = Client::with_capabilities(transport, self.capabilities);
958
959 // Register handlers
960 if let Some(handler) = self.elicitation_handler {
961 client.set_elicitation_handler(handler);
962 }
963 if let Some(handler) = self.log_handler {
964 client.set_log_handler(handler);
965 }
966 if let Some(handler) = self.resource_update_handler {
967 client.set_resource_update_handler(handler);
968 }
969
970 // Apply connection configuration (store for future use in actual connections)
971 // Note: The current Client doesn't expose connection config setters,
972 // so we'll store this for when the transport supports it
973
974 // Register plugins with the client
975 let has_plugins = !self.plugins.is_empty();
976 for plugin in self.plugins {
977 client.register_plugin(plugin).await?;
978 }
979
980 // Initialize plugins after registration
981 if has_plugins {
982 client.initialize_plugins().await?;
983 }
984
985 Ok(client)
986 }
987
988 /// Build a client with resilient transport (circuit breaker, retry, health checking)
989 ///
990 /// When resilience features are enabled via `enable_resilience()` or any resilience
991 /// configuration method, this wraps the transport in a `TurboTransport` that provides:
992 /// - Automatic retry with exponential backoff
993 /// - Circuit breaker pattern for fast failure
994 /// - Health checking and monitoring
995 /// - Message deduplication
996 ///
997 /// # Arguments
998 ///
999 /// * `transport` - The base transport to wrap with resilience features
1000 ///
1001 /// # Returns
1002 ///
1003 /// Returns a configured `Client<TurboTransport>` instance.
1004 ///
1005 /// # Errors
1006 ///
1007 /// Returns an error if plugin initialization fails.
1008 ///
1009 /// # Examples
1010 ///
1011 /// ```rust,no_run
1012 /// use turbomcp_client::ClientBuilder;
1013 /// use turbomcp_transport::stdio::StdioTransport;
1014 /// use turbomcp_transport::resilience::{RetryConfig, CircuitBreakerConfig, HealthCheckConfig};
1015 /// use std::time::Duration;
1016 ///
1017 /// # async fn example() -> turbomcp_protocol::Result<()> {
1018 /// let client = ClientBuilder::new()
1019 /// .with_retry_config(RetryConfig {
1020 /// max_attempts: 5,
1021 /// base_delay: Duration::from_millis(200),
1022 /// ..Default::default()
1023 /// })
1024 /// .with_circuit_breaker_config(CircuitBreakerConfig {
1025 /// failure_threshold: 3,
1026 /// timeout: Duration::from_secs(30),
1027 /// ..Default::default()
1028 /// })
1029 /// .with_health_check_config(HealthCheckConfig {
1030 /// interval: Duration::from_secs(15),
1031 /// timeout: Duration::from_secs(5),
1032 /// ..Default::default()
1033 /// })
1034 /// .build_resilient(StdioTransport::new())
1035 /// .await?;
1036 /// # Ok(())
1037 /// # }
1038 /// ```
1039 pub async fn build_resilient<T: Transport + 'static>(
1040 self,
1041 transport: T,
1042 ) -> Result<Client<turbomcp_transport::resilience::TurboTransport>> {
1043 use turbomcp_transport::resilience::TurboTransport;
1044
1045 // Get configurations or use defaults
1046 let retry_config = self.retry_config.unwrap_or_default();
1047 let circuit_config = self.circuit_breaker_config.unwrap_or_default();
1048 let health_config = self.health_check_config.unwrap_or_default();
1049
1050 // Wrap transport in TurboTransport
1051 let robust_transport = TurboTransport::new(
1052 Box::new(transport),
1053 retry_config,
1054 circuit_config,
1055 health_config,
1056 );
1057
1058 // Create client with resilient transport
1059 let client = Client::with_capabilities(robust_transport, self.capabilities);
1060
1061 // Register handlers
1062 if let Some(handler) = self.elicitation_handler {
1063 client.set_elicitation_handler(handler);
1064 }
1065 if let Some(handler) = self.log_handler {
1066 client.set_log_handler(handler);
1067 }
1068 if let Some(handler) = self.resource_update_handler {
1069 client.set_resource_update_handler(handler);
1070 }
1071
1072 // Register plugins
1073 let has_plugins = !self.plugins.is_empty();
1074 for plugin in self.plugins {
1075 client.register_plugin(plugin).await?;
1076 }
1077
1078 if has_plugins {
1079 client.initialize_plugins().await?;
1080 }
1081
1082 Ok(client)
1083 }
1084
1085 /// Build a client synchronously with basic configuration only
1086 ///
1087 /// This is a convenience method for simple use cases where no async setup
1088 /// is required. For advanced features like plugins, use `build()` instead.
1089 ///
1090 /// # Arguments
1091 ///
1092 /// * `transport` - The transport to use for the client
1093 ///
1094 /// # Returns
1095 ///
1096 /// Returns a configured `Client` instance.
1097 ///
1098 /// # Examples
1099 ///
1100 /// ```rust,no_run
1101 /// use turbomcp_client::ClientBuilder;
1102 /// use turbomcp_transport::stdio::StdioTransport;
1103 ///
1104 /// let client = ClientBuilder::new()
1105 /// .with_tools(true)
1106 /// .build_sync(StdioTransport::new());
1107 /// ```
1108 pub fn build_sync<T: Transport + 'static>(self, transport: T) -> Client<T> {
1109 let client = Client::with_capabilities(transport, self.capabilities);
1110
1111 // Register synchronous handlers only
1112 if let Some(handler) = self.elicitation_handler {
1113 client.set_elicitation_handler(handler);
1114 }
1115 if let Some(handler) = self.log_handler {
1116 client.set_log_handler(handler);
1117 }
1118 if let Some(handler) = self.resource_update_handler {
1119 client.set_resource_update_handler(handler);
1120 }
1121
1122 client
1123 }
1124
1125 // ============================================================================
1126 // CONFIGURATION ACCESS
1127 // ============================================================================
1128
1129 /// Get the current capabilities configuration
1130 #[must_use]
1131 pub fn capabilities(&self) -> &ClientCapabilities {
1132 &self.capabilities
1133 }
1134
1135 /// Get the current connection configuration
1136 #[must_use]
1137 pub fn connection_config(&self) -> &ConnectionConfig {
1138 &self.connection_config
1139 }
1140
1141 /// Get the number of registered plugins
1142 #[must_use]
1143 pub fn plugin_count(&self) -> usize {
1144 self.plugins.len()
1145 }
1146
1147 /// Check if any handlers are registered
1148 #[must_use]
1149 pub fn has_handlers(&self) -> bool {
1150 self.elicitation_handler.is_some()
1151 || self.log_handler.is_some()
1152 || self.resource_update_handler.is_some()
1153 }
1154}
1155
1156// Re-export types for public API
1157pub use turbomcp_protocol::types::ServerCapabilities as PublicServerCapabilities;