turbomcp_client/lib.rs
1//! # `TurboMCP` Client
2//!
3//! MCP (Model Context Protocol) client implementation for connecting to MCP servers
4//! and consuming their capabilities (tools, prompts, resources, and sampling).
5//!
6//! ## Features
7//!
8//! - Connection management with automatic reconnection
9//! - Error handling and recovery mechanisms
10//! - Support for all MCP capabilities including bidirectional sampling
11//! - Elicitation response handling for server-initiated user input requests
12//! - Transport-agnostic design (works with any `Transport` implementation)
13//! - Type-safe protocol communication
14//! - Request/response correlation tracking
15//! - Timeout and cancellation support
16//! - Automatic capability negotiation
17//! - Handler support for server-initiated requests (sampling and elicitation)
18//!
19//! ## Architecture
20//!
21//! The client follows a layered architecture:
22//!
23//! ```text
24//! Application Layer
25//! ↓
26//! Client API (this crate)
27//! ↓
28//! Protocol Layer (turbomcp-protocol)
29//! ↓
30//! Transport Layer (turbomcp-transport)
31//! ```
32//!
33//! ## Usage
34//!
35//! ```rust,no_run
36//! use turbomcp_client::{Client, ClientBuilder};
37//! use turbomcp_transport::stdio::StdioTransport;
38//!
39//! # async fn example() -> turbomcp_protocol::Result<()> {
40//! // Create a client with stdio transport
41//! let transport = StdioTransport::new();
42//! let mut client = Client::new(transport);
43//!
44//! // Initialize connection and negotiate capabilities
45//! let result = client.initialize().await?;
46//! println!("Connected to: {}", result.server_info.name);
47//!
48//! // List and call tools
49//! let tools = client.list_tools().await?;
50//! for tool in tools {
51//! println!("Tool: {} - {}", tool.name, tool.description.as_deref().unwrap_or("No description"));
52//! }
53//!
54//! // Access resources
55//! let resources = client.list_resources().await?;
56//! for resource in resources {
57//! println!("Resource: {}", resource);
58//! }
59//! # Ok(())
60//! # }
61//! ```
62//!
63//! ## Elicitation Response Handling (New in 1.0.3)
64//!
65//! The client now supports handling server-initiated elicitation requests:
66//!
67//! ```rust,no_run
68//! use turbomcp_client::Client;
69//! use std::collections::HashMap;
70//!
71//! // Simple elicitation handling example
72//! async fn handle_server_elicitation() {
73//! // When server requests user input, you would:
74//! // 1. Present the schema to the user
75//! // 2. Collect their input
76//! // 3. Send response back to server
77//!
78//! let user_preferences: HashMap<String, String> = HashMap::new();
79//! // Your UI/CLI interaction logic here
80//! println!("Server requesting user preferences");
81//! }
82//! ```
83//!
84//! ## Sampling Support (New in 1.0.3)
85//!
86//! Handle server-initiated sampling requests for LLM capabilities:
87//!
88//! ```rust,no_run
89//! use turbomcp_client::Client;
90//! use turbomcp_client::sampling::SamplingHandler;
91//! use turbomcp_protocol::types::{CreateMessageRequest, CreateMessageResult, Role, Content, StopReason, TextContent};
92//! use async_trait::async_trait;
93//!
94//! #[derive(Debug)]
95//! struct MySamplingHandler {
96//! // Your LLM client would go here
97//! }
98//!
99//! #[async_trait]
100//! impl SamplingHandler for MySamplingHandler {
101//! async fn handle_create_message(
102//! &self,
103//! request_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 // Progress (MCP 2025-06-18 spec compliant)
195 ProgressHandler,
196 ProgressNotification,
197 PromptListChangedHandler,
198 // List changed handlers (MCP 2025-06-18 spec compliant)
199 ResourceListChangedHandler,
200 // Resource updates (MCP 2025-06-18 spec compliant)
201 ResourceUpdateHandler,
202 ResourceUpdatedNotification,
203 // Roots
204 RootsHandler,
205 ToolListChangedHandler,
206};
207
208// Sampling types
209pub use sampling::{SamplingHandler, ServerInfo, UserInteractionHandler};
210
211// Plugin system
212pub use plugins::{
213 CachePlugin, ClientPlugin, MetricsPlugin, PluginConfig, PluginContext, PluginError,
214 PluginResult, RetryPlugin,
215};
216
217// Common protocol types
218pub use turbomcp_protocol::types::{
219 Content, LogLevel, Prompt, Resource, ResourceContents, Role, Tool,
220};
221
222// Transport re-exports (with feature gates)
223#[cfg(feature = "stdio")]
224pub use turbomcp_transport::stdio::StdioTransport;
225
226#[cfg(feature = "http")]
227pub use turbomcp_transport::streamable_http_client::{
228 RetryPolicy, StreamableHttpClientConfig, StreamableHttpClientTransport,
229};
230
231#[cfg(feature = "tcp")]
232pub use turbomcp_transport::tcp::{TcpTransport, TcpTransportBuilder};
233
234#[cfg(feature = "unix")]
235pub use turbomcp_transport::unix::{UnixTransport, UnixTransportBuilder};
236
237#[cfg(feature = "websocket")]
238pub use turbomcp_transport::websocket_bidirectional::{
239 WebSocketBidirectionalConfig, WebSocketBidirectionalTransport,
240};
241
242/// Client capability configuration
243///
244/// Defines the capabilities that this client supports when connecting to MCP servers.
245/// These capabilities are sent during the initialization handshake to negotiate
246/// which features will be available during the session.
247///
248/// # Examples
249///
250/// ```
251/// use turbomcp_client::ClientCapabilities;
252///
253/// let capabilities = ClientCapabilities {
254/// tools: true,
255/// prompts: true,
256/// resources: true,
257/// sampling: false,
258/// };
259/// ```
260#[derive(Debug, Clone, Default)]
261pub struct ClientCapabilities {
262 /// Whether the client supports tool calling
263 pub tools: bool,
264
265 /// Whether the client supports prompts
266 pub prompts: bool,
267
268 /// Whether the client supports resources
269 pub resources: bool,
270
271 /// Whether the client supports sampling
272 pub sampling: bool,
273}
274
275impl ClientCapabilities {
276 /// All capabilities enabled (tools, prompts, resources, sampling)
277 ///
278 /// This is the most comprehensive configuration, enabling full MCP protocol support.
279 ///
280 /// # Example
281 ///
282 /// ```rust
283 /// use turbomcp_client::ClientCapabilities;
284 ///
285 /// let capabilities = ClientCapabilities::all();
286 /// assert!(capabilities.tools);
287 /// assert!(capabilities.prompts);
288 /// assert!(capabilities.resources);
289 /// assert!(capabilities.sampling);
290 /// ```
291 pub fn all() -> Self {
292 Self {
293 tools: true,
294 prompts: true,
295 resources: true,
296 sampling: true,
297 }
298 }
299
300 /// Core capabilities without sampling (tools, prompts, resources)
301 ///
302 /// This is the recommended default for most applications. It enables
303 /// all standard MCP features except server-initiated sampling requests.
304 ///
305 /// # Example
306 ///
307 /// ```rust
308 /// use turbomcp_client::ClientCapabilities;
309 ///
310 /// let capabilities = ClientCapabilities::core();
311 /// assert!(capabilities.tools);
312 /// assert!(capabilities.prompts);
313 /// assert!(capabilities.resources);
314 /// assert!(!capabilities.sampling);
315 /// ```
316 pub fn core() -> Self {
317 Self {
318 tools: true,
319 prompts: true,
320 resources: true,
321 sampling: false,
322 }
323 }
324
325 /// Minimal capabilities (tools only)
326 ///
327 /// Use this for simple tool-calling clients that don't need prompts,
328 /// resources, or sampling support.
329 ///
330 /// # Example
331 ///
332 /// ```rust
333 /// use turbomcp_client::ClientCapabilities;
334 ///
335 /// let capabilities = ClientCapabilities::minimal();
336 /// assert!(capabilities.tools);
337 /// assert!(!capabilities.prompts);
338 /// assert!(!capabilities.resources);
339 /// assert!(!capabilities.sampling);
340 /// ```
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 pub fn only_tools() -> Self {
354 Self::minimal()
355 }
356
357 /// Only resources enabled
358 ///
359 /// Use this for resource-focused clients that don't need tools or prompts.
360 ///
361 /// # Example
362 ///
363 /// ```rust
364 /// use turbomcp_client::ClientCapabilities;
365 ///
366 /// let capabilities = ClientCapabilities::only_resources();
367 /// assert!(!capabilities.tools);
368 /// assert!(!capabilities.prompts);
369 /// assert!(capabilities.resources);
370 /// ```
371 pub fn only_resources() -> Self {
372 Self {
373 tools: false,
374 prompts: false,
375 resources: true,
376 sampling: false,
377 }
378 }
379
380 /// Only prompts enabled
381 ///
382 /// Use this for prompt-focused clients that don't need tools or resources.
383 ///
384 /// # Example
385 ///
386 /// ```rust
387 /// use turbomcp_client::ClientCapabilities;
388 ///
389 /// let capabilities = ClientCapabilities::only_prompts();
390 /// assert!(!capabilities.tools);
391 /// assert!(capabilities.prompts);
392 /// assert!(!capabilities.resources);
393 /// ```
394 pub fn only_prompts() -> Self {
395 Self {
396 tools: false,
397 prompts: true,
398 resources: false,
399 sampling: false,
400 }
401 }
402
403 /// Only sampling enabled
404 ///
405 /// Use this for clients that exclusively handle server-initiated sampling requests.
406 pub fn only_sampling() -> Self {
407 Self {
408 tools: false,
409 prompts: false,
410 resources: false,
411 sampling: true,
412 }
413 }
414}
415
416/// JSON-RPC protocol handler for MCP communication
417// Note: ProtocolClient implementation moved to client/protocol.rs for better modularity
418/// MCP client for communicating with servers
419///
420/// The `Client` struct provides a beautiful, ergonomic interface for interacting with MCP servers.
421/// It handles all protocol complexity internally, exposing only clean, type-safe methods.
422///
423/// # Type Parameters
424///
425/// * `T` - The transport implementation used for communication
426///
427/// # Examples
428///
429/// ```rust,no_run
430/// use turbomcp_client::Client;
431/// use turbomcp_transport::stdio::StdioTransport;
432///
433/// # async fn example() -> turbomcp_protocol::Result<()> {
434/// let transport = StdioTransport::new();
435/// let mut client = Client::new(transport);
436///
437/// // Initialize and start using the client
438/// client.initialize().await?;
439/// # Ok(())
440/// # }
441/// ```
442// Re-export Client from the core module
443pub use client::core::Client;
444
445// Thread-safe wrapper for sharing Client across async tasks
446//
447// This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
448// for concurrent access to MCP client functionality. It addresses the limitations
449// identified in PR feedback where Client requires `&mut self` for all operations
450// but needs to be shared across multiple async tasks.
451//
452// # Design Rationale
453//
454// All Client methods require `&mut self` because:
455// - MCP connections maintain state (initialized flag, connection status)
456// - Request correlation tracking for JSON-RPC requires mutation
457// - Handler and plugin registries need mutable access
458//
459// Note: SharedClient has been removed in v2 - Client is now directly cloneable via Arc
460
461// ----------------------------------------------------------------------------
462// Re-exports
463// ----------------------------------------------------------------------------
464
465#[doc = "Result of client initialization"]
466#[doc = ""]
467#[doc = "Contains information about the server and the negotiated capabilities"]
468#[doc = "after a successful initialization handshake."]
469pub use client::config::InitializeResult;
470
471// ServerCapabilities is now imported from turbomcp_protocol::types
472
473/// Connection configuration for the client
474#[derive(Debug, Clone)]
475pub struct ConnectionConfig {
476 /// Request timeout in milliseconds
477 pub timeout_ms: u64,
478
479 /// Maximum number of retry attempts
480 pub max_retries: u32,
481
482 /// Retry delay in milliseconds
483 pub retry_delay_ms: u64,
484
485 /// Keep-alive interval in milliseconds
486 pub keepalive_ms: u64,
487}
488
489impl Default for ConnectionConfig {
490 fn default() -> Self {
491 Self {
492 timeout_ms: 30_000, // 30 seconds
493 max_retries: 3, // 3 attempts
494 retry_delay_ms: 1_000, // 1 second
495 keepalive_ms: 60_000, // 60 seconds
496 }
497 }
498}
499
500/// Builder for configuring and creating MCP clients
501///
502/// Provides a fluent interface for configuring client options before creation.
503/// The enhanced builder pattern supports comprehensive configuration including:
504/// - Protocol capabilities
505/// - Plugin registration
506/// - Handler registration
507/// - Connection settings
508/// - Resilience configuration
509///
510/// # Examples
511///
512/// Basic usage:
513/// ```rust,no_run
514/// use turbomcp_client::ClientBuilder;
515/// use turbomcp_transport::stdio::StdioTransport;
516///
517/// # async fn example() -> turbomcp_protocol::Result<()> {
518/// let client = ClientBuilder::new()
519/// .with_tools(true)
520/// .with_prompts(true)
521/// .with_resources(false)
522/// .build(StdioTransport::new());
523/// # Ok(())
524/// # }
525/// ```
526///
527/// Advanced configuration:
528/// ```rust,no_run
529/// use turbomcp_client::{ClientBuilder, ConnectionConfig};
530/// use turbomcp_client::plugins::{MetricsPlugin, PluginConfig};
531/// use turbomcp_transport::stdio::StdioTransport;
532/// use std::sync::Arc;
533///
534/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
535/// let client = ClientBuilder::new()
536/// .with_tools(true)
537/// .with_prompts(true)
538/// .with_resources(true)
539/// .with_sampling(true)
540/// .with_connection_config(ConnectionConfig {
541/// timeout_ms: 60_000,
542/// max_retries: 5,
543/// retry_delay_ms: 2_000,
544/// keepalive_ms: 30_000,
545/// })
546/// .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
547/// .build(StdioTransport::new())
548/// .await?;
549/// # Ok(())
550/// # }
551/// ```
552#[derive(Debug, Default)]
553pub struct ClientBuilder {
554 capabilities: ClientCapabilities,
555 connection_config: ConnectionConfig,
556 plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>,
557 elicitation_handler: Option<Arc<dyn crate::handlers::ElicitationHandler>>,
558 progress_handler: Option<Arc<dyn crate::handlers::ProgressHandler>>,
559 log_handler: Option<Arc<dyn crate::handlers::LogHandler>>,
560 resource_update_handler: Option<Arc<dyn crate::handlers::ResourceUpdateHandler>>,
561 // Robustness configuration
562 enable_resilience: bool,
563 retry_config: Option<turbomcp_transport::resilience::RetryConfig>,
564 circuit_breaker_config: Option<turbomcp_transport::resilience::CircuitBreakerConfig>,
565 health_check_config: Option<turbomcp_transport::resilience::HealthCheckConfig>,
566}
567
568// Default implementation is now derived
569
570impl ClientBuilder {
571 /// Create a new client builder
572 ///
573 /// Returns a new builder with default configuration.
574 pub fn new() -> Self {
575 Self::default()
576 }
577
578 // ============================================================================
579 // CAPABILITY CONFIGURATION
580 // ============================================================================
581
582 /// Enable or disable tool support
583 ///
584 /// # Arguments
585 ///
586 /// * `enabled` - Whether to enable tool support
587 pub fn with_tools(mut self, enabled: bool) -> Self {
588 self.capabilities.tools = enabled;
589 self
590 }
591
592 /// Enable or disable prompt support
593 ///
594 /// # Arguments
595 ///
596 /// * `enabled` - Whether to enable prompt support
597 pub fn with_prompts(mut self, enabled: bool) -> Self {
598 self.capabilities.prompts = enabled;
599 self
600 }
601
602 /// Enable or disable resource support
603 ///
604 /// # Arguments
605 ///
606 /// * `enabled` - Whether to enable resource support
607 pub fn with_resources(mut self, enabled: bool) -> Self {
608 self.capabilities.resources = enabled;
609 self
610 }
611
612 /// Enable or disable sampling support
613 ///
614 /// # Arguments
615 ///
616 /// * `enabled` - Whether to enable sampling support
617 pub fn with_sampling(mut self, enabled: bool) -> Self {
618 self.capabilities.sampling = enabled;
619 self
620 }
621
622 /// Configure all capabilities at once
623 ///
624 /// # Arguments
625 ///
626 /// * `capabilities` - The capabilities configuration
627 pub fn with_capabilities(mut self, capabilities: ClientCapabilities) -> Self {
628 self.capabilities = capabilities;
629 self
630 }
631
632 // ============================================================================
633 // CONNECTION CONFIGURATION
634 // ============================================================================
635
636 /// Configure connection settings
637 ///
638 /// # Arguments
639 ///
640 /// * `config` - The connection configuration
641 pub fn with_connection_config(mut self, config: ConnectionConfig) -> Self {
642 self.connection_config = config;
643 self
644 }
645
646 /// Set request timeout
647 ///
648 /// # Arguments
649 ///
650 /// * `timeout_ms` - Timeout in milliseconds
651 pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
652 self.connection_config.timeout_ms = timeout_ms;
653 self
654 }
655
656 /// Set maximum retry attempts
657 ///
658 /// # Arguments
659 ///
660 /// * `max_retries` - Maximum number of retries
661 pub fn with_max_retries(mut self, max_retries: u32) -> Self {
662 self.connection_config.max_retries = max_retries;
663 self
664 }
665
666 /// Set retry delay
667 ///
668 /// # Arguments
669 ///
670 /// * `delay_ms` - Retry delay in milliseconds
671 pub fn with_retry_delay(mut self, delay_ms: u64) -> Self {
672 self.connection_config.retry_delay_ms = delay_ms;
673 self
674 }
675
676 /// Set keep-alive interval
677 ///
678 /// # Arguments
679 ///
680 /// * `interval_ms` - Keep-alive interval in milliseconds
681 pub fn with_keepalive(mut self, interval_ms: u64) -> Self {
682 self.connection_config.keepalive_ms = interval_ms;
683 self
684 }
685
686 // ============================================================================
687 // ROBUSTNESS & RESILIENCE CONFIGURATION
688 // ============================================================================
689
690 /// Enable resilient transport with circuit breaker, retry, and health checking
691 ///
692 /// When enabled, the transport layer will automatically:
693 /// - Retry failed operations with exponential backoff
694 /// - Use circuit breaker pattern to prevent cascade failures
695 /// - Perform periodic health checks
696 /// - Deduplicate messages
697 ///
698 /// # Examples
699 ///
700 /// ```rust,no_run
701 /// use turbomcp_client::ClientBuilder;
702 /// use turbomcp_transport::stdio::StdioTransport;
703 ///
704 /// let client = ClientBuilder::new()
705 /// .enable_resilience()
706 /// .build(StdioTransport::new());
707 /// ```
708 pub fn enable_resilience(mut self) -> Self {
709 self.enable_resilience = true;
710 self
711 }
712
713 /// Configure retry behavior for resilient transport
714 ///
715 /// # Arguments
716 ///
717 /// * `config` - Retry configuration
718 ///
719 /// # Examples
720 ///
721 /// ```rust,no_run
722 /// use turbomcp_client::ClientBuilder;
723 /// use turbomcp_transport::resilience::RetryConfig;
724 /// use turbomcp_transport::stdio::StdioTransport;
725 /// use std::time::Duration;
726 ///
727 /// let client = ClientBuilder::new()
728 /// .enable_resilience()
729 /// .with_retry_config(RetryConfig {
730 /// max_attempts: 5,
731 /// base_delay: Duration::from_millis(100),
732 /// max_delay: Duration::from_secs(30),
733 /// backoff_multiplier: 2.0,
734 /// jitter_factor: 0.1,
735 /// retry_on_connection_error: true,
736 /// retry_on_timeout: true,
737 /// custom_retry_conditions: Vec::new(),
738 /// })
739 /// .build(StdioTransport::new());
740 /// ```
741 pub fn with_retry_config(
742 mut self,
743 config: turbomcp_transport::resilience::RetryConfig,
744 ) -> Self {
745 self.retry_config = Some(config);
746 self.enable_resilience = true; // Auto-enable resilience
747 self
748 }
749
750 /// Configure circuit breaker for resilient transport
751 ///
752 /// # Arguments
753 ///
754 /// * `config` - Circuit breaker configuration
755 ///
756 /// # Examples
757 ///
758 /// ```rust,no_run
759 /// use turbomcp_client::ClientBuilder;
760 /// use turbomcp_transport::resilience::CircuitBreakerConfig;
761 /// use turbomcp_transport::stdio::StdioTransport;
762 /// use std::time::Duration;
763 ///
764 /// let client = ClientBuilder::new()
765 /// .enable_resilience()
766 /// .with_circuit_breaker_config(CircuitBreakerConfig {
767 /// failure_threshold: 5,
768 /// success_threshold: 2,
769 /// timeout: Duration::from_secs(60),
770 /// rolling_window_size: 100,
771 /// minimum_requests: 10,
772 /// })
773 /// .build(StdioTransport::new());
774 /// ```
775 pub fn with_circuit_breaker_config(
776 mut self,
777 config: turbomcp_transport::resilience::CircuitBreakerConfig,
778 ) -> Self {
779 self.circuit_breaker_config = Some(config);
780 self.enable_resilience = true; // Auto-enable resilience
781 self
782 }
783
784 /// Configure health checking for resilient transport
785 ///
786 /// # Arguments
787 ///
788 /// * `config` - Health check configuration
789 ///
790 /// # Examples
791 ///
792 /// ```rust,no_run
793 /// use turbomcp_client::ClientBuilder;
794 /// use turbomcp_transport::resilience::HealthCheckConfig;
795 /// use turbomcp_transport::stdio::StdioTransport;
796 /// use std::time::Duration;
797 ///
798 /// let client = ClientBuilder::new()
799 /// .enable_resilience()
800 /// .with_health_check_config(HealthCheckConfig {
801 /// interval: Duration::from_secs(30),
802 /// timeout: Duration::from_secs(5),
803 /// failure_threshold: 3,
804 /// success_threshold: 1,
805 /// custom_check: None,
806 /// })
807 /// .build(StdioTransport::new());
808 /// ```
809 pub fn with_health_check_config(
810 mut self,
811 config: turbomcp_transport::resilience::HealthCheckConfig,
812 ) -> Self {
813 self.health_check_config = Some(config);
814 self.enable_resilience = true; // Auto-enable resilience
815 self
816 }
817
818 // ============================================================================
819 // PLUGIN SYSTEM CONFIGURATION
820 // ============================================================================
821
822 /// Register a plugin with the client
823 ///
824 /// Plugins provide middleware functionality for request/response processing,
825 /// metrics collection, retry logic, caching, and other cross-cutting concerns.
826 ///
827 /// # Arguments
828 ///
829 /// * `plugin` - The plugin implementation
830 ///
831 /// # Examples
832 ///
833 /// ```rust,no_run
834 /// use turbomcp_client::{ClientBuilder, ConnectionConfig};
835 /// use turbomcp_client::plugins::{MetricsPlugin, RetryPlugin, PluginConfig, RetryConfig};
836 /// use std::sync::Arc;
837 ///
838 /// let client = ClientBuilder::new()
839 /// .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
840 /// .with_plugin(Arc::new(RetryPlugin::new(PluginConfig::Retry(RetryConfig {
841 /// max_retries: 5,
842 /// base_delay_ms: 1000,
843 /// max_delay_ms: 30000,
844 /// backoff_multiplier: 2.0,
845 /// retry_on_timeout: true,
846 /// retry_on_connection_error: true,
847 /// }))));
848 /// ```
849 pub fn with_plugin(mut self, plugin: Arc<dyn crate::plugins::ClientPlugin>) -> Self {
850 self.plugins.push(plugin);
851 self
852 }
853
854 /// Register multiple plugins at once
855 ///
856 /// # Arguments
857 ///
858 /// * `plugins` - Vector of plugin implementations
859 pub fn with_plugins(mut self, plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>) -> Self {
860 self.plugins.extend(plugins);
861 self
862 }
863
864 // ============================================================================
865 // HANDLER REGISTRATION
866 // ============================================================================
867
868 /// Register an elicitation handler for processing user input requests
869 ///
870 /// # Arguments
871 ///
872 /// * `handler` - The elicitation handler implementation
873 pub fn with_elicitation_handler(
874 mut self,
875 handler: Arc<dyn crate::handlers::ElicitationHandler>,
876 ) -> Self {
877 self.elicitation_handler = Some(handler);
878 self
879 }
880
881 /// Register a progress handler for processing operation progress updates
882 ///
883 /// # Arguments
884 ///
885 /// * `handler` - The progress handler implementation
886 pub fn with_progress_handler(
887 mut self,
888 handler: Arc<dyn crate::handlers::ProgressHandler>,
889 ) -> Self {
890 self.progress_handler = Some(handler);
891 self
892 }
893
894 /// Register a log handler for processing server log messages
895 ///
896 /// # Arguments
897 ///
898 /// * `handler` - The log handler implementation
899 pub fn with_log_handler(mut self, handler: Arc<dyn crate::handlers::LogHandler>) -> Self {
900 self.log_handler = Some(handler);
901 self
902 }
903
904 /// Register a resource update handler for processing resource change notifications
905 ///
906 /// # Arguments
907 ///
908 /// * `handler` - The resource update handler implementation
909 pub fn with_resource_update_handler(
910 mut self,
911 handler: Arc<dyn crate::handlers::ResourceUpdateHandler>,
912 ) -> Self {
913 self.resource_update_handler = Some(handler);
914 self
915 }
916
917 // ============================================================================
918 // BUILD METHODS
919 // ============================================================================
920
921 /// Build a client with the configured options
922 ///
923 /// Creates a new client instance with all the configured options. The client
924 /// will be initialized with the registered plugins, handlers, and providers.
925 ///
926 /// # Arguments
927 ///
928 /// * `transport` - The transport to use for the client
929 ///
930 /// # Returns
931 ///
932 /// Returns a configured `Client` instance wrapped in a Result for async setup.
933 ///
934 /// # Examples
935 ///
936 /// ```rust,no_run
937 /// use turbomcp_client::ClientBuilder;
938 /// use turbomcp_transport::stdio::StdioTransport;
939 ///
940 /// # async fn example() -> turbomcp_protocol::Result<()> {
941 /// let client = ClientBuilder::new()
942 /// .with_tools(true)
943 /// .with_prompts(true)
944 /// .build(StdioTransport::new())
945 /// .await?;
946 /// # Ok(())
947 /// # }
948 /// ```
949 pub async fn build<T: Transport + 'static>(self, transport: T) -> Result<Client<T>> {
950 // Create base client with capabilities
951 let client = Client::with_capabilities(transport, self.capabilities);
952
953 // Register handlers
954 if let Some(handler) = self.elicitation_handler {
955 client.set_elicitation_handler(handler);
956 }
957 if let Some(handler) = self.progress_handler {
958 client.set_progress_handler(handler);
959 }
960 if let Some(handler) = self.log_handler {
961 client.set_log_handler(handler);
962 }
963 if let Some(handler) = self.resource_update_handler {
964 client.set_resource_update_handler(handler);
965 }
966
967 // Apply connection configuration (store for future use in actual connections)
968 // Note: The current Client doesn't expose connection config setters,
969 // so we'll store this for when the transport supports it
970
971 // Register plugins with the client
972 let has_plugins = !self.plugins.is_empty();
973 for plugin in self.plugins {
974 client.register_plugin(plugin).await?;
975 }
976
977 // Initialize plugins after registration
978 if has_plugins {
979 client.initialize_plugins().await?;
980 }
981
982 Ok(client)
983 }
984
985 /// Build a client with resilient transport (circuit breaker, retry, health checking)
986 ///
987 /// When resilience features are enabled via `enable_resilience()` or any resilience
988 /// configuration method, this wraps the transport in a `TurboTransport` that provides:
989 /// - Automatic retry with exponential backoff
990 /// - Circuit breaker pattern for fast failure
991 /// - Health checking and monitoring
992 /// - Message deduplication
993 ///
994 /// # Arguments
995 ///
996 /// * `transport` - The base transport to wrap with resilience features
997 ///
998 /// # Returns
999 ///
1000 /// Returns a configured `Client<TurboTransport>` instance.
1001 ///
1002 /// # Errors
1003 ///
1004 /// Returns an error if plugin initialization fails.
1005 ///
1006 /// # Examples
1007 ///
1008 /// ```rust,no_run
1009 /// use turbomcp_client::ClientBuilder;
1010 /// use turbomcp_transport::stdio::StdioTransport;
1011 /// use turbomcp_transport::resilience::{RetryConfig, CircuitBreakerConfig, HealthCheckConfig};
1012 /// use std::time::Duration;
1013 ///
1014 /// # async fn example() -> turbomcp_protocol::Result<()> {
1015 /// let client = ClientBuilder::new()
1016 /// .with_retry_config(RetryConfig {
1017 /// max_attempts: 5,
1018 /// base_delay: Duration::from_millis(200),
1019 /// ..Default::default()
1020 /// })
1021 /// .with_circuit_breaker_config(CircuitBreakerConfig {
1022 /// failure_threshold: 3,
1023 /// timeout: Duration::from_secs(30),
1024 /// ..Default::default()
1025 /// })
1026 /// .with_health_check_config(HealthCheckConfig {
1027 /// interval: Duration::from_secs(15),
1028 /// timeout: Duration::from_secs(5),
1029 /// ..Default::default()
1030 /// })
1031 /// .build_resilient(StdioTransport::new())
1032 /// .await?;
1033 /// # Ok(())
1034 /// # }
1035 /// ```
1036 pub async fn build_resilient<T: Transport + 'static>(
1037 self,
1038 transport: T,
1039 ) -> Result<Client<turbomcp_transport::resilience::TurboTransport>> {
1040 use turbomcp_transport::resilience::TurboTransport;
1041
1042 // Get configurations or use defaults
1043 let retry_config = self.retry_config.unwrap_or_default();
1044 let circuit_config = self.circuit_breaker_config.unwrap_or_default();
1045 let health_config = self.health_check_config.unwrap_or_default();
1046
1047 // Wrap transport in TurboTransport
1048 let robust_transport = TurboTransport::new(
1049 Box::new(transport),
1050 retry_config,
1051 circuit_config,
1052 health_config,
1053 );
1054
1055 // Create client with resilient transport
1056 let client = Client::with_capabilities(robust_transport, self.capabilities);
1057
1058 // Register handlers
1059 if let Some(handler) = self.elicitation_handler {
1060 client.set_elicitation_handler(handler);
1061 }
1062 if let Some(handler) = self.progress_handler {
1063 client.set_progress_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.progress_handler {
1116 client.set_progress_handler(handler);
1117 }
1118 if let Some(handler) = self.log_handler {
1119 client.set_log_handler(handler);
1120 }
1121 if let Some(handler) = self.resource_update_handler {
1122 client.set_resource_update_handler(handler);
1123 }
1124
1125 client
1126 }
1127
1128 // ============================================================================
1129 // CONFIGURATION ACCESS
1130 // ============================================================================
1131
1132 /// Get the current capabilities configuration
1133 pub fn capabilities(&self) -> &ClientCapabilities {
1134 &self.capabilities
1135 }
1136
1137 /// Get the current connection configuration
1138 pub fn connection_config(&self) -> &ConnectionConfig {
1139 &self.connection_config
1140 }
1141
1142 /// Get the number of registered plugins
1143 pub fn plugin_count(&self) -> usize {
1144 self.plugins.len()
1145 }
1146
1147 /// Check if any handlers are registered
1148 pub fn has_handlers(&self) -> bool {
1149 self.elicitation_handler.is_some()
1150 || self.progress_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;