1use crate::version::McpVersion;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
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 #[serde(skip_serializing_if = "Option::is_none")]
23 pub description: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub website_url: Option<String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub icons: Option<Vec<crate::icons::Icon>>,
30}
31
32impl Implementation {
33 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
34 Self {
35 name: name.into(),
36 version: version.into(),
37 title: None,
38 description: None,
39 website_url: None,
40 icons: None,
41 }
42 }
43
44 pub fn with_title(mut self, title: impl Into<String>) -> Self {
45 self.title = Some(title.into());
46 self
47 }
48
49 pub fn with_description(mut self, description: impl Into<String>) -> Self {
50 self.description = Some(description.into());
51 self
52 }
53
54 pub fn with_website_url(mut self, url: impl Into<String>) -> Self {
55 self.website_url = Some(url.into());
56 self
57 }
58
59 pub fn with_icons(mut self, icons: Vec<crate::icons::Icon>) -> Self {
60 self.icons = Some(icons);
61 self
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, Default)]
67#[serde(rename_all = "camelCase")]
68pub struct RootsCapabilities {
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub list_changed: Option<bool>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, Default)]
78#[serde(rename_all = "camelCase")]
79pub struct SamplingCapabilities {
80 #[serde(flatten)]
82 pub extra: HashMap<String, Value>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, Default)]
89#[serde(rename_all = "camelCase")]
90pub struct ElicitationCapabilities {
91 #[serde(flatten)]
93 pub extra: HashMap<String, Value>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, Default)]
98#[serde(rename_all = "camelCase")]
99pub struct ClientCapabilities {
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub roots: Option<RootsCapabilities>,
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub sampling: Option<SamplingCapabilities>,
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub elicitation: Option<ElicitationCapabilities>,
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub tasks: Option<TasksCapabilities>,
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub experimental: Option<HashMap<String, Value>>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, Default)]
119#[serde(rename_all = "camelCase")]
120pub struct PromptsCapabilities {
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub list_changed: Option<bool>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, Default)]
128#[serde(rename_all = "camelCase")]
129pub struct ToolsCapabilities {
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub list_changed: Option<bool>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize, Default)]
140#[serde(rename_all = "camelCase")]
141pub struct TasksCapabilities {
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub list: Option<TasksListCapabilities>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub cancel: Option<TasksCancelCapabilities>,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub requests: Option<TasksRequestCapabilities>,
151 #[serde(flatten)]
153 pub extra: HashMap<String, Value>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize, Default)]
158#[serde(rename_all = "camelCase")]
159pub struct TasksListCapabilities {
160 #[serde(flatten)]
162 pub extra: HashMap<String, Value>,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize, Default)]
167#[serde(rename_all = "camelCase")]
168pub struct TasksCancelCapabilities {
169 #[serde(flatten)]
171 pub extra: HashMap<String, Value>,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize, Default)]
176#[serde(rename_all = "camelCase")]
177pub struct TasksRequestCapabilities {
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub tools: Option<TasksToolCapabilities>,
181 #[serde(flatten)]
183 pub extra: HashMap<String, Value>,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize, Default)]
188#[serde(rename_all = "camelCase")]
189pub struct TasksToolCapabilities {
190 #[serde(skip_serializing_if = "Option::is_none")]
192 pub call: Option<TasksToolCallCapabilities>,
193 #[serde(flatten)]
195 pub extra: HashMap<String, Value>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize, Default)]
200#[serde(rename_all = "camelCase")]
201pub struct TasksToolCallCapabilities {
202 #[serde(flatten)]
204 pub extra: HashMap<String, Value>,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize, Default)]
209#[serde(rename_all = "camelCase")]
210pub struct ResourcesCapabilities {
211 #[serde(skip_serializing_if = "Option::is_none")]
213 pub subscribe: Option<bool>,
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub list_changed: Option<bool>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize, Default)]
221#[serde(rename_all = "camelCase")]
222pub struct LoggingCapabilities {
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub enabled: Option<bool>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub levels: Option<Vec<String>>,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize, Default)]
233#[serde(rename_all = "camelCase")]
234pub struct CompletionsCapabilities {
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub enabled: Option<bool>,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize, Default)]
242#[serde(rename_all = "camelCase")]
243pub struct ServerCapabilities {
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub logging: Option<LoggingCapabilities>,
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub completions: Option<CompletionsCapabilities>,
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub prompts: Option<PromptsCapabilities>,
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub resources: Option<ResourcesCapabilities>,
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub tools: Option<ToolsCapabilities>,
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub tasks: Option<TasksCapabilities>,
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub experimental: Option<HashMap<String, Value>>,
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270pub struct InitializeRequest {
271 pub protocol_version: String,
273 pub capabilities: ClientCapabilities,
275 pub client_info: Implementation,
277}
278
279impl InitializeRequest {
280 pub fn new(
281 protocol_version: McpVersion,
282 capabilities: ClientCapabilities,
283 client_info: Implementation,
284 ) -> Self {
285 Self {
286 protocol_version: protocol_version.as_str().to_string(),
287 capabilities,
288 client_info,
289 }
290 }
291
292 pub fn protocol_version(&self) -> Result<McpVersion, crate::McpError> {
294 self.protocol_version
295 .parse::<McpVersion>()
296 .map_err(|_| crate::McpError::VersionMismatch {
297 expected: McpVersion::CURRENT.as_str().to_string(),
298 actual: self.protocol_version.clone(),
299 })
300 }
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305#[serde(rename_all = "camelCase")]
306pub struct InitializeResult {
307 pub protocol_version: String,
309 pub capabilities: ServerCapabilities,
311 pub server_info: Implementation,
313 #[serde(skip_serializing_if = "Option::is_none")]
315 pub instructions: Option<String>,
316}
317
318impl InitializeResult {
319 pub fn new(
320 protocol_version: McpVersion,
321 capabilities: ServerCapabilities,
322 server_info: Implementation,
323 ) -> Self {
324 Self {
325 protocol_version: protocol_version.as_str().to_string(),
326 capabilities,
327 server_info,
328 instructions: None,
329 }
330 }
331
332 pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
333 self.instructions = Some(instructions.into());
334 self
335 }
336
337 pub fn protocol_version(&self) -> Result<McpVersion, crate::McpError> {
339 self.protocol_version
340 .parse::<McpVersion>()
341 .map_err(|_| crate::McpError::VersionMismatch {
342 expected: McpVersion::CURRENT.as_str().to_string(),
343 actual: self.protocol_version.clone(),
344 })
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[test]
353 fn test_implementation_creation() {
354 let impl_info = Implementation::new("test-client", "1.0.0").with_title("Test Client");
355
356 assert_eq!(impl_info.name, "test-client");
357 assert_eq!(impl_info.version, "1.0.0");
358 assert_eq!(impl_info.title, Some("Test Client".to_string()));
359 }
360
361 #[test]
362 fn test_initialize_request_serialization() {
363 let client_info = Implementation::new("test-client", "1.0.0");
364 let capabilities = ClientCapabilities::default();
365 let request = InitializeRequest::new(McpVersion::V2025_06_18, capabilities, client_info);
366
367 let json = serde_json::to_string(&request).unwrap();
368 assert!(json.contains("2025-06-18"));
369 assert!(json.contains("test-client"));
370 }
371
372 #[test]
373 fn test_initialize_response_creation() {
374 let server_info = Implementation::new("test-server", "1.0.0");
375 let capabilities = ServerCapabilities::default();
376 let response = InitializeResult::new(McpVersion::V2025_06_18, capabilities, server_info)
377 .with_instructions("Welcome to the test server!");
378
379 assert_eq!(response.protocol_version, "2025-06-18");
380 assert!(response.instructions.is_some());
381 }
382}