turul_mcp_protocol_2025_06_18/
initialize.rs1use std::collections::HashMap;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use crate::version::McpVersion;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(rename_all = "camelCase")]
13pub struct Implementation {
14 pub name: String,
16 pub version: String,
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub title: Option<String>,
21}
22
23impl Implementation {
24 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
25 Self {
26 name: name.into(),
27 version: version.into(),
28 title: None,
29 }
30 }
31
32 pub fn with_title(mut self, title: impl Into<String>) -> Self {
33 self.title = Some(title.into());
34 self
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, Default)]
40#[serde(rename_all = "camelCase")]
41pub struct RootsCapabilities {
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub list_changed: Option<bool>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, Default)]
49#[serde(rename_all = "camelCase")]
50pub struct SamplingCapabilities {
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub enabled: Option<bool>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, Default)]
58#[serde(rename_all = "camelCase")]
59pub struct ElicitationCapabilities {
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub enabled: Option<bool>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, Default)]
67#[serde(rename_all = "camelCase")]
68pub struct ClientCapabilities {
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub roots: Option<RootsCapabilities>,
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub sampling: Option<SamplingCapabilities>,
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub elicitation: Option<ElicitationCapabilities>,
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub experimental: Option<HashMap<String, Value>>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, Default)]
85#[serde(rename_all = "camelCase")]
86pub struct PromptsCapabilities {
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub list_changed: Option<bool>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize, Default)]
94#[serde(rename_all = "camelCase")]
95pub struct ToolsCapabilities {
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub list_changed: Option<bool>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, Default)]
103#[serde(rename_all = "camelCase")]
104pub struct ResourcesCapabilities {
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub subscribe: Option<bool>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub list_changed: Option<bool>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, Default)]
115#[serde(rename_all = "camelCase")]
116pub struct LoggingCapabilities {
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub enabled: Option<bool>,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub levels: Option<Vec<String>>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, Default)]
127#[serde(rename_all = "camelCase")]
128pub struct CompletionsCapabilities {
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub enabled: Option<bool>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, Default)]
136#[serde(rename_all = "camelCase")]
137pub struct ServerCapabilities {
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub logging: Option<LoggingCapabilities>,
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub completions: Option<CompletionsCapabilities>,
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub prompts: Option<PromptsCapabilities>,
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub resources: Option<ResourcesCapabilities>,
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub tools: Option<ToolsCapabilities>,
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub elicitation: Option<ElicitationCapabilities>,
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub experimental: Option<HashMap<String, Value>>,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
163#[serde(rename_all = "camelCase")]
164pub struct InitializeRequest {
165 pub protocol_version: String,
167 pub capabilities: ClientCapabilities,
169 pub client_info: Implementation,
171}
172
173impl InitializeRequest {
174 pub fn new(
175 protocol_version: McpVersion,
176 capabilities: ClientCapabilities,
177 client_info: Implementation,
178 ) -> Self {
179 Self {
180 protocol_version: protocol_version.as_str().to_string(),
181 capabilities,
182 client_info,
183 }
184 }
185
186 pub fn protocol_version(&self) -> Result<McpVersion, crate::McpError> {
188 McpVersion::from_str(&self.protocol_version).ok_or_else(|| {
189 crate::McpError::VersionMismatch {
190 expected: McpVersion::CURRENT.as_str().to_string(),
191 actual: self.protocol_version.clone(),
192 }
193 })
194 }
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct InitializeResult {
201 pub protocol_version: String,
203 pub capabilities: ServerCapabilities,
205 pub server_info: Implementation,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub instructions: Option<String>,
210}
211
212impl InitializeResult {
213 pub fn new(
214 protocol_version: McpVersion,
215 capabilities: ServerCapabilities,
216 server_info: Implementation,
217 ) -> Self {
218 Self {
219 protocol_version: protocol_version.as_str().to_string(),
220 capabilities,
221 server_info,
222 instructions: None,
223 }
224 }
225
226 pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
227 self.instructions = Some(instructions.into());
228 self
229 }
230
231 pub fn protocol_version(&self) -> Result<McpVersion, crate::McpError> {
233 McpVersion::from_str(&self.protocol_version).ok_or_else(|| {
234 crate::McpError::VersionMismatch {
235 expected: McpVersion::CURRENT.as_str().to_string(),
236 actual: self.protocol_version.clone(),
237 }
238 })
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_implementation_creation() {
248 let impl_info = Implementation::new("test-client", "1.0.0")
249 .with_title("Test Client");
250
251 assert_eq!(impl_info.name, "test-client");
252 assert_eq!(impl_info.version, "1.0.0");
253 assert_eq!(impl_info.title, Some("Test Client".to_string()));
254 }
255
256 #[test]
257 fn test_initialize_request_serialization() {
258 let client_info = Implementation::new("test-client", "1.0.0");
259 let capabilities = ClientCapabilities::default();
260 let request = InitializeRequest::new(
261 McpVersion::V2025_06_18,
262 capabilities,
263 client_info,
264 );
265
266 let json = serde_json::to_string(&request).unwrap();
267 assert!(json.contains("2025-06-18"));
268 assert!(json.contains("test-client"));
269 }
270
271 #[test]
272 fn test_initialize_response_creation() {
273 let server_info = Implementation::new("test-server", "1.0.0");
274 let capabilities = ServerCapabilities::default();
275 let response = InitializeResult::new(
276 McpVersion::V2025_06_18,
277 capabilities,
278 server_info,
279 ).with_instructions("Welcome to the test server!");
280
281 assert_eq!(response.protocol_version, "2025-06-18");
282 assert!(response.instructions.is_some());
283 }
284}