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