Skip to main content

prism_mcp_rs/client/
enhanced_builder.rs

1//! Enhanced client builder with fluent interface
2//! Consolidates all client creation into single builder pattern
3
4use crate::client::McpClient;
5use crate::core::enhanced_errors::{McpError, McpResult};
6use crate::protocol::types::{ClientCapabilities, ClientInfo};
7use crate::protocol::ProtocolMode;
8use crate::protocol::TASKS_EXTENSION_ID;
9use std::collections::HashMap;
10use std::time::Duration;
11
12/// Configuration for retry behavior
13#[derive(Debug, Clone)]
14pub struct RetryConfig {
15    pub max_attempts: Option<u32>,
16    pub initial_delay_ms: u64,
17    pub max_delay_ms: u64,
18    pub backoff_multiplier: f64,
19}
20
21impl Default for RetryConfig {
22    fn default() -> Self {
23        Self {
24            max_attempts: Some(3),
25            initial_delay_ms: 1000,
26            max_delay_ms: 30000,
27            backoff_multiplier: 2.0,
28        }
29    }
30}
31
32/// Configuration for connections
33#[derive(Debug, Clone)]
34pub struct ConnectionConfig {
35    pub timeout_ms: u64,
36    pub keep_alive: bool,
37    pub compression: bool,
38}
39
40impl Default for ConnectionConfig {
41    fn default() -> Self {
42        Self {
43            timeout_ms: 30000,
44            keep_alive: true,
45            compression: false,
46        }
47    }
48}
49
50/// Enhanced client builder with fluent interface
51#[derive(Debug)]
52pub struct McpClientBuilder {
53    name: Option<String>,
54    version: Option<String>,
55    capabilities: Option<ClientCapabilities>,
56    timeout: Option<Duration>,
57    max_retries: Option<u32>,
58    validate_requests: Option<bool>,
59    validate_responses: Option<bool>,
60    protocol_mode: Option<ProtocolMode>,
61    max_mrtr_rounds: Option<u8>,
62}
63
64impl McpClientBuilder {
65    /// Create a new client builder
66    pub fn new() -> Self {
67        Self {
68            name: None,
69            version: None,
70            capabilities: None,
71            timeout: None,
72            max_retries: None,
73            validate_requests: None,
74            validate_responses: None,
75            protocol_mode: None,
76            max_mrtr_rounds: None,
77        }
78    }
79
80    /// Set client name (flexible input types)
81    pub fn name(mut self, name: impl Into<String>) -> Self {
82        self.name = Some(name.into());
83        self
84    }
85
86    /// Set client version (flexible input types)
87    pub fn version(mut self, version: impl Into<String>) -> Self {
88        self.version = Some(version.into());
89        self
90    }
91
92    /// Set client capabilities
93    pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
94        self.capabilities = Some(capabilities);
95        self
96    }
97
98    /// Declare support for the official MCP Tasks extension.
99    pub fn with_tasks_extension(mut self) -> Self {
100        let capabilities = self.capabilities.get_or_insert_with(Default::default);
101        capabilities
102            .extensions
103            .get_or_insert_with(HashMap::new)
104            .insert(TASKS_EXTENSION_ID.to_string(), serde_json::json!({}));
105        self
106    }
107
108    /// Set request timeout with type safety
109    pub fn timeout(mut self, timeout: Duration) -> Self {
110        self.timeout = Some(timeout);
111        self
112    }
113
114    /// Set maximum retry attempts
115    pub fn max_retries(mut self, retries: u32) -> Self {
116        self.max_retries = Some(retries);
117        self
118    }
119
120    /// Enable or disable request validation
121    pub fn validate_requests(mut self, validate: bool) -> Self {
122        self.validate_requests = Some(validate);
123        self
124    }
125
126    /// Enable or disable response validation
127    pub fn validate_responses(mut self, validate: bool) -> Self {
128        self.validate_responses = Some(validate);
129        self
130    }
131
132    /// Select automatic, modern-only, or legacy-only protocol behavior.
133    pub fn protocol_mode(mut self, mode: ProtocolMode) -> Self {
134        self.protocol_mode = Some(mode);
135        self
136    }
137
138    /// Bound automatic MCP 2026 multi-round-trip retries.
139    pub fn max_mrtr_rounds(mut self, rounds: u8) -> Self {
140        self.max_mrtr_rounds = Some(rounds.max(1));
141        self
142    }
143
144    /// Build the client with validation
145    pub fn build(self) -> McpResult<McpClient> {
146        let name = self
147            .name
148            .ok_or_else(|| McpError::validation("Client name is required"))?;
149        let version = self.version.unwrap_or_else(|| "1.0.0".to_string());
150
151        let client_info = ClientInfo::new(name, version);
152        let capabilities = self.capabilities.unwrap_or_default();
153
154        let config = crate::client::mcp_client::ClientConfig {
155            request_timeout_ms: self.timeout.map(|d| d.as_millis() as u64).unwrap_or(30000),
156            max_retries: self.max_retries.unwrap_or(3),
157            retry_delay_ms: 1000,
158            validate_requests: self.validate_requests.unwrap_or(true),
159            validate_responses: self.validate_responses.unwrap_or(true),
160            protocol_mode: self.protocol_mode.unwrap_or_default(),
161            max_mrtr_rounds: self.max_mrtr_rounds.unwrap_or(10),
162        };
163
164        Ok(McpClient::from_parts(client_info, capabilities, config))
165    }
166}
167
168impl Default for McpClientBuilder {
169    fn default() -> Self {
170        Self::new()
171    }
172}
173
174/// Convenient constructors for common cases
175impl McpClientBuilder {
176    /// Quick builder for development/testing
177    pub fn development(name: impl Into<String>) -> Self {
178        Self::new()
179            .name(name)
180            .version("dev")
181            .timeout(Duration::from_secs(10))
182            .max_retries(1)
183    }
184
185    /// Convenience preset with validation, timeout, and retry values.
186    ///
187    /// Applications must still configure transport security, authentication,
188    /// policy, and workload-specific deadlines.
189    pub fn production(name: impl Into<String>, version: impl Into<String>) -> Self {
190        Self::new()
191            .name(name)
192            .version(version)
193            .timeout(Duration::from_secs(30))
194            .max_retries(3)
195            .validate_requests(true)
196            .validate_responses(true)
197    }
198}