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 sampling;
146
147// Re-export key types for convenience
148pub use client::{ConnectionInfo, ConnectionState, ManagerConfig, ServerGroup, SessionManager};
149
150use std::sync::Arc;
151
152use turbomcp_protocol::{Error, Result};
153use turbomcp_transport::Transport;
154
155// Note: Handler types are now used only in client/operations modules
156
157/// Client capability configuration
158///
159/// Defines the capabilities that this client supports when connecting to MCP servers.
160/// These capabilities are sent during the initialization handshake to negotiate
161/// which features will be available during the session.
162///
163/// # Examples
164///
165/// ```
166/// use turbomcp_client::ClientCapabilities;
167///
168/// let capabilities = ClientCapabilities {
169/// tools: true,
170/// prompts: true,
171/// resources: true,
172/// sampling: false,
173/// };
174/// ```
175#[derive(Debug, Clone, Default)]
176pub struct ClientCapabilities {
177 /// Whether the client supports tool calling
178 pub tools: bool,
179
180 /// Whether the client supports prompts
181 pub prompts: bool,
182
183 /// Whether the client supports resources
184 pub resources: bool,
185
186 /// Whether the client supports sampling
187 pub sampling: bool,
188}
189
190/// JSON-RPC protocol handler for MCP communication
191// Note: ProtocolClient implementation moved to client/protocol.rs for better modularity
192/// MCP client for communicating with servers
193///
194/// The `Client` struct provides a beautiful, ergonomic interface for interacting with MCP servers.
195/// It handles all protocol complexity internally, exposing only clean, type-safe methods.
196///
197/// # Type Parameters
198///
199/// * `T` - The transport implementation used for communication
200///
201/// # Examples
202///
203/// ```rust,no_run
204/// use turbomcp_client::Client;
205/// use turbomcp_transport::stdio::StdioTransport;
206///
207/// # async fn example() -> turbomcp_protocol::Result<()> {
208/// let transport = StdioTransport::new();
209/// let mut client = Client::new(transport);
210///
211/// // Initialize and start using the client
212/// client.initialize().await?;
213/// # Ok(())
214/// # }
215/// ```
216// Re-export Client from the core module
217pub use client::core::Client;
218
219// Thread-safe wrapper for sharing Client across async tasks
220//
221// This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
222// for concurrent access to MCP client functionality. It addresses the limitations
223// identified in PR feedback where Client requires `&mut self` for all operations
224// but needs to be shared across multiple async tasks.
225//
226// # Design Rationale
227//
228// All Client methods require `&mut self` because:
229// - MCP connections maintain state (initialized flag, connection status)
230// - Request correlation tracking for JSON-RPC requires mutation
231// - Handler and plugin registries need mutable access
232//
233// Note: SharedClient has been removed in v2 - Client is now directly cloneable via Arc
234
235// ----------------------------------------------------------------------------
236// Re-exports
237// ----------------------------------------------------------------------------
238
239#[doc = "Result of client initialization"]
240#[doc = ""]
241#[doc = "Contains information about the server and the negotiated capabilities"]
242#[doc = "after a successful initialization handshake."]
243pub use client::config::InitializeResult;
244
245// ServerCapabilities is now imported from turbomcp_protocol::types
246
247/// Connection configuration for the client
248#[derive(Debug, Clone)]
249pub struct ConnectionConfig {
250 /// Request timeout in milliseconds
251 pub timeout_ms: u64,
252
253 /// Maximum number of retry attempts
254 pub max_retries: u32,
255
256 /// Retry delay in milliseconds
257 pub retry_delay_ms: u64,
258
259 /// Keep-alive interval in milliseconds
260 pub keepalive_ms: u64,
261}
262
263impl Default for ConnectionConfig {
264 fn default() -> Self {
265 Self {
266 timeout_ms: 30_000, // 30 seconds
267 max_retries: 3, // 3 attempts
268 retry_delay_ms: 1_000, // 1 second
269 keepalive_ms: 60_000, // 60 seconds
270 }
271 }
272}
273
274/// Builder for configuring and creating MCP clients
275///
276/// Provides a fluent interface for configuring client options before creation.
277/// The enhanced builder pattern supports comprehensive configuration including:
278/// - Protocol capabilities
279/// - Plugin registration
280/// - Handler registration
281/// - Connection settings
282/// - Resilience configuration
283///
284/// # Examples
285///
286/// Basic usage:
287/// ```rust,no_run
288/// use turbomcp_client::ClientBuilder;
289/// use turbomcp_transport::stdio::StdioTransport;
290///
291/// # async fn example() -> turbomcp_protocol::Result<()> {
292/// let client = ClientBuilder::new()
293/// .with_tools(true)
294/// .with_prompts(true)
295/// .with_resources(false)
296/// .build(StdioTransport::new());
297/// # Ok(())
298/// # }
299/// ```
300///
301/// Advanced configuration:
302/// ```rust,no_run
303/// use turbomcp_client::{ClientBuilder, ConnectionConfig};
304/// use turbomcp_client::plugins::{MetricsPlugin, PluginConfig};
305/// use turbomcp_transport::stdio::StdioTransport;
306/// use std::sync::Arc;
307///
308/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
309/// let client = ClientBuilder::new()
310/// .with_tools(true)
311/// .with_prompts(true)
312/// .with_resources(true)
313/// .with_sampling(true)
314/// .with_connection_config(ConnectionConfig {
315/// timeout_ms: 60_000,
316/// max_retries: 5,
317/// retry_delay_ms: 2_000,
318/// keepalive_ms: 30_000,
319/// })
320/// .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
321/// .build(StdioTransport::new())
322/// .await?;
323/// # Ok(())
324/// # }
325/// ```
326#[derive(Debug, Default)]
327pub struct ClientBuilder {
328 capabilities: ClientCapabilities,
329 connection_config: ConnectionConfig,
330 plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>,
331 elicitation_handler: Option<Arc<dyn crate::handlers::ElicitationHandler>>,
332 progress_handler: Option<Arc<dyn crate::handlers::ProgressHandler>>,
333 log_handler: Option<Arc<dyn crate::handlers::LogHandler>>,
334 resource_update_handler: Option<Arc<dyn crate::handlers::ResourceUpdateHandler>>,
335 // Robustness configuration
336 enable_resilience: bool,
337 retry_config: Option<turbomcp_transport::resilience::RetryConfig>,
338 circuit_breaker_config: Option<turbomcp_transport::resilience::CircuitBreakerConfig>,
339 health_check_config: Option<turbomcp_transport::resilience::HealthCheckConfig>,
340}
341
342// Default implementation is now derived
343
344impl ClientBuilder {
345 /// Create a new client builder
346 ///
347 /// Returns a new builder with default configuration.
348 pub fn new() -> Self {
349 Self::default()
350 }
351
352 // ============================================================================
353 // CAPABILITY CONFIGURATION
354 // ============================================================================
355
356 /// Enable or disable tool support
357 ///
358 /// # Arguments
359 ///
360 /// * `enabled` - Whether to enable tool support
361 pub fn with_tools(mut self, enabled: bool) -> Self {
362 self.capabilities.tools = enabled;
363 self
364 }
365
366 /// Enable or disable prompt support
367 ///
368 /// # Arguments
369 ///
370 /// * `enabled` - Whether to enable prompt support
371 pub fn with_prompts(mut self, enabled: bool) -> Self {
372 self.capabilities.prompts = enabled;
373 self
374 }
375
376 /// Enable or disable resource support
377 ///
378 /// # Arguments
379 ///
380 /// * `enabled` - Whether to enable resource support
381 pub fn with_resources(mut self, enabled: bool) -> Self {
382 self.capabilities.resources = enabled;
383 self
384 }
385
386 /// Enable or disable sampling support
387 ///
388 /// # Arguments
389 ///
390 /// * `enabled` - Whether to enable sampling support
391 pub fn with_sampling(mut self, enabled: bool) -> Self {
392 self.capabilities.sampling = enabled;
393 self
394 }
395
396 /// Configure all capabilities at once
397 ///
398 /// # Arguments
399 ///
400 /// * `capabilities` - The capabilities configuration
401 pub fn with_capabilities(mut self, capabilities: ClientCapabilities) -> Self {
402 self.capabilities = capabilities;
403 self
404 }
405
406 // ============================================================================
407 // CONNECTION CONFIGURATION
408 // ============================================================================
409
410 /// Configure connection settings
411 ///
412 /// # Arguments
413 ///
414 /// * `config` - The connection configuration
415 pub fn with_connection_config(mut self, config: ConnectionConfig) -> Self {
416 self.connection_config = config;
417 self
418 }
419
420 /// Set request timeout
421 ///
422 /// # Arguments
423 ///
424 /// * `timeout_ms` - Timeout in milliseconds
425 pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
426 self.connection_config.timeout_ms = timeout_ms;
427 self
428 }
429
430 /// Set maximum retry attempts
431 ///
432 /// # Arguments
433 ///
434 /// * `max_retries` - Maximum number of retries
435 pub fn with_max_retries(mut self, max_retries: u32) -> Self {
436 self.connection_config.max_retries = max_retries;
437 self
438 }
439
440 /// Set retry delay
441 ///
442 /// # Arguments
443 ///
444 /// * `delay_ms` - Retry delay in milliseconds
445 pub fn with_retry_delay(mut self, delay_ms: u64) -> Self {
446 self.connection_config.retry_delay_ms = delay_ms;
447 self
448 }
449
450 /// Set keep-alive interval
451 ///
452 /// # Arguments
453 ///
454 /// * `interval_ms` - Keep-alive interval in milliseconds
455 pub fn with_keepalive(mut self, interval_ms: u64) -> Self {
456 self.connection_config.keepalive_ms = interval_ms;
457 self
458 }
459
460 // ============================================================================
461 // ROBUSTNESS & RESILIENCE CONFIGURATION
462 // ============================================================================
463
464 /// Enable resilient transport with circuit breaker, retry, and health checking
465 ///
466 /// When enabled, the transport layer will automatically:
467 /// - Retry failed operations with exponential backoff
468 /// - Use circuit breaker pattern to prevent cascade failures
469 /// - Perform periodic health checks
470 /// - Deduplicate messages
471 ///
472 /// # Examples
473 ///
474 /// ```rust,no_run
475 /// use turbomcp_client::ClientBuilder;
476 /// use turbomcp_transport::stdio::StdioTransport;
477 ///
478 /// let client = ClientBuilder::new()
479 /// .enable_resilience()
480 /// .build(StdioTransport::new());
481 /// ```
482 pub fn enable_resilience(mut self) -> Self {
483 self.enable_resilience = true;
484 self
485 }
486
487 /// Configure retry behavior for resilient transport
488 ///
489 /// # Arguments
490 ///
491 /// * `config` - Retry configuration
492 ///
493 /// # Examples
494 ///
495 /// ```rust,no_run
496 /// use turbomcp_client::ClientBuilder;
497 /// use turbomcp_transport::resilience::RetryConfig;
498 /// use turbomcp_transport::stdio::StdioTransport;
499 /// use std::time::Duration;
500 ///
501 /// let client = ClientBuilder::new()
502 /// .enable_resilience()
503 /// .with_retry_config(RetryConfig {
504 /// max_attempts: 5,
505 /// base_delay: Duration::from_millis(100),
506 /// max_delay: Duration::from_secs(30),
507 /// backoff_multiplier: 2.0,
508 /// jitter_factor: 0.1,
509 /// retry_on_connection_error: true,
510 /// retry_on_timeout: true,
511 /// custom_retry_conditions: Vec::new(),
512 /// })
513 /// .build(StdioTransport::new());
514 /// ```
515 pub fn with_retry_config(
516 mut self,
517 config: turbomcp_transport::resilience::RetryConfig,
518 ) -> Self {
519 self.retry_config = Some(config);
520 self.enable_resilience = true; // Auto-enable resilience
521 self
522 }
523
524 /// Configure circuit breaker for resilient transport
525 ///
526 /// # Arguments
527 ///
528 /// * `config` - Circuit breaker configuration
529 ///
530 /// # Examples
531 ///
532 /// ```rust,no_run
533 /// use turbomcp_client::ClientBuilder;
534 /// use turbomcp_transport::resilience::CircuitBreakerConfig;
535 /// use turbomcp_transport::stdio::StdioTransport;
536 /// use std::time::Duration;
537 ///
538 /// let client = ClientBuilder::new()
539 /// .enable_resilience()
540 /// .with_circuit_breaker_config(CircuitBreakerConfig {
541 /// failure_threshold: 5,
542 /// success_threshold: 2,
543 /// timeout: Duration::from_secs(60),
544 /// rolling_window_size: 100,
545 /// minimum_requests: 10,
546 /// })
547 /// .build(StdioTransport::new());
548 /// ```
549 pub fn with_circuit_breaker_config(
550 mut self,
551 config: turbomcp_transport::resilience::CircuitBreakerConfig,
552 ) -> Self {
553 self.circuit_breaker_config = Some(config);
554 self.enable_resilience = true; // Auto-enable resilience
555 self
556 }
557
558 /// Configure health checking for resilient transport
559 ///
560 /// # Arguments
561 ///
562 /// * `config` - Health check configuration
563 ///
564 /// # Examples
565 ///
566 /// ```rust,no_run
567 /// use turbomcp_client::ClientBuilder;
568 /// use turbomcp_transport::resilience::HealthCheckConfig;
569 /// use turbomcp_transport::stdio::StdioTransport;
570 /// use std::time::Duration;
571 ///
572 /// let client = ClientBuilder::new()
573 /// .enable_resilience()
574 /// .with_health_check_config(HealthCheckConfig {
575 /// interval: Duration::from_secs(30),
576 /// timeout: Duration::from_secs(5),
577 /// failure_threshold: 3,
578 /// success_threshold: 1,
579 /// custom_check: None,
580 /// })
581 /// .build(StdioTransport::new());
582 /// ```
583 pub fn with_health_check_config(
584 mut self,
585 config: turbomcp_transport::resilience::HealthCheckConfig,
586 ) -> Self {
587 self.health_check_config = Some(config);
588 self.enable_resilience = true; // Auto-enable resilience
589 self
590 }
591
592 // ============================================================================
593 // PLUGIN SYSTEM CONFIGURATION
594 // ============================================================================
595
596 /// Register a plugin with the client
597 ///
598 /// Plugins provide middleware functionality for request/response processing,
599 /// metrics collection, retry logic, caching, and other cross-cutting concerns.
600 ///
601 /// # Arguments
602 ///
603 /// * `plugin` - The plugin implementation
604 ///
605 /// # Examples
606 ///
607 /// ```rust,no_run
608 /// use turbomcp_client::{ClientBuilder, ConnectionConfig};
609 /// use turbomcp_client::plugins::{MetricsPlugin, RetryPlugin, PluginConfig, RetryConfig};
610 /// use std::sync::Arc;
611 ///
612 /// let client = ClientBuilder::new()
613 /// .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
614 /// .with_plugin(Arc::new(RetryPlugin::new(PluginConfig::Retry(RetryConfig {
615 /// max_retries: 5,
616 /// base_delay_ms: 1000,
617 /// max_delay_ms: 30000,
618 /// backoff_multiplier: 2.0,
619 /// retry_on_timeout: true,
620 /// retry_on_connection_error: true,
621 /// }))));
622 /// ```
623 pub fn with_plugin(mut self, plugin: Arc<dyn crate::plugins::ClientPlugin>) -> Self {
624 self.plugins.push(plugin);
625 self
626 }
627
628 /// Register multiple plugins at once
629 ///
630 /// # Arguments
631 ///
632 /// * `plugins` - Vector of plugin implementations
633 pub fn with_plugins(mut self, plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>) -> Self {
634 self.plugins.extend(plugins);
635 self
636 }
637
638 // ============================================================================
639 // HANDLER REGISTRATION
640 // ============================================================================
641
642 /// Register an elicitation handler for processing user input requests
643 ///
644 /// # Arguments
645 ///
646 /// * `handler` - The elicitation handler implementation
647 pub fn with_elicitation_handler(
648 mut self,
649 handler: Arc<dyn crate::handlers::ElicitationHandler>,
650 ) -> Self {
651 self.elicitation_handler = Some(handler);
652 self
653 }
654
655 /// Register a progress handler for processing operation progress updates
656 ///
657 /// # Arguments
658 ///
659 /// * `handler` - The progress handler implementation
660 pub fn with_progress_handler(
661 mut self,
662 handler: Arc<dyn crate::handlers::ProgressHandler>,
663 ) -> Self {
664 self.progress_handler = Some(handler);
665 self
666 }
667
668 /// Register a log handler for processing server log messages
669 ///
670 /// # Arguments
671 ///
672 /// * `handler` - The log handler implementation
673 pub fn with_log_handler(mut self, handler: Arc<dyn crate::handlers::LogHandler>) -> Self {
674 self.log_handler = Some(handler);
675 self
676 }
677
678 /// Register a resource update handler for processing resource change notifications
679 ///
680 /// # Arguments
681 ///
682 /// * `handler` - The resource update handler implementation
683 pub fn with_resource_update_handler(
684 mut self,
685 handler: Arc<dyn crate::handlers::ResourceUpdateHandler>,
686 ) -> Self {
687 self.resource_update_handler = Some(handler);
688 self
689 }
690
691 // ============================================================================
692 // BUILD METHODS
693 // ============================================================================
694
695 /// Build a client with the configured options
696 ///
697 /// Creates a new client instance with all the configured options. The client
698 /// will be initialized with the registered plugins, handlers, and providers.
699 ///
700 /// # Arguments
701 ///
702 /// * `transport` - The transport to use for the client
703 ///
704 /// # Returns
705 ///
706 /// Returns a configured `Client` instance wrapped in a Result for async setup.
707 ///
708 /// # Examples
709 ///
710 /// ```rust,no_run
711 /// use turbomcp_client::ClientBuilder;
712 /// use turbomcp_transport::stdio::StdioTransport;
713 ///
714 /// # async fn example() -> turbomcp_protocol::Result<()> {
715 /// let client = ClientBuilder::new()
716 /// .with_tools(true)
717 /// .with_prompts(true)
718 /// .build(StdioTransport::new())
719 /// .await?;
720 /// # Ok(())
721 /// # }
722 /// ```
723 pub async fn build<T: Transport>(self, transport: T) -> Result<Client<T>> {
724 // Create base client with capabilities
725 let client = Client::with_capabilities(transport, self.capabilities);
726
727 // Register handlers
728 if let Some(handler) = self.elicitation_handler {
729 client.on_elicitation(handler);
730 }
731 if let Some(handler) = self.progress_handler {
732 client.on_progress(handler);
733 }
734 if let Some(handler) = self.log_handler {
735 client.on_log(handler);
736 }
737 if let Some(handler) = self.resource_update_handler {
738 client.on_resource_update(handler);
739 }
740
741 // Apply connection configuration (store for future use in actual connections)
742 // Note: The current Client doesn't expose connection config setters,
743 // so we'll store this for when the transport supports it
744
745 // Register plugins with the client
746 let has_plugins = !self.plugins.is_empty();
747 for plugin in self.plugins {
748 client.register_plugin(plugin).await.map_err(|e| {
749 Error::bad_request(format!("Failed to register plugin during build: {}", e))
750 })?;
751 }
752
753 // Initialize plugins after registration
754 if has_plugins {
755 client.initialize_plugins().await.map_err(|e| {
756 Error::bad_request(format!("Failed to initialize plugins during build: {}", e))
757 })?;
758 }
759
760 Ok(client)
761 }
762
763 /// Build a client with resilient transport (circuit breaker, retry, health checking)
764 ///
765 /// When resilience features are enabled via `enable_resilience()` or any resilience
766 /// configuration method, this wraps the transport in a `TurboTransport` that provides:
767 /// - Automatic retry with exponential backoff
768 /// - Circuit breaker pattern for fast failure
769 /// - Health checking and monitoring
770 /// - Message deduplication
771 ///
772 /// # Arguments
773 ///
774 /// * `transport` - The base transport to wrap with resilience features
775 ///
776 /// # Returns
777 ///
778 /// Returns a configured `Client<TurboTransport>` instance.
779 ///
780 /// # Errors
781 ///
782 /// Returns an error if plugin initialization fails.
783 ///
784 /// # Examples
785 ///
786 /// ```rust,no_run
787 /// use turbomcp_client::ClientBuilder;
788 /// use turbomcp_transport::stdio::StdioTransport;
789 /// use turbomcp_transport::resilience::{RetryConfig, CircuitBreakerConfig, HealthCheckConfig};
790 /// use std::time::Duration;
791 ///
792 /// # async fn example() -> turbomcp_protocol::Result<()> {
793 /// let client = ClientBuilder::new()
794 /// .with_retry_config(RetryConfig {
795 /// max_attempts: 5,
796 /// base_delay: Duration::from_millis(200),
797 /// ..Default::default()
798 /// })
799 /// .with_circuit_breaker_config(CircuitBreakerConfig {
800 /// failure_threshold: 3,
801 /// timeout: Duration::from_secs(30),
802 /// ..Default::default()
803 /// })
804 /// .with_health_check_config(HealthCheckConfig {
805 /// interval: Duration::from_secs(15),
806 /// timeout: Duration::from_secs(5),
807 /// ..Default::default()
808 /// })
809 /// .build_resilient(StdioTransport::new())
810 /// .await?;
811 /// # Ok(())
812 /// # }
813 /// ```
814 pub async fn build_resilient<T: Transport + 'static>(
815 self,
816 transport: T,
817 ) -> Result<Client<turbomcp_transport::resilience::TurboTransport>> {
818 use turbomcp_transport::resilience::TurboTransport;
819
820 // Get configurations or use defaults
821 let retry_config = self.retry_config.unwrap_or_default();
822 let circuit_config = self.circuit_breaker_config.unwrap_or_default();
823 let health_config = self.health_check_config.unwrap_or_default();
824
825 // Wrap transport in TurboTransport
826 let robust_transport = TurboTransport::new(
827 Box::new(transport),
828 retry_config,
829 circuit_config,
830 health_config,
831 );
832
833 // Create client with resilient transport
834 let client = Client::with_capabilities(robust_transport, self.capabilities);
835
836 // Register handlers
837 if let Some(handler) = self.elicitation_handler {
838 client.on_elicitation(handler);
839 }
840 if let Some(handler) = self.progress_handler {
841 client.on_progress(handler);
842 }
843 if let Some(handler) = self.log_handler {
844 client.on_log(handler);
845 }
846 if let Some(handler) = self.resource_update_handler {
847 client.on_resource_update(handler);
848 }
849
850 // Register plugins
851 let has_plugins = !self.plugins.is_empty();
852 for plugin in self.plugins {
853 client.register_plugin(plugin).await.map_err(|e| {
854 Error::bad_request(format!("Failed to register plugin during build: {}", e))
855 })?;
856 }
857
858 if has_plugins {
859 client.initialize_plugins().await.map_err(|e| {
860 Error::bad_request(format!("Failed to initialize plugins during build: {}", e))
861 })?;
862 }
863
864 Ok(client)
865 }
866
867 /// Build a client synchronously with basic configuration only
868 ///
869 /// This is a convenience method for simple use cases where no async setup
870 /// is required. For advanced features like plugins, use `build()` instead.
871 ///
872 /// # Arguments
873 ///
874 /// * `transport` - The transport to use for the client
875 ///
876 /// # Returns
877 ///
878 /// Returns a configured `Client` instance.
879 ///
880 /// # Examples
881 ///
882 /// ```rust,no_run
883 /// use turbomcp_client::ClientBuilder;
884 /// use turbomcp_transport::stdio::StdioTransport;
885 ///
886 /// let client = ClientBuilder::new()
887 /// .with_tools(true)
888 /// .build_sync(StdioTransport::new());
889 /// ```
890 pub fn build_sync<T: Transport>(self, transport: T) -> Client<T> {
891 let client = Client::with_capabilities(transport, self.capabilities);
892
893 // Register synchronous handlers only
894 if let Some(handler) = self.elicitation_handler {
895 client.on_elicitation(handler);
896 }
897 if let Some(handler) = self.progress_handler {
898 client.on_progress(handler);
899 }
900 if let Some(handler) = self.log_handler {
901 client.on_log(handler);
902 }
903 if let Some(handler) = self.resource_update_handler {
904 client.on_resource_update(handler);
905 }
906
907 client
908 }
909
910 // ============================================================================
911 // CONFIGURATION ACCESS
912 // ============================================================================
913
914 /// Get the current capabilities configuration
915 pub fn capabilities(&self) -> &ClientCapabilities {
916 &self.capabilities
917 }
918
919 /// Get the current connection configuration
920 pub fn connection_config(&self) -> &ConnectionConfig {
921 &self.connection_config
922 }
923
924 /// Get the number of registered plugins
925 pub fn plugin_count(&self) -> usize {
926 self.plugins.len()
927 }
928
929 /// Check if any handlers are registered
930 pub fn has_handlers(&self) -> bool {
931 self.elicitation_handler.is_some()
932 || self.progress_handler.is_some()
933 || self.log_handler.is_some()
934 || self.resource_update_handler.is_some()
935 }
936}
937
938// Re-export types for public API
939pub use turbomcp_protocol::types::ServerCapabilities as PublicServerCapabilities;