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_core::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};
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//!         // This enables the server to request LLM sampling through the client
107//!         
108//!         Ok(CreateMessageResult {
109//!             role: turbomcp_protocol::types::Role::Assistant,
110//!             content: turbomcp_protocol::types::Content::Text(
111//!                 turbomcp_protocol::types::TextContent {
112//!                     text: "Response from LLM".to_string(),
113//!                     annotations: None,
114//!                     meta: None,
115//!                 }
116//!             ),
117//!             model: Some("gpt-4".to_string()),
118//!             stop_reason: Some("end_turn".to_string()),
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_core::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 handlers;
143pub mod llm;
144pub mod plugins;
145pub mod sampling;
146
147use std::collections::HashMap;
148use std::sync::Arc;
149use std::sync::atomic::{AtomicU64, Ordering};
150use tokio::sync::Mutex;
151
152use turbomcp_core::{Error, PROTOCOL_VERSION, Result};
153use turbomcp_protocol::jsonrpc::{
154    JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcVersion,
155};
156use turbomcp_protocol::types::{
157    CallToolRequest,
158    CallToolResult,
159    ClientCapabilities as ProtocolClientCapabilities,
160    CompleteResult,
161    Content,
162    CreateMessageRequest,
163    EmptyResult,
164    GetPromptRequest,
165    GetPromptResult,
166    InitializeRequest,
167    InitializeResult as ProtocolInitializeResult,
168    ListPromptsResult,
169    ListResourceTemplatesResult,
170    ListResourcesResult,
171    ListRootsResult,
172    ListToolsResult,
173    LogLevel,
174    // Missing protocol method types
175    PingResult,
176    Prompt,
177    PromptInput,
178    ReadResourceRequest,
179    ReadResourceResult,
180    ServerCapabilities,
181    SetLevelRequest,
182    SetLevelResult,
183    SubscribeRequest,
184    Tool,
185    UnsubscribeRequest,
186};
187use turbomcp_transport::{Transport, TransportMessage};
188
189use crate::handlers::{
190    ElicitationHandler, HandlerRegistry, LogHandler, ProgressHandler, ResourceUpdateHandler,
191};
192use crate::sampling::SamplingHandler;
193
194/// Client capability configuration
195///
196/// Defines the capabilities that this client supports when connecting to MCP servers.
197/// These capabilities are sent during the initialization handshake to negotiate
198/// which features will be available during the session.
199///
200/// # Examples
201///
202/// ```
203/// use turbomcp_client::ClientCapabilities;
204///
205/// let capabilities = ClientCapabilities {
206///     tools: true,
207///     prompts: true,
208///     resources: true,
209///     sampling: false,
210/// };
211/// ```
212#[derive(Debug, Clone, Default)]
213pub struct ClientCapabilities {
214    /// Whether the client supports tool calling
215    pub tools: bool,
216
217    /// Whether the client supports prompts
218    pub prompts: bool,
219
220    /// Whether the client supports resources
221    pub resources: bool,
222
223    /// Whether the client supports sampling
224    pub sampling: bool,
225}
226
227/// JSON-RPC protocol handler for MCP communication
228///
229/// Handles request/response correlation, serialization, and protocol-level concerns.
230/// This is the missing abstraction layer between raw Transport and high-level Client APIs.
231#[derive(Debug)]
232struct ProtocolClient<T: Transport> {
233    transport: T,
234    next_id: AtomicU64,
235}
236
237impl<T: Transport> ProtocolClient<T> {
238    fn new(transport: T) -> Self {
239        Self {
240            transport,
241            next_id: AtomicU64::new(1),
242        }
243    }
244
245    /// Send JSON-RPC request and await typed response
246    async fn request<R: serde::de::DeserializeOwned>(
247        &mut self,
248        method: &str,
249        params: Option<serde_json::Value>,
250    ) -> Result<R> {
251        let id = self.next_id.fetch_add(1, Ordering::Relaxed);
252        let request = JsonRpcRequest {
253            jsonrpc: JsonRpcVersion,
254            id: turbomcp_core::MessageId::from(id.to_string()),
255            method: method.to_string(),
256            params,
257        };
258
259        // Serialize and send
260        let payload = serde_json::to_vec(&request)
261            .map_err(|e| Error::protocol(format!("Failed to serialize request: {e}")))?;
262
263        let message = TransportMessage::new(
264            turbomcp_core::MessageId::from(format!("req-{id}")),
265            payload.into(),
266        );
267        self.transport
268            .send(message)
269            .await
270            .map_err(|e| Error::transport(format!("Transport send failed: {e}")))?;
271
272        // Receive and deserialize response
273        let response_msg = self
274            .transport
275            .receive()
276            .await
277            .map_err(|e| Error::transport(format!("Transport receive failed: {e}")))?
278            .ok_or_else(|| Error::transport("No response received".to_string()))?;
279
280        let response: JsonRpcResponse = serde_json::from_slice(&response_msg.payload)
281            .map_err(|e| Error::protocol(format!("Invalid JSON-RPC response: {e}")))?;
282
283        if let Some(error) = response.error() {
284            return Err(Error::rpc(error.code, &error.message));
285        }
286
287        let result = response
288            .result()
289            .ok_or_else(|| Error::protocol("Response missing result field".to_string()))?;
290
291        serde_json::from_value(result.clone())
292            .map_err(|e| Error::protocol(format!("Invalid response format: {e}")))
293    }
294
295    /// Send JSON-RPC notification (no response expected)
296    async fn notify(&mut self, method: &str, params: Option<serde_json::Value>) -> Result<()> {
297        let notification = JsonRpcNotification {
298            jsonrpc: JsonRpcVersion,
299            method: method.to_string(),
300            params,
301        };
302
303        let payload = serde_json::to_vec(&notification)
304            .map_err(|e| Error::protocol(format!("Failed to serialize notification: {e}")))?;
305
306        let message = TransportMessage::new(
307            turbomcp_core::MessageId::from("notification"),
308            payload.into(),
309        );
310        self.transport
311            .send(message)
312            .await
313            .map_err(|e| Error::transport(format!("Transport send failed: {e}")))?;
314
315        Ok(())
316    }
317}
318
319/// MCP client for communicating with servers
320///
321/// The `Client` struct provides a beautiful, ergonomic interface for interacting with MCP servers.
322/// It handles all protocol complexity internally, exposing only clean, type-safe methods.
323///
324/// # Type Parameters
325///
326/// * `T` - The transport implementation used for communication
327///
328/// # Examples
329///
330/// ```rust,no_run
331/// use turbomcp_client::Client;
332/// use turbomcp_transport::stdio::StdioTransport;
333///
334/// # async fn example() -> turbomcp_core::Result<()> {
335/// let transport = StdioTransport::new();
336/// let mut client = Client::new(transport);
337///
338/// // Initialize and start using the client
339/// client.initialize().await?;
340/// # Ok(())
341/// # }
342/// ```
343#[derive(Debug)]
344pub struct Client<T: Transport> {
345    protocol: ProtocolClient<T>,
346    capabilities: ClientCapabilities,
347    initialized: bool,
348    #[allow(dead_code)]
349    sampling_handler: Option<Arc<dyn SamplingHandler>>,
350    /// Handler registry for bidirectional communication
351    handlers: HandlerRegistry,
352    /// Plugin registry for middleware and extensibility
353    plugin_registry: crate::plugins::PluginRegistry,
354}
355
356impl<T: Transport> Client<T> {
357    /// Create a new client with the specified transport
358    ///
359    /// Creates a new MCP client instance with default capabilities.
360    /// The client must be initialized before use by calling `initialize()`.
361    ///
362    /// # Arguments
363    ///
364    /// * `transport` - The transport implementation to use for communication
365    ///
366    /// # Examples
367    ///
368    /// ```rust,no_run
369    /// use turbomcp_client::Client;
370    /// use turbomcp_transport::stdio::StdioTransport;
371    ///
372    /// let transport = StdioTransport::new();
373    /// let client = Client::new(transport);
374    /// ```
375    pub fn new(transport: T) -> Self {
376        Self {
377            protocol: ProtocolClient::new(transport),
378            capabilities: ClientCapabilities::default(),
379            initialized: false,
380            sampling_handler: None,
381            handlers: HandlerRegistry::new(),
382            plugin_registry: crate::plugins::PluginRegistry::new(),
383        }
384    }
385
386    /// Create a new client with custom capabilities
387    ///
388    /// # Arguments
389    ///
390    /// * `transport` - The transport implementation to use
391    /// * `capabilities` - The client capabilities to negotiate
392    ///
393    /// # Examples
394    ///
395    /// ```rust,no_run
396    /// use turbomcp_client::{Client, ClientCapabilities};
397    /// use turbomcp_transport::stdio::StdioTransport;
398    ///
399    /// let capabilities = ClientCapabilities {
400    ///     tools: true,
401    ///     prompts: true,
402    ///     resources: false,
403    ///     sampling: false,
404    /// };
405    ///
406    /// let transport = StdioTransport::new();
407    /// let client = Client::with_capabilities(transport, capabilities);
408    /// ```
409    pub fn with_capabilities(transport: T, capabilities: ClientCapabilities) -> Self {
410        Self {
411            protocol: ProtocolClient::new(transport),
412            capabilities,
413            initialized: false,
414            sampling_handler: None,
415            handlers: HandlerRegistry::new(),
416            plugin_registry: crate::plugins::PluginRegistry::new(),
417        }
418    }
419
420    /// Set the sampling handler for processing server-initiated sampling requests
421    ///
422    /// # Arguments
423    ///
424    /// * `handler` - The handler implementation for sampling requests
425    ///
426    /// # Examples
427    ///
428    /// ```rust,no_run
429    /// use turbomcp_client::{Client, sampling::SamplingHandler};
430    /// use turbomcp_transport::stdio::StdioTransport;
431    /// use turbomcp_protocol::types::{CreateMessageRequest, CreateMessageResult};
432    /// use async_trait::async_trait;
433    /// use std::sync::Arc;
434    ///
435    /// #[derive(Debug)]
436    /// struct ExampleHandler;
437    ///
438    /// #[async_trait]
439    /// impl SamplingHandler for ExampleHandler {
440    ///     async fn handle_create_message(
441    ///         &self,
442    ///         _request: CreateMessageRequest,
443    ///     ) -> Result<CreateMessageResult, Box<dyn std::error::Error + Send + Sync>> {
444    ///         // Handle sampling request
445    ///         todo!("Implement sampling logic")
446    ///     }
447    /// }
448    ///
449    /// let mut client = Client::new(StdioTransport::new());
450    /// client.set_sampling_handler(Arc::new(ExampleHandler));
451    /// ```
452    pub fn set_sampling_handler(&mut self, handler: Arc<dyn SamplingHandler>) {
453        self.sampling_handler = Some(handler);
454        self.capabilities.sampling = true;
455    }
456
457    /// Process incoming messages from the server
458    ///
459    /// This method should be called in a loop to handle server-initiated requests
460    /// like sampling. It processes one message at a time.
461    ///
462    /// # Returns
463    ///
464    /// Returns `Ok(true)` if a message was processed, `Ok(false)` if no message was available.
465    ///
466    /// # Examples
467    ///
468    /// ```rust,no_run
469    /// # use turbomcp_client::Client;
470    /// # use turbomcp_transport::stdio::StdioTransport;
471    /// # async fn example() -> turbomcp_core::Result<()> {
472    /// let mut client = Client::new(StdioTransport::new());
473    ///
474    /// // Process messages in background
475    /// tokio::spawn(async move {
476    ///     loop {
477    ///         if let Err(e) = client.process_message().await {
478    ///             eprintln!("Error processing message: {}", e);
479    ///         }
480    ///     }
481    /// });
482    /// # Ok(())
483    /// # }
484    /// ```
485    pub async fn process_message(&mut self) -> Result<bool> {
486        // Try to receive a message without blocking
487        let message = match self.protocol.transport.receive().await {
488            Ok(Some(msg)) => msg,
489            Ok(None) => return Ok(false),
490            Err(e) => {
491                return Err(Error::transport(format!(
492                    "Failed to receive message: {}",
493                    e
494                )));
495            }
496        };
497
498        // Parse as JSON-RPC message
499        let json_msg: JsonRpcMessage = serde_json::from_slice(&message.payload)
500            .map_err(|e| Error::protocol(format!("Invalid JSON-RPC message: {}", e)))?;
501
502        match json_msg {
503            JsonRpcMessage::Request(request) => {
504                self.handle_request(request).await?;
505                Ok(true)
506            }
507            JsonRpcMessage::Response(_) => {
508                // Responses are handled by the protocol client during request/response flow
509                Ok(true)
510            }
511            JsonRpcMessage::Notification(notification) => {
512                self.handle_notification(notification).await?;
513                Ok(true)
514            }
515            JsonRpcMessage::RequestBatch(_)
516            | JsonRpcMessage::ResponseBatch(_)
517            | JsonRpcMessage::MessageBatch(_) => {
518                // Batch operations not yet supported
519                Ok(true)
520            }
521        }
522    }
523
524    async fn handle_request(&mut self, request: JsonRpcRequest) -> Result<()> {
525        match request.method.as_str() {
526            "sampling/createMessage" => {
527                if let Some(handler) = &self.sampling_handler {
528                    let params: CreateMessageRequest =
529                        serde_json::from_value(request.params.unwrap_or(serde_json::Value::Null))
530                            .map_err(|e| {
531                            Error::protocol(format!("Invalid createMessage params: {}", e))
532                        })?;
533
534                    match handler.handle_create_message(params).await {
535                        Ok(result) => {
536                            let result_value = serde_json::to_value(result).map_err(|e| {
537                                Error::protocol(format!("Failed to serialize response: {}", e))
538                            })?;
539                            let response = JsonRpcResponse::success(result_value, request.id);
540                            self.send_response(response).await?;
541                        }
542                        Err(e) => {
543                            let error = turbomcp_protocol::jsonrpc::JsonRpcError {
544                                code: -32603,
545                                message: format!("Sampling handler error: {}", e),
546                                data: None,
547                            };
548                            let response = JsonRpcResponse::error_response(error, request.id);
549                            self.send_response(response).await?;
550                        }
551                    }
552                } else {
553                    // No handler configured
554                    let error = turbomcp_protocol::jsonrpc::JsonRpcError {
555                        code: -32601,
556                        message: "Sampling not supported".to_string(),
557                        data: None,
558                    };
559                    let response = JsonRpcResponse::error_response(error, request.id);
560                    self.send_response(response).await?;
561                }
562            }
563            _ => {
564                // Unknown method
565                let error = turbomcp_protocol::jsonrpc::JsonRpcError {
566                    code: -32601,
567                    message: format!("Method not found: {}", request.method),
568                    data: None,
569                };
570                let response = JsonRpcResponse::error_response(error, request.id);
571                self.send_response(response).await?;
572            }
573        }
574        Ok(())
575    }
576
577    async fn handle_notification(&mut self, _notification: JsonRpcNotification) -> Result<()> {
578        // Handle notifications if needed
579        // Currently MCP doesn't define client-side notifications
580        Ok(())
581    }
582
583    async fn send_response(&mut self, response: JsonRpcResponse) -> Result<()> {
584        let payload = serde_json::to_vec(&response)
585            .map_err(|e| Error::protocol(format!("Failed to serialize response: {}", e)))?;
586
587        let message = TransportMessage::new(
588            turbomcp_core::MessageId::from("response".to_string()),
589            payload.into(),
590        );
591
592        self.protocol
593            .transport
594            .send(message)
595            .await
596            .map_err(|e| Error::transport(format!("Failed to send response: {}", e)))?;
597
598        Ok(())
599    }
600
601    /// Initialize the connection with the MCP server
602    ///
603    /// Performs the initialization handshake with the server, negotiating capabilities
604    /// and establishing the protocol version. This method must be called before
605    /// any other operations can be performed.
606    ///
607    /// # Returns
608    ///
609    /// Returns an `InitializeResult` containing server information and negotiated capabilities.
610    ///
611    /// # Errors
612    ///
613    /// Returns an error if:
614    /// - The transport connection fails
615    /// - The server rejects the initialization request
616    /// - Protocol negotiation fails
617    ///
618    /// # Examples
619    ///
620    /// ```rust,no_run
621    /// # use turbomcp_client::Client;
622    /// # use turbomcp_transport::stdio::StdioTransport;
623    /// # async fn example() -> turbomcp_core::Result<()> {
624    /// let mut client = Client::new(StdioTransport::new());
625    ///
626    /// let result = client.initialize().await?;
627    /// println!("Server: {} v{}", result.server_info.name, result.server_info.version);
628    /// # Ok(())
629    /// # }
630    /// ```
631    pub async fn initialize(&mut self) -> Result<InitializeResult> {
632        // Build client capabilities based on configuration
633        let mut client_caps = ProtocolClientCapabilities::default();
634        if self.capabilities.sampling {
635            client_caps.sampling = Some(turbomcp_protocol::types::SamplingCapabilities);
636        }
637
638        // Send MCP initialization request
639        let request = InitializeRequest {
640            protocol_version: PROTOCOL_VERSION.to_string(),
641            capabilities: client_caps,
642            client_info: turbomcp_protocol::Implementation {
643                name: "turbomcp-client".to_string(),
644                version: env!("CARGO_PKG_VERSION").to_string(),
645                title: Some("TurboMCP Client".to_string()),
646            },
647            _meta: None,
648        };
649
650        let protocol_response: ProtocolInitializeResult = self
651            .protocol
652            .request("initialize", Some(serde_json::to_value(request)?))
653            .await?;
654        self.initialized = true;
655
656        // Send initialized notification
657        self.protocol
658            .notify("notifications/initialized", None)
659            .await?;
660
661        // Convert protocol response to client response type
662        Ok(InitializeResult {
663            server_info: protocol_response.server_info,
664            server_capabilities: protocol_response.capabilities,
665        })
666    }
667
668    /// List all available tools from the MCP server
669    ///
670    /// Returns a list of complete tool definitions with schemas that can be used
671    /// for form generation, validation, and documentation. Tools represent
672    /// executable functions provided by the server.
673    ///
674    /// # Returns
675    ///
676    /// Returns a vector of complete `Tool` objects with schemas and metadata.
677    ///
678    /// # Errors
679    ///
680    /// Returns an error if:
681    /// - The client is not initialized
682    /// - The server doesn't support tools
683    /// - The request fails
684    ///
685    /// # Examples
686    ///
687    /// ```rust,no_run
688    /// # use turbomcp_client::Client;
689    /// # use turbomcp_transport::stdio::StdioTransport;
690    /// # async fn example() -> turbomcp_core::Result<()> {
691    /// let mut client = Client::new(StdioTransport::new());
692    /// client.initialize().await?;
693    ///
694    /// let tools = client.list_tools().await?;
695    /// for tool in tools {
696    ///     println!("Tool: {} - {}", tool.name, tool.description.as_deref().unwrap_or("No description"));
697    ///     // Access full inputSchema for form generation
698    ///     let schema = &tool.input_schema;
699    /// }
700    /// # Ok(())
701    /// # }
702    /// ```
703    pub async fn list_tools(&mut self) -> Result<Vec<Tool>> {
704        if !self.initialized {
705            return Err(Error::bad_request("Client not initialized"));
706        }
707
708        // Send tools/list request with plugin middleware
709        let response: ListToolsResult = self.execute_with_plugins("tools/list", None).await?;
710        Ok(response.tools) // Return full Tool objects with schemas
711    }
712
713    /// List available tool names from the MCP server
714    ///
715    /// Returns only the tool names for cases where full schemas are not needed.
716    /// For most use cases, prefer `list_tools()` which provides complete tool definitions.
717    ///
718    /// # Returns
719    ///
720    /// Returns a vector of tool names available on the server.
721    ///
722    /// # Examples
723    ///
724    /// ```rust,no_run
725    /// # use turbomcp_client::Client;
726    /// # use turbomcp_transport::stdio::StdioTransport;
727    /// # async fn example() -> turbomcp_core::Result<()> {
728    /// let mut client = Client::new(StdioTransport::new());
729    /// client.initialize().await?;
730    ///
731    /// let tool_names = client.list_tool_names().await?;
732    /// for name in tool_names {
733    ///     println!("Available tool: {}", name);
734    /// }
735    /// # Ok(())
736    /// # }
737    /// ```
738    pub async fn list_tool_names(&mut self) -> Result<Vec<String>> {
739        let tools = self.list_tools().await?;
740        Ok(tools.into_iter().map(|tool| tool.name).collect())
741    }
742
743    /// Call a tool on the server
744    ///
745    /// Executes a tool on the server with the provided arguments.
746    ///
747    /// # Arguments
748    ///
749    /// * `name` - The name of the tool to call
750    /// * `arguments` - Optional arguments to pass to the tool
751    ///
752    /// # Returns
753    ///
754    /// Returns the result of the tool execution.
755    ///
756    /// # Examples
757    ///
758    /// ```rust,no_run
759    /// # use turbomcp_client::Client;
760    /// # use turbomcp_transport::stdio::StdioTransport;
761    /// # use std::collections::HashMap;
762    /// # async fn example() -> turbomcp_core::Result<()> {
763    /// let mut client = Client::new(StdioTransport::new());
764    /// client.initialize().await?;
765    ///
766    /// let mut args = HashMap::new();
767    /// args.insert("input".to_string(), serde_json::json!("test"));
768    ///
769    /// let result = client.call_tool("my_tool", Some(args)).await?;
770    /// println!("Tool result: {:?}", result);
771    /// # Ok(())
772    /// # }
773    /// ```
774    pub async fn call_tool(
775        &mut self,
776        name: &str,
777        arguments: Option<HashMap<String, serde_json::Value>>,
778    ) -> Result<serde_json::Value> {
779        if !self.initialized {
780            return Err(Error::bad_request("Client not initialized"));
781        }
782
783        // 🎉 TurboMCP v1.0.7: Clean plugin execution with macro!
784        let request_data = CallToolRequest {
785            name: name.to_string(),
786            arguments: Some(arguments.unwrap_or_default()),
787            _meta: None,
788        };
789
790        with_plugins!(self, "tools/call", request_data, {
791            // Core protocol call - plugins execute automatically around this
792            let result: CallToolResult = self
793                .protocol
794                .request("tools/call", Some(serde_json::to_value(&request_data)?))
795                .await?;
796
797            Ok(self.extract_tool_content(&result))
798        })
799    }
800
801    /// Execute a protocol method with plugin middleware
802    ///
803    /// This is a generic helper for wrapping protocol calls with plugin middleware.
804    async fn execute_with_plugins<R>(
805        &mut self,
806        method_name: &str,
807        params: Option<serde_json::Value>,
808    ) -> Result<R>
809    where
810        R: serde::de::DeserializeOwned + serde::Serialize + Clone,
811    {
812        // Create JSON-RPC request for plugin context
813        let json_rpc_request = turbomcp_protocol::jsonrpc::JsonRpcRequest {
814            jsonrpc: turbomcp_protocol::jsonrpc::JsonRpcVersion,
815            id: turbomcp_core::MessageId::Number(1),
816            method: method_name.to_string(),
817            params: params.clone(),
818        };
819
820        // 1. Create request context for plugins
821        let mut req_ctx =
822            crate::plugins::RequestContext::new(json_rpc_request, std::collections::HashMap::new());
823
824        // 2. Execute before_request plugin middleware
825        if let Err(e) = self
826            .plugin_registry
827            .execute_before_request(&mut req_ctx)
828            .await
829        {
830            return Err(Error::bad_request(format!(
831                "Plugin before_request failed: {}",
832                e
833            )));
834        }
835
836        // 3. Execute the actual protocol call
837        let start_time = std::time::Instant::now();
838        let protocol_result: Result<R> = self
839            .protocol
840            .request(method_name, req_ctx.params().cloned())
841            .await;
842        let duration = start_time.elapsed();
843
844        // 4. Prepare response context
845        let mut resp_ctx = match protocol_result {
846            Ok(ref response) => {
847                let response_value = serde_json::to_value(response.clone())?;
848                crate::plugins::ResponseContext::new(req_ctx, Some(response_value), None, duration)
849            }
850            Err(ref e) => {
851                crate::plugins::ResponseContext::new(req_ctx, None, Some(*e.clone()), duration)
852            }
853        };
854
855        // 5. Execute after_response plugin middleware
856        if let Err(e) = self
857            .plugin_registry
858            .execute_after_response(&mut resp_ctx)
859            .await
860        {
861            return Err(Error::bad_request(format!(
862                "Plugin after_response failed: {}",
863                e
864            )));
865        }
866
867        // 6. Return the final result, checking for plugin modifications
868        match protocol_result {
869            Ok(ref response) => {
870                // Check if plugins modified the response
871                if let Some(modified_response) = resp_ctx.response {
872                    // Try to deserialize the modified response
873                    if let Ok(modified_result) =
874                        serde_json::from_value::<R>(modified_response.clone())
875                    {
876                        return Ok(modified_result);
877                    }
878                }
879
880                // No plugin modifications, use original response
881                Ok(response.clone())
882            }
883            Err(e) => {
884                // Check if plugins provided an error recovery response
885                if let Some(recovery_response) = resp_ctx.response {
886                    if let Ok(recovery_result) = serde_json::from_value::<R>(recovery_response) {
887                        Ok(recovery_result)
888                    } else {
889                        Err(e)
890                    }
891                } else {
892                    Err(e)
893                }
894            }
895        }
896    }
897
898    /// Helper method to extract content from CallToolResult
899    fn extract_tool_content(&self, response: &CallToolResult) -> serde_json::Value {
900        // Extract content from response - for simplicity, return the first text content
901        if let Some(content) = response.content.first() {
902            match content {
903                Content::Text(text_content) => serde_json::json!({
904                    "text": text_content.text,
905                    "is_error": response.is_error.unwrap_or(false)
906                }),
907                Content::Image(image_content) => serde_json::json!({
908                    "image": image_content.data,
909                    "mime_type": image_content.mime_type,
910                    "is_error": response.is_error.unwrap_or(false)
911                }),
912                Content::Resource(resource_content) => serde_json::json!({
913                    "resource": resource_content.resource,
914                    "annotations": resource_content.annotations,
915                    "is_error": response.is_error.unwrap_or(false)
916                }),
917                Content::Audio(audio_content) => serde_json::json!({
918                    "audio": audio_content.data,
919                    "mime_type": audio_content.mime_type,
920                    "is_error": response.is_error.unwrap_or(false)
921                }),
922                Content::ResourceLink(resource_link) => serde_json::json!({
923                    "resource_uri": resource_link.uri,
924                    "is_error": response.is_error.unwrap_or(false)
925                }),
926            }
927        } else {
928            serde_json::json!({
929                "message": "No content returned",
930                "is_error": response.is_error.unwrap_or(false)
931            })
932        }
933    }
934
935    /// Request completion suggestions from the server
936    ///
937    /// # Arguments
938    ///
939    /// * `handler_name` - The completion handler name
940    /// * `argument_value` - The partial value to complete
941    ///
942    /// # Examples
943    ///
944    /// ```rust,no_run
945    /// # use turbomcp_client::Client;
946    /// # use turbomcp_transport::stdio::StdioTransport;
947    /// # async fn example() -> turbomcp_core::Result<()> {
948    /// let mut client = Client::new(StdioTransport::new());
949    /// client.initialize().await?;
950    ///
951    /// let result = client.complete("complete_path", "/usr/b").await?;
952    /// println!("Completions: {:?}", result.values);
953    /// # Ok(())
954    /// # }
955    /// ```
956    pub async fn complete(
957        &mut self,
958        handler_name: &str,
959        argument_value: &str,
960    ) -> Result<turbomcp_protocol::types::CompletionResponse> {
961        if !self.initialized {
962            return Err(Error::bad_request("Client not initialized"));
963        }
964
965        // Create proper completion request using protocol types
966        use turbomcp_protocol::types::{
967            ArgumentInfo, CompleteRequestParams, CompletionReference, PromptReferenceData,
968        };
969
970        let request_params = CompleteRequestParams {
971            argument: ArgumentInfo {
972                name: "partial".to_string(),
973                value: argument_value.to_string(),
974            },
975            reference: CompletionReference::Prompt(PromptReferenceData {
976                name: handler_name.to_string(),
977                title: None,
978            }),
979            context: None,
980            _meta: None,
981        };
982
983        let serialized_params = serde_json::to_value(&request_params)?;
984
985        with_plugins!(self, "completion/complete", serialized_params, {
986            // Core protocol call - plugins execute automatically around this
987            let result: CompleteResult = self
988                .protocol
989                .request("completion/complete", Some(serialized_params))
990                .await?;
991
992            Ok(result.completion)
993        })
994    }
995
996    /// Complete a prompt argument with full MCP protocol support
997    ///
998    /// This method provides access to the complete MCP completion protocol,
999    /// allowing specification of argument names, prompt references, and context.
1000    ///
1001    /// # Arguments
1002    ///
1003    /// * `prompt_name` - Name of the prompt to complete for
1004    /// * `argument_name` - Name of the argument being completed
1005    /// * `argument_value` - Current value for completion matching
1006    /// * `context` - Optional context with previously resolved arguments
1007    ///
1008    /// # Examples
1009    ///
1010    /// ```rust,no_run
1011    /// # use turbomcp_client::Client;
1012    /// # use turbomcp_transport::stdio::StdioTransport;
1013    /// # use turbomcp_protocol::types::CompletionContext;
1014    /// # use std::collections::HashMap;
1015    /// # async fn example() -> turbomcp_core::Result<()> {
1016    /// let mut client = Client::new(StdioTransport::new());
1017    /// client.initialize().await?;
1018    ///
1019    /// // Complete with context
1020    /// let mut context_args = HashMap::new();
1021    /// context_args.insert("language".to_string(), "rust".to_string());
1022    /// let context = CompletionContext { arguments: Some(context_args) };
1023    ///
1024    /// let completions = client.complete_prompt(
1025    ///     "code_review",
1026    ///     "framework",
1027    ///     "tok",
1028    ///     Some(context)
1029    /// ).await?;
1030    ///
1031    /// for completion in completions.values {
1032    ///     println!("Suggestion: {}", completion);
1033    /// }
1034    /// # Ok(())
1035    /// # }
1036    /// ```
1037    pub async fn complete_prompt(
1038        &mut self,
1039        prompt_name: &str,
1040        argument_name: &str,
1041        argument_value: &str,
1042        context: Option<turbomcp_protocol::types::CompletionContext>,
1043    ) -> Result<turbomcp_protocol::types::CompletionResponse> {
1044        if !self.initialized {
1045            return Err(Error::bad_request("Client not initialized"));
1046        }
1047
1048        use turbomcp_protocol::types::{
1049            ArgumentInfo, CompleteRequestParams, CompletionReference, PromptReferenceData,
1050        };
1051
1052        let request_params = CompleteRequestParams {
1053            argument: ArgumentInfo {
1054                name: argument_name.to_string(),
1055                value: argument_value.to_string(),
1056            },
1057            reference: CompletionReference::Prompt(PromptReferenceData {
1058                name: prompt_name.to_string(),
1059                title: None,
1060            }),
1061            context,
1062            _meta: None,
1063        };
1064
1065        let serialized_params = serde_json::to_value(&request_params)?;
1066
1067        with_plugins!(self, "completion/complete", serialized_params, {
1068            let result: CompleteResult = self
1069                .protocol
1070                .request("completion/complete", Some(serialized_params))
1071                .await?;
1072
1073            Ok(result.completion)
1074        })
1075    }
1076
1077    /// Complete a resource template URI with full MCP protocol support
1078    ///
1079    /// This method provides completion for resource template URIs, allowing
1080    /// servers to suggest values for URI template variables.
1081    ///
1082    /// # Arguments
1083    ///
1084    /// * `resource_uri` - Resource template URI (e.g., "/files/{path}")
1085    /// * `argument_name` - Name of the argument being completed
1086    /// * `argument_value` - Current value for completion matching
1087    /// * `context` - Optional context with previously resolved arguments
1088    ///
1089    /// # Examples
1090    ///
1091    /// ```rust,no_run
1092    /// # use turbomcp_client::Client;
1093    /// # use turbomcp_transport::stdio::StdioTransport;
1094    /// # async fn example() -> turbomcp_core::Result<()> {
1095    /// let mut client = Client::new(StdioTransport::new());
1096    /// client.initialize().await?;
1097    ///
1098    /// let completions = client.complete_resource(
1099    ///     "/files/{path}",
1100    ///     "path",
1101    ///     "/home/user/doc",
1102    ///     None
1103    /// ).await?;
1104    ///
1105    /// for completion in completions.values {
1106    ///     println!("Path suggestion: {}", completion);
1107    /// }
1108    /// # Ok(())
1109    /// # }
1110    /// ```
1111    pub async fn complete_resource(
1112        &mut self,
1113        resource_uri: &str,
1114        argument_name: &str,
1115        argument_value: &str,
1116        context: Option<turbomcp_protocol::types::CompletionContext>,
1117    ) -> Result<turbomcp_protocol::types::CompletionResponse> {
1118        if !self.initialized {
1119            return Err(Error::bad_request("Client not initialized"));
1120        }
1121
1122        use turbomcp_protocol::types::{
1123            ArgumentInfo, CompleteRequestParams, CompletionReference, ResourceTemplateReferenceData,
1124        };
1125
1126        let request_params = CompleteRequestParams {
1127            argument: ArgumentInfo {
1128                name: argument_name.to_string(),
1129                value: argument_value.to_string(),
1130            },
1131            reference: CompletionReference::ResourceTemplate(ResourceTemplateReferenceData {
1132                uri: resource_uri.to_string(),
1133            }),
1134            context,
1135            _meta: None,
1136        };
1137
1138        let serialized_params = serde_json::to_value(&request_params)?;
1139
1140        with_plugins!(self, "completion/complete", serialized_params, {
1141            let result: CompleteResult = self
1142                .protocol
1143                .request("completion/complete", Some(serialized_params))
1144                .await?;
1145
1146            Ok(result.completion)
1147        })
1148    }
1149
1150    /// List available resources from the server
1151    ///
1152    /// # Examples
1153    ///
1154    /// ```rust,no_run
1155    /// # use turbomcp_client::Client;
1156    /// # use turbomcp_transport::stdio::StdioTransport;
1157    /// # async fn example() -> turbomcp_core::Result<()> {
1158    /// let mut client = Client::new(StdioTransport::new());
1159    /// client.initialize().await?;
1160    ///
1161    /// let resources = client.list_resources().await?;
1162    /// for resource in resources {
1163    ///     println!("Available resource: {}", resource);
1164    /// }
1165    /// # Ok(())
1166    /// # }
1167    /// ```
1168    pub async fn list_resources(&mut self) -> Result<Vec<String>> {
1169        if !self.initialized {
1170            return Err(Error::bad_request("Client not initialized"));
1171        }
1172
1173        // Execute with plugin middleware
1174        let response: ListResourcesResult =
1175            self.execute_with_plugins("resources/list", None).await?;
1176
1177        let resource_uris = response
1178            .resources
1179            .into_iter()
1180            .map(|resource| resource.uri)
1181            .collect();
1182        Ok(resource_uris)
1183    }
1184
1185    /// Send a ping request to check server health and connectivity
1186    ///
1187    /// Sends a ping request to the server to verify the connection is active
1188    /// and the server is responding. This is useful for health checks and
1189    /// connection validation.
1190    ///
1191    /// # Returns
1192    ///
1193    /// Returns `PingResult` on successful ping.
1194    ///
1195    /// # Errors
1196    ///
1197    /// Returns an error if:
1198    /// - The client is not initialized
1199    /// - The server is not responding
1200    /// - The connection has failed
1201    ///
1202    /// # Examples
1203    ///
1204    /// ```rust,no_run
1205    /// # use turbomcp_client::Client;
1206    /// # use turbomcp_transport::stdio::StdioTransport;
1207    /// # async fn example() -> turbomcp_core::Result<()> {
1208    /// let mut client = Client::new(StdioTransport::new());
1209    /// client.initialize().await?;
1210    ///
1211    /// let result = client.ping().await?;
1212    /// println!("Server is responding");
1213    /// # Ok(())
1214    /// # }
1215    /// ```
1216    pub async fn ping(&mut self) -> Result<PingResult> {
1217        if !self.initialized {
1218            return Err(Error::bad_request("Client not initialized"));
1219        }
1220
1221        // Send ping request with plugin middleware (no parameters needed)
1222        let response: PingResult = self.execute_with_plugins("ping", None).await?;
1223        Ok(response)
1224    }
1225
1226    /// Read the content of a specific resource by URI
1227    ///
1228    /// Retrieves the content of a resource identified by its URI. This allows
1229    /// clients to access specific files, documents, or other resources
1230    /// provided by the server.
1231    ///
1232    /// # Arguments
1233    ///
1234    /// * `uri` - The URI of the resource to read
1235    ///
1236    /// # Returns
1237    ///
1238    /// Returns `ReadResourceResult` containing the resource content.
1239    ///
1240    /// # Errors
1241    ///
1242    /// Returns an error if:
1243    /// - The client is not initialized
1244    /// - The URI is invalid or empty
1245    /// - The resource doesn't exist
1246    /// - Access to the resource is denied
1247    ///
1248    /// # Examples
1249    ///
1250    /// ```rust,no_run
1251    /// # use turbomcp_client::Client;
1252    /// # use turbomcp_transport::stdio::StdioTransport;
1253    /// # async fn example() -> turbomcp_core::Result<()> {
1254    /// let mut client = Client::new(StdioTransport::new());
1255    /// client.initialize().await?;
1256    ///
1257    /// let result = client.read_resource("file:///path/to/document.txt").await?;
1258    /// for content in result.contents {
1259    ///     println!("Resource content: {:?}", content);
1260    /// }
1261    /// # Ok(())
1262    /// # }
1263    /// ```
1264    pub async fn read_resource(&mut self, uri: &str) -> Result<ReadResourceResult> {
1265        if !self.initialized {
1266            return Err(Error::bad_request("Client not initialized"));
1267        }
1268
1269        if uri.is_empty() {
1270            return Err(Error::bad_request("Resource URI cannot be empty"));
1271        }
1272
1273        // Send read_resource request
1274        let request = ReadResourceRequest {
1275            uri: uri.to_string(),
1276            _meta: None,
1277        };
1278
1279        let response: ReadResourceResult = self
1280            .execute_with_plugins("resources/read", Some(serde_json::to_value(request)?))
1281            .await?;
1282        Ok(response)
1283    }
1284
1285    /// List available prompt templates from the server
1286    ///
1287    /// Retrieves the complete list of prompt templates that the server provides,
1288    /// including all metadata: title, description, and argument schemas. This is
1289    /// the MCP-compliant implementation that provides everything needed for UI generation
1290    /// and dynamic form creation.
1291    ///
1292    /// # Returns
1293    ///
1294    /// Returns a vector of `Prompt` objects containing:
1295    /// - `name`: Programmatic identifier
1296    /// - `title`: Human-readable display name (optional)
1297    /// - `description`: Description of what the prompt does (optional)
1298    /// - `arguments`: Array of argument schemas with validation info (optional)
1299    ///
1300    /// # Errors
1301    ///
1302    /// Returns an error if:
1303    /// - The client is not initialized
1304    /// - The server doesn't support prompts
1305    /// - The request fails
1306    ///
1307    /// # Examples
1308    ///
1309    /// ```rust,no_run
1310    /// # use turbomcp_client::Client;
1311    /// # use turbomcp_transport::stdio::StdioTransport;
1312    /// # async fn example() -> turbomcp_core::Result<()> {
1313    /// let mut client = Client::new(StdioTransport::new());
1314    /// client.initialize().await?;
1315    ///
1316    /// let prompts = client.list_prompts().await?;
1317    /// for prompt in prompts {
1318    ///     println!("Prompt: {} ({})", prompt.name, prompt.title.unwrap_or("No title".to_string()));
1319    ///     if let Some(args) = prompt.arguments {
1320    ///         println!("  Arguments: {:?}", args);
1321    ///         for arg in args {
1322    ///             let required = arg.required.unwrap_or(false);
1323    ///             println!("    - {}: {} (required: {})", arg.name,
1324    ///                     arg.description.unwrap_or("No description".to_string()), required);
1325    ///         }
1326    ///     }
1327    /// }
1328    /// # Ok(())
1329    /// # }
1330    /// ```
1331    pub async fn list_prompts(&mut self) -> Result<Vec<Prompt>> {
1332        if !self.initialized {
1333            return Err(Error::bad_request("Client not initialized"));
1334        }
1335
1336        // Execute with plugin middleware - return full Prompt objects per MCP spec
1337        let response: ListPromptsResult = self.execute_with_plugins("prompts/list", None).await?;
1338        Ok(response.prompts)
1339    }
1340
1341    /// Get a specific prompt template with argument support
1342    ///
1343    /// Retrieves a specific prompt template from the server with support for
1344    /// parameter substitution. When arguments are provided, the server will
1345    /// substitute them into the prompt template using {parameter} syntax.
1346    ///
1347    /// This is the MCP-compliant implementation that supports the full protocol specification.
1348    ///
1349    /// # Arguments
1350    ///
1351    /// * `name` - The name of the prompt to retrieve
1352    /// * `arguments` - Optional parameters for template substitution
1353    ///
1354    /// # Returns
1355    ///
1356    /// Returns `GetPromptResult` containing the prompt template with parameters substituted.
1357    ///
1358    /// # Errors
1359    ///
1360    /// Returns an error if:
1361    /// - The client is not initialized
1362    /// - The prompt name is empty
1363    /// - The prompt doesn't exist
1364    /// - Required arguments are missing
1365    /// - Argument types don't match schema
1366    /// - The request fails
1367    ///
1368    /// # Examples
1369    ///
1370    /// ```rust,no_run
1371    /// # use turbomcp_client::Client;
1372    /// # use turbomcp_transport::stdio::StdioTransport;
1373    /// # use turbomcp_protocol::PromptInput;
1374    /// # use std::collections::HashMap;
1375    /// # async fn example() -> turbomcp_core::Result<()> {
1376    /// let mut client = Client::new(StdioTransport::new());
1377    /// client.initialize().await?;
1378    ///
1379    /// // Get prompt without arguments (template form)
1380    /// let template = client.get_prompt("greeting", None).await?;
1381    /// println!("Template has {} messages", template.messages.len());
1382    ///
1383    /// // Get prompt with arguments (substituted form)
1384    /// let mut args = HashMap::new();
1385    /// args.insert("name".to_string(), serde_json::Value::String("Alice".to_string()));
1386    /// args.insert("greeting".to_string(), serde_json::Value::String("Hello".to_string()));
1387    ///
1388    /// let result = client.get_prompt("greeting", Some(args)).await?;
1389    /// println!("Generated prompt with {} messages", result.messages.len());
1390    /// # Ok(())
1391    /// # }
1392    /// ```
1393    pub async fn get_prompt(
1394        &mut self,
1395        name: &str,
1396        arguments: Option<PromptInput>,
1397    ) -> Result<GetPromptResult> {
1398        if !self.initialized {
1399            return Err(Error::bad_request("Client not initialized"));
1400        }
1401
1402        if name.is_empty() {
1403            return Err(Error::bad_request("Prompt name cannot be empty"));
1404        }
1405
1406        // Send prompts/get request with full argument support
1407        let request = GetPromptRequest {
1408            name: name.to_string(),
1409            arguments, // Support for parameter substitution
1410            _meta: None,
1411        };
1412
1413        self.execute_with_plugins("prompts/get", Some(serde_json::to_value(request).unwrap()))
1414            .await
1415    }
1416
1417    /// List available filesystem root directories
1418    ///
1419    /// Retrieves the list of root directories that the server has access to.
1420    /// This is useful for understanding what parts of the filesystem are
1421    /// available for resource access.
1422    ///
1423    /// # Returns
1424    ///
1425    /// Returns a vector of root directory URIs available on the server.
1426    ///
1427    /// # Errors
1428    ///
1429    /// Returns an error if:
1430    /// - The client is not initialized
1431    /// - The server doesn't support filesystem access
1432    /// - The request fails
1433    ///
1434    /// # Examples
1435    ///
1436    /// ```rust,no_run
1437    /// # use turbomcp_client::Client;
1438    /// # use turbomcp_transport::stdio::StdioTransport;
1439    /// # async fn example() -> turbomcp_core::Result<()> {
1440    /// let mut client = Client::new(StdioTransport::new());
1441    /// client.initialize().await?;
1442    ///
1443    /// let roots = client.list_roots().await?;
1444    /// for root_uri in roots {
1445    ///     println!("Available root: {}", root_uri);
1446    /// }
1447    /// # Ok(())
1448    /// # }
1449    /// ```
1450    pub async fn list_roots(&mut self) -> Result<Vec<String>> {
1451        if !self.initialized {
1452            return Err(Error::bad_request("Client not initialized"));
1453        }
1454
1455        // Send roots/list request with plugin middleware
1456        let response: ListRootsResult = self.execute_with_plugins("roots/list", None).await?;
1457        let root_uris = response.roots.into_iter().map(|root| root.uri).collect();
1458        Ok(root_uris)
1459    }
1460
1461    /// Set the logging level for the server
1462    ///
1463    /// Controls the verbosity of server logging. This allows clients to
1464    /// adjust the amount of log information they receive from the server.
1465    ///
1466    /// # Arguments
1467    ///
1468    /// * `level` - The desired logging level (Error, Warn, Info, Debug)
1469    ///
1470    /// # Returns
1471    ///
1472    /// Returns `SetLevelResult` on successful level change.
1473    ///
1474    /// # Errors
1475    ///
1476    /// Returns an error if:
1477    /// - The client is not initialized
1478    /// - The server doesn't support logging control
1479    /// - The request fails
1480    ///
1481    /// # Examples
1482    ///
1483    /// ```rust,no_run
1484    /// # use turbomcp_client::Client;
1485    /// # use turbomcp_transport::stdio::StdioTransport;
1486    /// # use turbomcp_protocol::types::LogLevel;
1487    /// # async fn example() -> turbomcp_core::Result<()> {
1488    /// let mut client = Client::new(StdioTransport::new());
1489    /// client.initialize().await?;
1490    ///
1491    /// // Set server to debug logging
1492    /// client.set_log_level(LogLevel::Debug).await?;
1493    /// println!("Server logging level set to debug");
1494    /// # Ok(())
1495    /// # }
1496    /// ```
1497    pub async fn set_log_level(&mut self, level: LogLevel) -> Result<SetLevelResult> {
1498        if !self.initialized {
1499            return Err(Error::bad_request("Client not initialized"));
1500        }
1501
1502        // Send logging/setLevel request
1503        let request = SetLevelRequest { level };
1504
1505        let response: SetLevelResult = self
1506            .execute_with_plugins("logging/setLevel", Some(serde_json::to_value(request)?))
1507            .await?;
1508        Ok(response)
1509    }
1510
1511    /// Subscribe to resource change notifications
1512    ///
1513    /// Registers interest in receiving notifications when the specified
1514    /// resource changes. The server will send notifications when the
1515    /// resource is modified, created, or deleted.
1516    ///
1517    /// # Arguments
1518    ///
1519    /// * `uri` - The URI of the resource to monitor
1520    ///
1521    /// # Returns
1522    ///
1523    /// Returns `EmptyResult` on successful subscription.
1524    ///
1525    /// # Errors
1526    ///
1527    /// Returns an error if:
1528    /// - The client is not initialized
1529    /// - The URI is invalid or empty
1530    /// - The server doesn't support subscriptions
1531    /// - The request fails
1532    ///
1533    /// # Examples
1534    ///
1535    /// ```rust,no_run
1536    /// # use turbomcp_client::Client;
1537    /// # use turbomcp_transport::stdio::StdioTransport;
1538    /// # async fn example() -> turbomcp_core::Result<()> {
1539    /// let mut client = Client::new(StdioTransport::new());
1540    /// client.initialize().await?;
1541    ///
1542    /// // Subscribe to file changes
1543    /// client.subscribe("file:///watch/directory").await?;
1544    /// println!("Subscribed to resource changes");
1545    /// # Ok(())
1546    /// # }
1547    /// ```
1548    pub async fn subscribe(&mut self, uri: &str) -> Result<EmptyResult> {
1549        if !self.initialized {
1550            return Err(Error::bad_request("Client not initialized"));
1551        }
1552
1553        if uri.is_empty() {
1554            return Err(Error::bad_request("Subscription URI cannot be empty"));
1555        }
1556
1557        // Send resources/subscribe request with plugin middleware
1558        let request = SubscribeRequest {
1559            uri: uri.to_string(),
1560        };
1561
1562        self.execute_with_plugins(
1563            "resources/subscribe",
1564            Some(serde_json::to_value(request).unwrap()),
1565        )
1566        .await
1567    }
1568
1569    /// Unsubscribe from resource change notifications
1570    ///
1571    /// Cancels a previous subscription to resource changes. After unsubscribing,
1572    /// the client will no longer receive notifications for the specified resource.
1573    ///
1574    /// # Arguments
1575    ///
1576    /// * `uri` - The URI of the resource to stop monitoring
1577    ///
1578    /// # Returns
1579    ///
1580    /// Returns `EmptyResult` on successful unsubscription.
1581    ///
1582    /// # Errors
1583    ///
1584    /// Returns an error if:
1585    /// - The client is not initialized
1586    /// - The URI is invalid or empty
1587    /// - No active subscription exists for the URI
1588    /// - The request fails
1589    ///
1590    /// # Examples
1591    ///
1592    /// ```rust,no_run
1593    /// # use turbomcp_client::Client;
1594    /// # use turbomcp_transport::stdio::StdioTransport;
1595    /// # async fn example() -> turbomcp_core::Result<()> {
1596    /// let mut client = Client::new(StdioTransport::new());
1597    /// client.initialize().await?;
1598    ///
1599    /// // Unsubscribe from file changes
1600    /// client.unsubscribe("file:///watch/directory").await?;
1601    /// println!("Unsubscribed from resource changes");
1602    /// # Ok(())
1603    /// # }
1604    /// ```
1605    pub async fn unsubscribe(&mut self, uri: &str) -> Result<EmptyResult> {
1606        if !self.initialized {
1607            return Err(Error::bad_request("Client not initialized"));
1608        }
1609
1610        if uri.is_empty() {
1611            return Err(Error::bad_request("Unsubscription URI cannot be empty"));
1612        }
1613
1614        // Send resources/unsubscribe request with plugin middleware
1615        let request = UnsubscribeRequest {
1616            uri: uri.to_string(),
1617        };
1618
1619        self.execute_with_plugins(
1620            "resources/unsubscribe",
1621            Some(serde_json::to_value(request).unwrap()),
1622        )
1623        .await
1624    }
1625
1626    /// List available resource templates
1627    ///
1628    /// Retrieves the list of resource templates that define URI patterns
1629    /// for accessing different types of resources. Templates help clients
1630    /// understand what resources are available and how to access them.
1631    ///
1632    /// # Returns
1633    ///
1634    /// Returns a vector of resource template URI patterns.
1635    ///
1636    /// # Errors
1637    ///
1638    /// Returns an error if:
1639    /// - The client is not initialized
1640    /// - The server doesn't support resource templates
1641    /// - The request fails
1642    ///
1643    /// # Examples
1644    ///
1645    /// ```rust,no_run
1646    /// # use turbomcp_client::Client;
1647    /// # use turbomcp_transport::stdio::StdioTransport;
1648    /// # async fn example() -> turbomcp_core::Result<()> {
1649    /// let mut client = Client::new(StdioTransport::new());
1650    /// client.initialize().await?;
1651    ///
1652    /// let templates = client.list_resource_templates().await?;
1653    /// for template in templates {
1654    ///     println!("Resource template: {}", template);
1655    /// }
1656    /// # Ok(())
1657    /// # }
1658    /// ```
1659    pub async fn list_resource_templates(&mut self) -> Result<Vec<String>> {
1660        if !self.initialized {
1661            return Err(Error::bad_request("Client not initialized"));
1662        }
1663
1664        // Send resources/templates request with plugin middleware
1665        let response: ListResourceTemplatesResult = self
1666            .execute_with_plugins("resources/templates", None)
1667            .await?;
1668        let template_uris = response
1669            .resource_templates
1670            .into_iter()
1671            .map(|template| template.uri_template)
1672            .collect();
1673        Ok(template_uris)
1674    }
1675
1676    // ============================================================================
1677    // HANDLER REGISTRATION METHODS
1678    // ============================================================================
1679
1680    /// Register an elicitation handler for processing user input requests
1681    ///
1682    /// Elicitation handlers are called when the server needs user input during
1683    /// operations. The handler should present the request to the user and
1684    /// collect their response according to the provided schema.
1685    ///
1686    /// # Arguments
1687    ///
1688    /// * `handler` - The elicitation handler implementation
1689    ///
1690    /// # Examples
1691    ///
1692    /// ```rust,no_run
1693    /// use turbomcp_client::Client;
1694    /// use turbomcp_client::handlers::{ElicitationHandler, ElicitationRequest, ElicitationResponse, ElicitationAction, HandlerResult};
1695    /// use turbomcp_transport::stdio::StdioTransport;
1696    /// use async_trait::async_trait;
1697    /// use std::sync::Arc;
1698    /// use serde_json::json;
1699    ///
1700    /// #[derive(Debug)]
1701    /// struct MyElicitationHandler;
1702    ///
1703    /// #[async_trait]
1704    /// impl ElicitationHandler for MyElicitationHandler {
1705    ///     async fn handle_elicitation(
1706    ///         &self,
1707    ///         request: ElicitationRequest,
1708    ///     ) -> HandlerResult<ElicitationResponse> {
1709    ///         Ok(ElicitationResponse {
1710    ///             action: ElicitationAction::Accept,
1711    ///             content: Some(json!({"user_input": "example"})),
1712    ///         })
1713    ///     }
1714    /// }
1715    ///
1716    /// let mut client = Client::new(StdioTransport::new());
1717    /// client.on_elicitation(Arc::new(MyElicitationHandler));
1718    /// ```
1719    pub fn on_elicitation(&mut self, handler: Arc<dyn ElicitationHandler>) {
1720        self.handlers.set_elicitation_handler(handler);
1721    }
1722
1723    /// Register a progress handler for processing operation progress updates
1724    ///
1725    /// Progress handlers receive notifications about long-running server operations.
1726    /// This allows clients to display progress bars, status updates, or other
1727    /// feedback to users.
1728    ///
1729    /// # Arguments
1730    ///
1731    /// * `handler` - The progress handler implementation
1732    ///
1733    /// # Examples
1734    ///
1735    /// ```rust,no_run
1736    /// use turbomcp_client::Client;
1737    /// use turbomcp_client::handlers::{ProgressHandler, ProgressNotification, HandlerResult};
1738    /// use turbomcp_transport::stdio::StdioTransport;
1739    /// use async_trait::async_trait;
1740    /// use std::sync::Arc;
1741    ///
1742    /// #[derive(Debug)]
1743    /// struct MyProgressHandler;
1744    ///
1745    /// #[async_trait]
1746    /// impl ProgressHandler for MyProgressHandler {
1747    ///     async fn handle_progress(&self, notification: ProgressNotification) -> HandlerResult<()> {
1748    ///         println!("Progress: {:?}", notification);
1749    ///         Ok(())
1750    ///     }
1751    /// }
1752    ///
1753    /// let mut client = Client::new(StdioTransport::new());
1754    /// client.on_progress(Arc::new(MyProgressHandler));
1755    /// ```
1756    pub fn on_progress(&mut self, handler: Arc<dyn ProgressHandler>) {
1757        self.handlers.set_progress_handler(handler);
1758    }
1759
1760    /// Register a log handler for processing server log messages
1761    ///
1762    /// Log handlers receive log messages from the server and can route them
1763    /// to the client's logging system. This is useful for debugging and
1764    /// maintaining a unified log across client and server.
1765    ///
1766    /// # Arguments
1767    ///
1768    /// * `handler` - The log handler implementation
1769    ///
1770    /// # Examples
1771    ///
1772    /// ```rust,no_run
1773    /// use turbomcp_client::Client;
1774    /// use turbomcp_client::handlers::{LogHandler, LogMessage, HandlerResult};
1775    /// use turbomcp_transport::stdio::StdioTransport;
1776    /// use async_trait::async_trait;
1777    /// use std::sync::Arc;
1778    ///
1779    /// #[derive(Debug)]
1780    /// struct MyLogHandler;
1781    ///
1782    /// #[async_trait]
1783    /// impl LogHandler for MyLogHandler {
1784    ///     async fn handle_log(&self, log: LogMessage) -> HandlerResult<()> {
1785    ///         println!("Server log: {}", log.message);
1786    ///         Ok(())
1787    ///     }
1788    /// }
1789    ///
1790    /// let mut client = Client::new(StdioTransport::new());
1791    /// client.on_log(Arc::new(MyLogHandler));
1792    /// ```
1793    pub fn on_log(&mut self, handler: Arc<dyn LogHandler>) {
1794        self.handlers.set_log_handler(handler);
1795    }
1796
1797    /// Register a resource update handler for processing resource change notifications
1798    ///
1799    /// Resource update handlers receive notifications when subscribed resources
1800    /// change on the server. This enables reactive updates to cached data or
1801    /// UI refreshes when server-side resources change.
1802    ///
1803    /// # Arguments
1804    ///
1805    /// * `handler` - The resource update handler implementation
1806    ///
1807    /// # Examples
1808    ///
1809    /// ```rust,no_run
1810    /// use turbomcp_client::Client;
1811    /// use turbomcp_client::handlers::{ResourceUpdateHandler, ResourceUpdateNotification, HandlerResult};
1812    /// use turbomcp_transport::stdio::StdioTransport;
1813    /// use async_trait::async_trait;
1814    /// use std::sync::Arc;
1815    ///
1816    /// #[derive(Debug)]
1817    /// struct MyResourceUpdateHandler;
1818    ///
1819    /// #[async_trait]
1820    /// impl ResourceUpdateHandler for MyResourceUpdateHandler {
1821    ///     async fn handle_resource_update(
1822    ///         &self,
1823    ///         notification: ResourceUpdateNotification,
1824    ///     ) -> HandlerResult<()> {
1825    ///         println!("Resource updated: {}", notification.uri);
1826    ///         Ok(())
1827    ///     }
1828    /// }
1829    ///
1830    /// let mut client = Client::new(StdioTransport::new());
1831    /// client.on_resource_update(Arc::new(MyResourceUpdateHandler));
1832    /// ```
1833    pub fn on_resource_update(&mut self, handler: Arc<dyn ResourceUpdateHandler>) {
1834        self.handlers.set_resource_update_handler(handler);
1835    }
1836
1837    /// Check if an elicitation handler is registered
1838    pub fn has_elicitation_handler(&self) -> bool {
1839        self.handlers.has_elicitation_handler()
1840    }
1841
1842    /// Check if a progress handler is registered
1843    pub fn has_progress_handler(&self) -> bool {
1844        self.handlers.has_progress_handler()
1845    }
1846
1847    /// Check if a log handler is registered
1848    pub fn has_log_handler(&self) -> bool {
1849        self.handlers.has_log_handler()
1850    }
1851
1852    /// Check if a resource update handler is registered
1853    pub fn has_resource_update_handler(&self) -> bool {
1854        self.handlers.has_resource_update_handler()
1855    }
1856
1857    /// Get the client's capabilities configuration
1858    pub fn capabilities(&self) -> &ClientCapabilities {
1859        &self.capabilities
1860    }
1861
1862    // ============================================================================
1863    // PLUGIN MANAGEMENT
1864    // ============================================================================
1865
1866    /// Register a plugin with the client
1867    ///
1868    /// # Arguments
1869    ///
1870    /// * `plugin` - The plugin to register
1871    ///
1872    /// # Examples
1873    ///
1874    /// ```rust,no_run
1875    /// use turbomcp_client::plugins::{MetricsPlugin, PluginConfig};
1876    /// use std::sync::Arc;
1877    ///
1878    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1879    /// # let mut client = turbomcp_client::Client::new(turbomcp_transport::stdio::StdioTransport::new());
1880    /// let metrics_plugin = Arc::new(MetricsPlugin::new(PluginConfig::Metrics));
1881    /// client.register_plugin(metrics_plugin).await?;
1882    /// # Ok(())
1883    /// # }
1884    /// ```
1885    pub async fn register_plugin(
1886        &mut self,
1887        plugin: std::sync::Arc<dyn crate::plugins::ClientPlugin>,
1888    ) -> Result<()> {
1889        self.plugin_registry
1890            .register_plugin(plugin)
1891            .await
1892            .map_err(|e| Error::bad_request(format!("Failed to register plugin: {}", e)))
1893    }
1894
1895    /// Check if a plugin is registered
1896    ///
1897    /// # Arguments
1898    ///
1899    /// * `name` - The name of the plugin to check
1900    pub fn has_plugin(&self, name: &str) -> bool {
1901        self.plugin_registry.has_plugin(name)
1902    }
1903
1904    /// Get plugin data for a specific plugin type
1905    ///
1906    /// # Arguments
1907    ///
1908    /// * `name` - The name of the plugin
1909    ///
1910    /// # Examples
1911    ///
1912    /// ```rust,no_run
1913    /// use turbomcp_client::plugins::MetricsPlugin;
1914    ///
1915    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1916    /// # let client = turbomcp_client::Client::new(turbomcp_transport::stdio::StdioTransport::new());
1917    /// if let Some(plugin) = client.get_plugin("metrics") {
1918    ///     // Use plugin data
1919    /// }
1920    /// # Ok(())
1921    /// # }
1922    /// ```
1923    pub fn get_plugin(
1924        &self,
1925        name: &str,
1926    ) -> Option<std::sync::Arc<dyn crate::plugins::ClientPlugin>> {
1927        self.plugin_registry.get_plugin(name)
1928    }
1929
1930    /// Initialize all registered plugins
1931    ///
1932    /// This should be called after registration but before using the client.
1933    pub async fn initialize_plugins(&mut self) -> Result<()> {
1934        // Set up client context for plugins with actual client capabilities
1935        let mut capabilities = std::collections::HashMap::new();
1936        capabilities.insert(
1937            "protocol_version".to_string(),
1938            serde_json::json!("2024-11-05"),
1939        );
1940        capabilities.insert(
1941            "mcp_version".to_string(),
1942            serde_json::json!(env!("CARGO_PKG_VERSION")),
1943        );
1944        capabilities.insert(
1945            "supports_notifications".to_string(),
1946            serde_json::json!(true),
1947        );
1948        capabilities.insert(
1949            "supports_sampling".to_string(),
1950            serde_json::json!(self.sampling_handler.is_some()),
1951        );
1952        capabilities.insert("supports_progress".to_string(), serde_json::json!(true));
1953        capabilities.insert("supports_roots".to_string(), serde_json::json!(true));
1954
1955        // Extract client configuration
1956        let mut config = std::collections::HashMap::new();
1957        config.insert(
1958            "client_name".to_string(),
1959            serde_json::json!("turbomcp-client"),
1960        );
1961        config.insert(
1962            "initialized".to_string(),
1963            serde_json::json!(self.initialized),
1964        );
1965        config.insert(
1966            "plugin_count".to_string(),
1967            serde_json::json!(self.plugin_registry.plugin_count()),
1968        );
1969
1970        let context = crate::plugins::PluginContext::new(
1971            "turbomcp-client".to_string(),
1972            env!("CARGO_PKG_VERSION").to_string(),
1973            capabilities,
1974            config,
1975            vec![], // Will be populated by the registry
1976        );
1977
1978        self.plugin_registry.set_client_context(context);
1979
1980        // Note: Individual plugins are initialized automatically during registration
1981        // via PluginRegistry::register_plugin(). This method ensures the registry
1982        // has proper client context for any future plugin registrations.
1983        Ok(())
1984    }
1985
1986    /// Cleanup all registered plugins
1987    ///
1988    /// This should be called when the client is being shut down.
1989    pub async fn cleanup_plugins(&mut self) -> Result<()> {
1990        // Clear the plugin registry - plugins will be dropped and cleaned up automatically
1991        // The Rust ownership system ensures proper cleanup when the Arc<dyn ClientPlugin>
1992        // references are dropped.
1993
1994        // Note: The plugin system uses RAII (Resource Acquisition Is Initialization)
1995        // pattern where plugins clean up their resources in their Drop implementation.
1996        // No explicit cleanup is needed beyond clearing the registry.
1997
1998        self.plugin_registry = crate::plugins::PluginRegistry::new();
1999        Ok(())
2000    }
2001}
2002
2003/// Thread-safe wrapper for sharing Client across async tasks
2004///
2005/// This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
2006/// for concurrent access to MCP client functionality. It addresses the limitations
2007/// identified in PR feedback where Client requires `&mut self` for all operations
2008/// but needs to be shared across multiple async tasks.
2009///
2010/// # Design Rationale
2011///
2012/// All Client methods require `&mut self` because:
2013/// - MCP connections maintain state (initialized flag, connection status)
2014/// - Request correlation tracking for JSON-RPC requires mutation
2015/// - Handler and plugin registries need mutable access
2016///
2017/// While Client implements Send + Sync, this only means it's safe to move/share
2018/// between threads, not that multiple tasks can mutate it concurrently.
2019///
2020/// # Examples
2021///
2022/// ```rust,no_run
2023/// use turbomcp_client::{Client, SharedClient};
2024/// use turbomcp_transport::stdio::StdioTransport;
2025///
2026/// # async fn example() -> turbomcp_core::Result<()> {
2027/// let transport = StdioTransport::new();
2028/// let client = Client::new(transport);
2029/// let shared = SharedClient::new(client);
2030///
2031/// // Initialize once
2032/// shared.initialize().await?;
2033///
2034/// // Clone for sharing across tasks
2035/// let shared1 = shared.clone();
2036/// let shared2 = shared.clone();
2037///
2038/// // Both tasks can use the client concurrently
2039/// let handle1 = tokio::spawn(async move {
2040///     shared1.list_tools().await
2041/// });
2042///
2043/// let handle2 = tokio::spawn(async move {
2044///     shared2.list_prompts().await
2045/// });
2046///
2047/// let (tools, prompts) = tokio::try_join!(handle1, handle2).unwrap();
2048/// # Ok(())
2049/// # }
2050/// ```
2051pub struct SharedClient<T: Transport> {
2052    inner: Arc<Mutex<Client<T>>>,
2053}
2054
2055impl<T: Transport> SharedClient<T> {
2056    /// Create a new shared client wrapper
2057    ///
2058    /// Takes ownership of a Client and wraps it for thread-safe sharing.
2059    /// The original client can no longer be accessed directly after this call.
2060    pub fn new(client: Client<T>) -> Self {
2061        Self {
2062            inner: Arc::new(Mutex::new(client)),
2063        }
2064    }
2065
2066    /// Initialize the MCP connection
2067    ///
2068    /// This method should be called once before using any other client operations.
2069    /// It negotiates capabilities with the server and establishes the communication protocol.
2070    pub async fn initialize(&self) -> Result<InitializeResult> {
2071        self.inner.lock().await.initialize().await
2072    }
2073
2074    /// List all available tools from the MCP server
2075    ///
2076    /// Returns a list of complete tool definitions with schemas that can be used
2077    /// for form generation, validation, and documentation. Tools represent
2078    /// executable functions provided by the server.
2079    pub async fn list_tools(&self) -> Result<Vec<Tool>> {
2080        self.inner.lock().await.list_tools().await
2081    }
2082
2083    /// List available tool names from the MCP server
2084    ///
2085    /// Returns only the tool names for cases where full schemas are not needed.
2086    /// For most use cases, prefer `list_tools()` which provides complete tool definitions.
2087    pub async fn list_tool_names(&self) -> Result<Vec<String>> {
2088        self.inner.lock().await.list_tool_names().await
2089    }
2090
2091    /// Execute a tool with the given name and arguments
2092    ///
2093    /// Calls a specific tool on the MCP server with the provided arguments.
2094    /// The arguments should match the tool's expected parameter schema.
2095    pub async fn call_tool(
2096        &self,
2097        name: &str,
2098        arguments: Option<HashMap<String, serde_json::Value>>,
2099    ) -> Result<serde_json::Value> {
2100        self.inner.lock().await.call_tool(name, arguments).await
2101    }
2102
2103    /// List all available prompts from the MCP server
2104    ///
2105    /// Returns full Prompt objects with metadata including name, title, description,
2106    /// and argument schemas. This information can be used to generate UI forms
2107    /// for prompt parameter collection.
2108    pub async fn list_prompts(&self) -> Result<Vec<Prompt>> {
2109        self.inner.lock().await.list_prompts().await
2110    }
2111
2112    /// Get a prompt with optional argument substitution
2113    ///
2114    /// Retrieves a prompt from the server. If arguments are provided, template
2115    /// parameters (e.g., `{parameter}`) will be substituted with the given values.
2116    /// Pass `None` for arguments to get the raw template form.
2117    pub async fn get_prompt(
2118        &self,
2119        name: &str,
2120        arguments: Option<PromptInput>,
2121    ) -> Result<GetPromptResult> {
2122        self.inner.lock().await.get_prompt(name, arguments).await
2123    }
2124
2125    /// List available resources from the MCP server
2126    ///
2127    /// Resources represent data or content that can be read by the client.
2128    /// Returns a list of resource identifiers and metadata.
2129    pub async fn list_resources(&self) -> Result<Vec<String>> {
2130        self.inner.lock().await.list_resources().await
2131    }
2132
2133    /// Read a specific resource from the MCP server
2134    ///
2135    /// Retrieves the content of a resource identified by its URI.
2136    /// The content format depends on the specific resource type.
2137    pub async fn read_resource(&self, uri: &str) -> Result<ReadResourceResult> {
2138        self.inner.lock().await.read_resource(uri).await
2139    }
2140
2141    /// List resource templates from the MCP server
2142    ///
2143    /// Resource templates define patterns for generating resource URIs.
2144    /// They allow servers to describe families of related resources.
2145    pub async fn list_resource_templates(&self) -> Result<Vec<String>> {
2146        self.inner.lock().await.list_resource_templates().await
2147    }
2148
2149    /// Set the logging level for the MCP server
2150    ///
2151    /// Controls the verbosity of logs sent from the server to the client.
2152    /// Higher log levels provide more detailed information.
2153    pub async fn set_log_level(&self, level: LogLevel) -> Result<SetLevelResult> {
2154        self.inner.lock().await.set_log_level(level).await
2155    }
2156
2157    /// Subscribe to notifications from a specific URI
2158    ///
2159    /// Registers interest in receiving notifications when the specified
2160    /// resource or endpoint changes. Used for real-time updates.
2161    pub async fn subscribe(&self, uri: &str) -> Result<EmptyResult> {
2162        self.inner.lock().await.subscribe(uri).await
2163    }
2164
2165    /// Unsubscribe from notifications for a specific URI
2166    ///
2167    /// Removes a previously registered subscription to stop receiving
2168    /// notifications for the specified resource or endpoint.
2169    pub async fn unsubscribe(&self, uri: &str) -> Result<EmptyResult> {
2170        self.inner.lock().await.unsubscribe(uri).await
2171    }
2172
2173    /// Send a ping to test connection health
2174    ///
2175    /// Verifies that the MCP connection is still active and responsive.
2176    /// Used for health checking and keepalive functionality.
2177    pub async fn ping(&self) -> Result<PingResult> {
2178        self.inner.lock().await.ping().await
2179    }
2180
2181    /// Get the client's configured capabilities
2182    ///
2183    /// Returns the capabilities that this client supports.
2184    /// These are negotiated during initialization.
2185    pub async fn capabilities(&self) -> ClientCapabilities {
2186        let client = self.inner.lock().await;
2187        client.capabilities().clone()
2188    }
2189
2190    /// Request argument completion from the MCP server
2191    ///
2192    /// Provides autocompletion suggestions for prompt arguments and resource URIs.
2193    /// This enables rich, IDE-like experiences with contextual suggestions.
2194    ///
2195    /// # Arguments
2196    ///
2197    /// * `handler_name` - The completion handler name
2198    /// * `argument_value` - The partial value to complete
2199    ///
2200    /// # Examples
2201    ///
2202    /// ```rust,no_run
2203    /// # use turbomcp_client::{Client, SharedClient};
2204    /// # use turbomcp_transport::stdio::StdioTransport;
2205    /// # async fn example() -> turbomcp_core::Result<()> {
2206    /// let shared = SharedClient::new(Client::new(StdioTransport::new()));
2207    /// shared.initialize().await?;
2208    ///
2209    /// let result = shared.complete("complete_path", "/usr/b").await?;
2210    /// println!("Completions: {:?}", result.values);
2211    /// # Ok(())
2212    /// # }
2213    /// ```
2214    pub async fn complete(
2215        &self,
2216        handler_name: &str,
2217        argument_value: &str,
2218    ) -> Result<turbomcp_protocol::types::CompletionResponse> {
2219        self.inner
2220            .lock()
2221            .await
2222            .complete(handler_name, argument_value)
2223            .await
2224    }
2225
2226    /// Complete a prompt argument with full MCP protocol support
2227    ///
2228    /// This method provides access to the complete MCP completion protocol,
2229    /// allowing specification of argument names, prompt references, and context.
2230    ///
2231    /// # Arguments
2232    ///
2233    /// * `prompt_name` - Name of the prompt to complete for
2234    /// * `argument_name` - Name of the argument being completed
2235    /// * `argument_value` - Current value for completion matching
2236    /// * `context` - Optional context with previously resolved arguments
2237    ///
2238    /// # Examples
2239    ///
2240    /// ```rust,no_run
2241    /// # use turbomcp_client::{Client, SharedClient};
2242    /// # use turbomcp_transport::stdio::StdioTransport;
2243    /// # use turbomcp_protocol::types::CompletionContext;
2244    /// # use std::collections::HashMap;
2245    /// # async fn example() -> turbomcp_core::Result<()> {
2246    /// let shared = SharedClient::new(Client::new(StdioTransport::new()));
2247    /// shared.initialize().await?;
2248    ///
2249    /// // Complete with context
2250    /// let mut context_args = HashMap::new();
2251    /// context_args.insert("language".to_string(), "rust".to_string());
2252    /// let context = CompletionContext { arguments: Some(context_args) };
2253    ///
2254    /// let completions = shared.complete_prompt(
2255    ///     "code_review",
2256    ///     "framework",
2257    ///     "tok",
2258    ///     Some(context)
2259    /// ).await?;
2260    ///
2261    /// for completion in completions.values {
2262    ///     println!("Suggestion: {}", completion);
2263    /// }
2264    /// # Ok(())
2265    /// # }
2266    /// ```
2267    pub async fn complete_prompt(
2268        &self,
2269        prompt_name: &str,
2270        argument_name: &str,
2271        argument_value: &str,
2272        context: Option<turbomcp_protocol::types::CompletionContext>,
2273    ) -> Result<turbomcp_protocol::types::CompletionResponse> {
2274        self.inner
2275            .lock()
2276            .await
2277            .complete_prompt(prompt_name, argument_name, argument_value, context)
2278            .await
2279    }
2280
2281    /// Complete a resource template URI with full MCP protocol support
2282    ///
2283    /// This method provides completion for resource template URIs, allowing
2284    /// servers to suggest values for URI template variables.
2285    ///
2286    /// # Arguments
2287    ///
2288    /// * `resource_uri` - Resource template URI (e.g., "/files/{path}")
2289    /// * `argument_name` - Name of the argument being completed
2290    /// * `argument_value` - Current value for completion matching
2291    /// * `context` - Optional context with previously resolved arguments
2292    ///
2293    /// # Examples
2294    ///
2295    /// ```rust,no_run
2296    /// # use turbomcp_client::{Client, SharedClient};
2297    /// # use turbomcp_transport::stdio::StdioTransport;
2298    /// # async fn example() -> turbomcp_core::Result<()> {
2299    /// let shared = SharedClient::new(Client::new(StdioTransport::new()));
2300    /// shared.initialize().await?;
2301    ///
2302    /// let completions = shared.complete_resource(
2303    ///     "/files/{path}",
2304    ///     "path",
2305    ///     "/home/user/doc",
2306    ///     None
2307    /// ).await?;
2308    ///
2309    /// for completion in completions.values {
2310    ///     println!("Path suggestion: {}", completion);
2311    /// }
2312    /// # Ok(())
2313    /// # }
2314    /// ```
2315    pub async fn complete_resource(
2316        &self,
2317        resource_uri: &str,
2318        argument_name: &str,
2319        argument_value: &str,
2320        context: Option<turbomcp_protocol::types::CompletionContext>,
2321    ) -> Result<turbomcp_protocol::types::CompletionResponse> {
2322        self.inner
2323            .lock()
2324            .await
2325            .complete_resource(resource_uri, argument_name, argument_value, context)
2326            .await
2327    }
2328
2329    /// List filesystem roots available to the server
2330    ///
2331    /// Returns filesystem root directories that the server has access to.
2332    /// This helps servers understand their operating boundaries and available
2333    /// resources within the filesystem.
2334    ///
2335    /// # Examples
2336    ///
2337    /// ```rust,no_run
2338    /// # use turbomcp_client::{Client, SharedClient};
2339    /// # use turbomcp_transport::stdio::StdioTransport;
2340    /// # async fn example() -> turbomcp_core::Result<()> {
2341    /// let shared = SharedClient::new(Client::new(StdioTransport::new()));
2342    /// shared.initialize().await?;
2343    ///
2344    /// let roots = shared.list_roots().await?;
2345    /// for root_uri in roots {
2346    ///     println!("Available root: {}", root_uri);
2347    /// }
2348    /// # Ok(())
2349    /// # }
2350    /// ```
2351    pub async fn list_roots(&self) -> Result<Vec<String>> {
2352        self.inner.lock().await.list_roots().await
2353    }
2354
2355    /// Register an elicitation handler for processing server requests for user information
2356    ///
2357    /// Elicitation handlers respond to server requests for additional information
2358    /// from users during interactions. This enables interactive workflows where
2359    /// servers can gather necessary information dynamically.
2360    ///
2361    /// # Arguments
2362    ///
2363    /// * `handler` - The elicitation handler implementation
2364    ///
2365    /// # Examples
2366    ///
2367    /// ```rust,no_run
2368    /// use turbomcp_client::{Client, SharedClient};
2369    /// use turbomcp_client::handlers::{ElicitationHandler, ElicitationRequest, ElicitationResponse, ElicitationAction, HandlerResult};
2370    /// use turbomcp_transport::stdio::StdioTransport;
2371    /// use async_trait::async_trait;
2372    /// use std::sync::Arc;
2373    ///
2374    /// #[derive(Debug)]
2375    /// struct MyElicitationHandler;
2376    ///
2377    /// #[async_trait]
2378    /// impl ElicitationHandler for MyElicitationHandler {
2379    ///     async fn handle_elicitation(&self, request: ElicitationRequest) -> HandlerResult<ElicitationResponse> {
2380    ///         // Process user input request and return response
2381    ///         Ok(ElicitationResponse {
2382    ///             action: ElicitationAction::Accept,
2383    ///             content: Some(serde_json::json!({"name": "example"})),
2384    ///         })
2385    ///     }
2386    /// }
2387    ///
2388    /// # async fn example() -> turbomcp_core::Result<()> {
2389    /// let shared = SharedClient::new(Client::new(StdioTransport::new()));
2390    /// shared.on_elicitation(Arc::new(MyElicitationHandler)).await;
2391    /// # Ok(())
2392    /// # }
2393    /// ```
2394    pub async fn on_elicitation(&self, handler: Arc<dyn crate::handlers::ElicitationHandler>) {
2395        self.inner.lock().await.on_elicitation(handler);
2396    }
2397
2398    /// Register a progress handler for processing server progress notifications
2399    ///
2400    /// Progress handlers receive updates about long-running operations on the server.
2401    /// This enables progress bars, status updates, and better user experience during
2402    /// extended operations.
2403    ///
2404    /// # Arguments
2405    ///
2406    /// * `handler` - The progress handler implementation
2407    pub async fn on_progress(&self, handler: Arc<dyn crate::handlers::ProgressHandler>) {
2408        self.inner.lock().await.on_progress(handler);
2409    }
2410
2411    /// Register a log handler for processing server log messages
2412    ///
2413    /// Log handlers receive log messages from the server and can route them
2414    /// to the client's logging system. This is useful for debugging and
2415    /// maintaining a unified log across client and server.
2416    ///
2417    /// # Arguments
2418    ///
2419    /// * `handler` - The log handler implementation
2420    pub async fn on_log(&self, handler: Arc<dyn crate::handlers::LogHandler>) {
2421        self.inner.lock().await.on_log(handler);
2422    }
2423
2424    /// Register a resource update handler for processing resource change notifications
2425    ///
2426    /// Resource update handlers receive notifications when subscribed resources
2427    /// change on the server. This enables reactive updates to cached data or
2428    /// UI refreshes when server-side resources change.
2429    ///
2430    /// # Arguments
2431    ///
2432    /// * `handler` - The resource update handler implementation
2433    pub async fn on_resource_update(
2434        &self,
2435        handler: Arc<dyn crate::handlers::ResourceUpdateHandler>,
2436    ) {
2437        self.inner.lock().await.on_resource_update(handler);
2438    }
2439
2440    /// Check if an elicitation handler is registered
2441    pub async fn has_elicitation_handler(&self) -> bool {
2442        self.inner.lock().await.has_elicitation_handler()
2443    }
2444
2445    /// Check if a progress handler is registered
2446    pub async fn has_progress_handler(&self) -> bool {
2447        self.inner.lock().await.has_progress_handler()
2448    }
2449
2450    /// Check if a log handler is registered
2451    pub async fn has_log_handler(&self) -> bool {
2452        self.inner.lock().await.has_log_handler()
2453    }
2454
2455    /// Check if a resource update handler is registered
2456    pub async fn has_resource_update_handler(&self) -> bool {
2457        self.inner.lock().await.has_resource_update_handler()
2458    }
2459}
2460
2461impl<T: Transport> Clone for SharedClient<T> {
2462    /// Clone the shared client for use in multiple async tasks
2463    ///
2464    /// This creates a new reference to the same underlying client,
2465    /// allowing multiple tasks to share access safely.
2466    fn clone(&self) -> Self {
2467        Self {
2468            inner: Arc::clone(&self.inner),
2469        }
2470    }
2471}
2472
2473/// Result of client initialization
2474///
2475/// Contains information about the server and the negotiated capabilities
2476/// after a successful initialization handshake.
2477///
2478/// # Examples
2479///
2480/// ```rust,no_run
2481/// # use turbomcp_client::Client;
2482/// # use turbomcp_transport::stdio::StdioTransport;
2483/// # async fn example() -> turbomcp_core::Result<()> {
2484/// let mut client = Client::new(StdioTransport::new());
2485/// let result = client.initialize().await?;
2486///
2487/// println!("Server: {}", result.server_info.name);
2488/// println!("Version: {}", result.server_info.version);
2489/// if let Some(title) = result.server_info.title {
2490///     println!("Title: {}", title);
2491/// }
2492/// # Ok(())
2493/// # }
2494/// ```
2495#[derive(Debug)]
2496pub struct InitializeResult {
2497    /// Information about the server
2498    pub server_info: turbomcp_protocol::Implementation,
2499
2500    /// Capabilities supported by the server
2501    pub server_capabilities: ServerCapabilities,
2502}
2503
2504// ServerCapabilities is now imported from turbomcp_protocol::types
2505
2506/// Connection configuration for the client
2507#[derive(Debug, Clone)]
2508pub struct ConnectionConfig {
2509    /// Request timeout in milliseconds
2510    pub timeout_ms: u64,
2511
2512    /// Maximum number of retry attempts
2513    pub max_retries: u32,
2514
2515    /// Retry delay in milliseconds
2516    pub retry_delay_ms: u64,
2517
2518    /// Keep-alive interval in milliseconds
2519    pub keepalive_ms: u64,
2520}
2521
2522impl Default for ConnectionConfig {
2523    fn default() -> Self {
2524        Self {
2525            timeout_ms: 30_000,    // 30 seconds
2526            max_retries: 3,        // 3 attempts
2527            retry_delay_ms: 1_000, // 1 second
2528            keepalive_ms: 60_000,  // 60 seconds
2529        }
2530    }
2531}
2532
2533/// Builder for configuring and creating MCP clients
2534///
2535/// Provides a fluent interface for configuring client options before creation.
2536/// The enhanced builder pattern supports comprehensive configuration including:
2537/// - Protocol capabilities
2538/// - Plugin registration
2539/// - LLM provider configuration
2540/// - Handler registration
2541/// - Connection settings
2542/// - Session management
2543///
2544/// # Examples
2545///
2546/// Basic usage:
2547/// ```rust,no_run
2548/// use turbomcp_client::ClientBuilder;
2549/// use turbomcp_transport::stdio::StdioTransport;
2550///
2551/// # async fn example() -> turbomcp_core::Result<()> {
2552/// let client = ClientBuilder::new()
2553///     .with_tools(true)
2554///     .with_prompts(true)
2555///     .with_resources(false)
2556///     .build(StdioTransport::new());
2557/// # Ok(())
2558/// # }
2559/// ```
2560///
2561/// Advanced configuration:
2562/// ```rust,no_run
2563/// use turbomcp_client::{ClientBuilder, ConnectionConfig};
2564/// use turbomcp_client::plugins::{MetricsPlugin, PluginConfig};
2565/// use turbomcp_client::llm::{OpenAIProvider, LLMProviderConfig};
2566/// use turbomcp_transport::stdio::StdioTransport;
2567/// use std::sync::Arc;
2568///
2569/// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
2570/// let client = ClientBuilder::new()
2571///     .with_tools(true)
2572///     .with_prompts(true)
2573///     .with_resources(true)
2574///     .with_sampling(true)
2575///     .with_connection_config(ConnectionConfig {
2576///         timeout_ms: 60_000,
2577///         max_retries: 5,
2578///         retry_delay_ms: 2_000,
2579///         keepalive_ms: 30_000,
2580///     })
2581///     .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
2582///     .with_llm_provider("openai", Arc::new(OpenAIProvider::new(LLMProviderConfig {
2583///         api_key: std::env::var("OPENAI_API_KEY")?,
2584///         model: "gpt-4".to_string(),
2585///         ..Default::default()
2586///     })?))
2587///     .build(StdioTransport::new())
2588///     .await?;
2589/// # Ok(())
2590/// # }
2591/// ```
2592#[derive(Debug, Default)]
2593pub struct ClientBuilder {
2594    capabilities: ClientCapabilities,
2595    connection_config: ConnectionConfig,
2596    plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>,
2597    llm_providers: HashMap<String, Arc<dyn crate::llm::LLMProvider>>,
2598    elicitation_handler: Option<Arc<dyn crate::handlers::ElicitationHandler>>,
2599    progress_handler: Option<Arc<dyn crate::handlers::ProgressHandler>>,
2600    log_handler: Option<Arc<dyn crate::handlers::LogHandler>>,
2601    resource_update_handler: Option<Arc<dyn crate::handlers::ResourceUpdateHandler>>,
2602    session_config: Option<crate::llm::SessionConfig>,
2603}
2604
2605// Default implementation is now derived
2606
2607impl ClientBuilder {
2608    /// Create a new client builder
2609    ///
2610    /// Returns a new builder with default configuration.
2611    pub fn new() -> Self {
2612        Self::default()
2613    }
2614
2615    // ============================================================================
2616    // CAPABILITY CONFIGURATION
2617    // ============================================================================
2618
2619    /// Enable or disable tool support
2620    ///
2621    /// # Arguments
2622    ///
2623    /// * `enabled` - Whether to enable tool support
2624    pub fn with_tools(mut self, enabled: bool) -> Self {
2625        self.capabilities.tools = enabled;
2626        self
2627    }
2628
2629    /// Enable or disable prompt support
2630    ///
2631    /// # Arguments
2632    ///
2633    /// * `enabled` - Whether to enable prompt support
2634    pub fn with_prompts(mut self, enabled: bool) -> Self {
2635        self.capabilities.prompts = enabled;
2636        self
2637    }
2638
2639    /// Enable or disable resource support
2640    ///
2641    /// # Arguments
2642    ///
2643    /// * `enabled` - Whether to enable resource support
2644    pub fn with_resources(mut self, enabled: bool) -> Self {
2645        self.capabilities.resources = enabled;
2646        self
2647    }
2648
2649    /// Enable or disable sampling support
2650    ///
2651    /// # Arguments
2652    ///
2653    /// * `enabled` - Whether to enable sampling support
2654    pub fn with_sampling(mut self, enabled: bool) -> Self {
2655        self.capabilities.sampling = enabled;
2656        self
2657    }
2658
2659    /// Configure all capabilities at once
2660    ///
2661    /// # Arguments
2662    ///
2663    /// * `capabilities` - The capabilities configuration
2664    pub fn with_capabilities(mut self, capabilities: ClientCapabilities) -> Self {
2665        self.capabilities = capabilities;
2666        self
2667    }
2668
2669    // ============================================================================
2670    // CONNECTION CONFIGURATION
2671    // ============================================================================
2672
2673    /// Configure connection settings
2674    ///
2675    /// # Arguments
2676    ///
2677    /// * `config` - The connection configuration
2678    pub fn with_connection_config(mut self, config: ConnectionConfig) -> Self {
2679        self.connection_config = config;
2680        self
2681    }
2682
2683    /// Set request timeout
2684    ///
2685    /// # Arguments
2686    ///
2687    /// * `timeout_ms` - Timeout in milliseconds
2688    pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
2689        self.connection_config.timeout_ms = timeout_ms;
2690        self
2691    }
2692
2693    /// Set maximum retry attempts
2694    ///
2695    /// # Arguments
2696    ///
2697    /// * `max_retries` - Maximum number of retries
2698    pub fn with_max_retries(mut self, max_retries: u32) -> Self {
2699        self.connection_config.max_retries = max_retries;
2700        self
2701    }
2702
2703    /// Set retry delay
2704    ///
2705    /// # Arguments
2706    ///
2707    /// * `delay_ms` - Retry delay in milliseconds
2708    pub fn with_retry_delay(mut self, delay_ms: u64) -> Self {
2709        self.connection_config.retry_delay_ms = delay_ms;
2710        self
2711    }
2712
2713    /// Set keep-alive interval
2714    ///
2715    /// # Arguments
2716    ///
2717    /// * `interval_ms` - Keep-alive interval in milliseconds
2718    pub fn with_keepalive(mut self, interval_ms: u64) -> Self {
2719        self.connection_config.keepalive_ms = interval_ms;
2720        self
2721    }
2722
2723    // ============================================================================
2724    // PLUGIN SYSTEM CONFIGURATION
2725    // ============================================================================
2726
2727    /// Register a plugin with the client
2728    ///
2729    /// Plugins provide middleware functionality for request/response processing,
2730    /// metrics collection, retry logic, caching, and other cross-cutting concerns.
2731    ///
2732    /// # Arguments
2733    ///
2734    /// * `plugin` - The plugin implementation
2735    ///
2736    /// # Examples
2737    ///
2738    /// ```rust,no_run
2739    /// use turbomcp_client::{ClientBuilder, ConnectionConfig};
2740    /// use turbomcp_client::plugins::{MetricsPlugin, RetryPlugin, PluginConfig, RetryConfig};
2741    /// use std::sync::Arc;
2742    ///
2743    /// let client = ClientBuilder::new()
2744    ///     .with_plugin(Arc::new(MetricsPlugin::new(PluginConfig::Metrics)))
2745    ///     .with_plugin(Arc::new(RetryPlugin::new(PluginConfig::Retry(RetryConfig {
2746    ///         max_retries: 5,
2747    ///         base_delay_ms: 1000,
2748    ///         max_delay_ms: 30000,
2749    ///         backoff_multiplier: 2.0,
2750    ///         retry_on_timeout: true,
2751    ///         retry_on_connection_error: true,
2752    ///     }))));
2753    /// ```
2754    pub fn with_plugin(mut self, plugin: Arc<dyn crate::plugins::ClientPlugin>) -> Self {
2755        self.plugins.push(plugin);
2756        self
2757    }
2758
2759    /// Register multiple plugins at once
2760    ///
2761    /// # Arguments
2762    ///
2763    /// * `plugins` - Vector of plugin implementations
2764    pub fn with_plugins(mut self, plugins: Vec<Arc<dyn crate::plugins::ClientPlugin>>) -> Self {
2765        self.plugins.extend(plugins);
2766        self
2767    }
2768
2769    // ============================================================================
2770    // LLM PROVIDER CONFIGURATION
2771    // ============================================================================
2772
2773    /// Register an LLM provider
2774    ///
2775    /// LLM providers handle server-initiated sampling requests by forwarding them
2776    /// to language model services like OpenAI, Anthropic, or local models.
2777    ///
2778    /// # Arguments
2779    ///
2780    /// * `name` - Unique name for the provider
2781    /// * `provider` - The LLM provider implementation
2782    ///
2783    /// # Examples
2784    ///
2785    /// ```rust,no_run
2786    /// use turbomcp_client::ClientBuilder;
2787    /// use turbomcp_client::llm::{OpenAIProvider, AnthropicProvider, LLMProviderConfig};
2788    /// use std::sync::Arc;
2789    ///
2790    /// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
2791    /// let client = ClientBuilder::new()
2792    ///     .with_llm_provider("openai", Arc::new(OpenAIProvider::new(LLMProviderConfig {
2793    ///         api_key: std::env::var("OPENAI_API_KEY")?,
2794    ///         model: "gpt-4".to_string(),
2795    ///         ..Default::default()
2796    ///     })?))
2797    ///     .with_llm_provider("anthropic", Arc::new(AnthropicProvider::new(LLMProviderConfig {
2798    ///         api_key: std::env::var("ANTHROPIC_API_KEY")?,
2799    ///         model: "claude-3-5-sonnet-20241022".to_string(),
2800    ///         ..Default::default()
2801    ///     })?));
2802    /// # Ok(())
2803    /// # }
2804    /// ```
2805    pub fn with_llm_provider(
2806        mut self,
2807        name: impl Into<String>,
2808        provider: Arc<dyn crate::llm::LLMProvider>,
2809    ) -> Self {
2810        self.llm_providers.insert(name.into(), provider);
2811        self
2812    }
2813
2814    /// Register multiple LLM providers at once
2815    ///
2816    /// # Arguments
2817    ///
2818    /// * `providers` - Map of provider names to implementations
2819    pub fn with_llm_providers(
2820        mut self,
2821        providers: HashMap<String, Arc<dyn crate::llm::LLMProvider>>,
2822    ) -> Self {
2823        self.llm_providers.extend(providers);
2824        self
2825    }
2826
2827    /// Configure session management for conversations
2828    ///
2829    /// # Arguments
2830    ///
2831    /// * `config` - Session configuration for conversation tracking
2832    pub fn with_session_config(mut self, config: crate::llm::SessionConfig) -> Self {
2833        self.session_config = Some(config);
2834        self
2835    }
2836
2837    // ============================================================================
2838    // HANDLER REGISTRATION
2839    // ============================================================================
2840
2841    /// Register an elicitation handler for processing user input requests
2842    ///
2843    /// # Arguments
2844    ///
2845    /// * `handler` - The elicitation handler implementation
2846    pub fn with_elicitation_handler(
2847        mut self,
2848        handler: Arc<dyn crate::handlers::ElicitationHandler>,
2849    ) -> Self {
2850        self.elicitation_handler = Some(handler);
2851        self
2852    }
2853
2854    /// Register a progress handler for processing operation progress updates
2855    ///
2856    /// # Arguments
2857    ///
2858    /// * `handler` - The progress handler implementation
2859    pub fn with_progress_handler(
2860        mut self,
2861        handler: Arc<dyn crate::handlers::ProgressHandler>,
2862    ) -> Self {
2863        self.progress_handler = Some(handler);
2864        self
2865    }
2866
2867    /// Register a log handler for processing server log messages
2868    ///
2869    /// # Arguments
2870    ///
2871    /// * `handler` - The log handler implementation
2872    pub fn with_log_handler(mut self, handler: Arc<dyn crate::handlers::LogHandler>) -> Self {
2873        self.log_handler = Some(handler);
2874        self
2875    }
2876
2877    /// Register a resource update handler for processing resource change notifications
2878    ///
2879    /// # Arguments
2880    ///
2881    /// * `handler` - The resource update handler implementation
2882    pub fn with_resource_update_handler(
2883        mut self,
2884        handler: Arc<dyn crate::handlers::ResourceUpdateHandler>,
2885    ) -> Self {
2886        self.resource_update_handler = Some(handler);
2887        self
2888    }
2889
2890    // ============================================================================
2891    // BUILD METHODS
2892    // ============================================================================
2893
2894    /// Build a client with the configured options
2895    ///
2896    /// Creates a new client instance with all the configured options. The client
2897    /// will be initialized with the registered plugins, handlers, and providers.
2898    ///
2899    /// # Arguments
2900    ///
2901    /// * `transport` - The transport to use for the client
2902    ///
2903    /// # Returns
2904    ///
2905    /// Returns a configured `Client` instance wrapped in a Result for async setup.
2906    ///
2907    /// # Examples
2908    ///
2909    /// ```rust,no_run
2910    /// use turbomcp_client::ClientBuilder;
2911    /// use turbomcp_transport::stdio::StdioTransport;
2912    ///
2913    /// # async fn example() -> turbomcp_core::Result<()> {
2914    /// let client = ClientBuilder::new()
2915    ///     .with_tools(true)
2916    ///     .with_prompts(true)
2917    ///     .build(StdioTransport::new())
2918    ///     .await?;
2919    /// # Ok(())
2920    /// # }
2921    /// ```
2922    pub async fn build<T: Transport>(self, transport: T) -> Result<Client<T>> {
2923        // Create base client with capabilities
2924        let mut client = Client::with_capabilities(transport, self.capabilities);
2925
2926        // Register handlers
2927        if let Some(handler) = self.elicitation_handler {
2928            client.on_elicitation(handler);
2929        }
2930        if let Some(handler) = self.progress_handler {
2931            client.on_progress(handler);
2932        }
2933        if let Some(handler) = self.log_handler {
2934            client.on_log(handler);
2935        }
2936        if let Some(handler) = self.resource_update_handler {
2937            client.on_resource_update(handler);
2938        }
2939
2940        // Set up LLM providers if any are configured
2941        if !self.llm_providers.is_empty() {
2942            // Create LLM registry and register providers
2943            let mut registry = crate::llm::LLMRegistry::new();
2944            for (name, provider) in self.llm_providers {
2945                registry
2946                    .register_provider(&name, provider)
2947                    .await
2948                    .map_err(|e| {
2949                        Error::configuration(format!(
2950                            "Failed to register LLM provider '{}': {}",
2951                            name, e
2952                        ))
2953                    })?;
2954            }
2955
2956            // Configure session management if provided
2957            if let Some(session_config) = self.session_config {
2958                registry
2959                    .configure_sessions(session_config)
2960                    .await
2961                    .map_err(|e| {
2962                        Error::configuration(format!("Failed to configure sessions: {}", e))
2963                    })?;
2964            }
2965
2966            // Set up the registry as the sampling handler
2967            let sampling_handler = Arc::new(registry);
2968            client.set_sampling_handler(sampling_handler);
2969        }
2970
2971        // Apply connection configuration (store for future use in actual connections)
2972        // Note: The current Client doesn't expose connection config setters,
2973        // so we'll store this for when the transport supports it
2974
2975        // Register plugins with the client
2976        let has_plugins = !self.plugins.is_empty();
2977        for plugin in self.plugins {
2978            client.register_plugin(plugin).await.map_err(|e| {
2979                Error::bad_request(format!("Failed to register plugin during build: {}", e))
2980            })?;
2981        }
2982
2983        // Initialize plugins after registration
2984        if has_plugins {
2985            client.initialize_plugins().await.map_err(|e| {
2986                Error::bad_request(format!("Failed to initialize plugins during build: {}", e))
2987            })?;
2988        }
2989
2990        Ok(client)
2991    }
2992
2993    /// Build a client synchronously with basic configuration only
2994    ///
2995    /// This is a convenience method for simple use cases where no async setup
2996    /// is required. For advanced features like LLM providers, use `build()` instead.
2997    ///
2998    /// # Arguments
2999    ///
3000    /// * `transport` - The transport to use for the client
3001    ///
3002    /// # Returns
3003    ///
3004    /// Returns a configured `Client` instance.
3005    ///
3006    /// # Examples
3007    ///
3008    /// ```rust,no_run
3009    /// use turbomcp_client::ClientBuilder;
3010    /// use turbomcp_transport::stdio::StdioTransport;
3011    ///
3012    /// let client = ClientBuilder::new()
3013    ///     .with_tools(true)
3014    ///     .build_sync(StdioTransport::new());
3015    /// ```
3016    pub fn build_sync<T: Transport>(self, transport: T) -> Client<T> {
3017        let mut client = Client::with_capabilities(transport, self.capabilities);
3018
3019        // Register synchronous handlers only
3020        if let Some(handler) = self.elicitation_handler {
3021            client.on_elicitation(handler);
3022        }
3023        if let Some(handler) = self.progress_handler {
3024            client.on_progress(handler);
3025        }
3026        if let Some(handler) = self.log_handler {
3027            client.on_log(handler);
3028        }
3029        if let Some(handler) = self.resource_update_handler {
3030            client.on_resource_update(handler);
3031        }
3032
3033        client
3034    }
3035
3036    // ============================================================================
3037    // CONFIGURATION ACCESS
3038    // ============================================================================
3039
3040    /// Get the current capabilities configuration
3041    pub fn capabilities(&self) -> &ClientCapabilities {
3042        &self.capabilities
3043    }
3044
3045    /// Get the current connection configuration
3046    pub fn connection_config(&self) -> &ConnectionConfig {
3047        &self.connection_config
3048    }
3049
3050    /// Get the number of registered plugins
3051    pub fn plugin_count(&self) -> usize {
3052        self.plugins.len()
3053    }
3054
3055    /// Get the number of registered LLM providers
3056    pub fn llm_provider_count(&self) -> usize {
3057        self.llm_providers.len()
3058    }
3059
3060    /// Check if any handlers are registered
3061    pub fn has_handlers(&self) -> bool {
3062        self.elicitation_handler.is_some()
3063            || self.progress_handler.is_some()
3064            || self.log_handler.is_some()
3065            || self.resource_update_handler.is_some()
3066    }
3067}
3068
3069// Re-export types for public API
3070pub use turbomcp_protocol::types::ServerCapabilities as PublicServerCapabilities;