1use serde::{Deserialize, Serialize};
10use serde_json::Value;
11use std::collections::HashMap;
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct ListToolsRequest {
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub cursor: Option<String>,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct ListToolsResponse {
24 pub tools: Vec<Tool>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub next_cursor: Option<String>,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
34pub struct Tool {
35 pub name: String,
37
38 pub description: String,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub input_schema: Option<Value>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub extensions: Option<Value>,
48
49 #[serde(rename = "readOnly", skip_serializing_if = "Option::is_none")]
51 pub read_only: Option<bool>,
52
53 #[serde(rename = "returnType", skip_serializing_if = "Option::is_none")]
55 pub return_type: Option<Value>,
56}
57
58impl<'de> Deserialize<'de> for Tool {
60 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61 where
62 D: serde::Deserializer<'de>,
63 {
64 use serde::de::{self, MapAccess, Visitor};
65 use std::fmt;
66
67 #[derive(Deserialize)]
68 #[serde(field_identifier, rename_all = "camelCase")]
69 enum Field {
70 Name,
71 Description,
72 #[serde(alias = "input_schema")]
73 InputSchema,
74 #[serde(alias = "parameters_schema")]
75 ParametersSchema,
76 Extensions,
77 #[serde(alias = "read_only")]
78 ReadOnly,
79 ReturnType,
80 #[serde(other)]
81 Unknown,
82 }
83
84 struct ToolVisitor;
85
86 impl<'de> Visitor<'de> for ToolVisitor {
87 type Value = Tool;
88
89 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
90 formatter.write_str("struct Tool")
91 }
92
93 fn visit_map<V>(self, mut map: V) -> Result<Tool, V::Error>
94 where
95 V: MapAccess<'de>,
96 {
97 let mut name = None;
98 let mut description = None;
99 let mut input_schema = None;
100 let mut extensions = None;
101 let mut read_only = None;
102 let mut return_type = None;
103
104 while let Some(key) = map.next_key()? {
105 match key {
106 Field::Name => {
107 if name.is_some() {
108 return Err(de::Error::duplicate_field("name"));
109 }
110 name = Some(map.next_value()?);
111 }
112 Field::Description => {
113 if description.is_some() {
114 return Err(de::Error::duplicate_field("description"));
115 }
116 description = Some(map.next_value()?);
117 }
118 Field::InputSchema => {
119 if input_schema.is_none() {
120 input_schema = Some(map.next_value()?);
121 } else {
122 let _: Value = map.next_value()?;
124 }
125 }
126 Field::ParametersSchema => {
127 if input_schema.is_none() {
128 input_schema = Some(map.next_value()?);
129 } else {
130 let _: Value = map.next_value()?;
132 }
133 }
134 Field::Extensions => {
135 if extensions.is_some() {
136 return Err(de::Error::duplicate_field("extensions"));
137 }
138 extensions = Some(map.next_value()?);
139 }
140 Field::ReadOnly => {
141 if read_only.is_some() {
142 return Err(de::Error::duplicate_field("readOnly"));
143 }
144 read_only = Some(map.next_value()?);
145 }
146 Field::ReturnType => {
147 if return_type.is_some() {
148 return Err(de::Error::duplicate_field("returnType"));
149 }
150 return_type = Some(map.next_value()?);
151 }
152 Field::Unknown => {
153 let _: Value = map.next_value()?;
155 }
156 }
157 }
158
159 let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
160 let description =
161 description.ok_or_else(|| de::Error::missing_field("description"))?;
162
163 Ok(Tool {
164 name,
165 description,
166 input_schema,
167 extensions,
168 read_only,
169 return_type,
170 })
171 }
172 }
173
174 const FIELDS: &[&str] = &[
175 "name",
176 "description",
177 "inputSchema",
178 "parametersSchema",
179 "extensions",
180 "readOnly",
181 "returnType",
182 ];
183 deserializer.deserialize_struct("Tool", FIELDS, ToolVisitor)
184 }
185}
186
187impl Tool {
188 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
190 Self {
191 name: name.into(),
192 description: description.into(),
193 input_schema: None,
194 extensions: None,
195 read_only: None,
196 return_type: None,
197 }
198 }
199
200 pub fn with_input_schema(mut self, schema: Value) -> Self {
202 self.input_schema = Some(schema);
203 self
204 }
205
206 pub fn with_extensions(mut self, extensions: Value) -> Self {
208 self.extensions = Some(extensions);
209 self
210 }
211
212 pub fn with_read_only(mut self, read_only: bool) -> Self {
214 self.read_only = Some(read_only);
215 self
216 }
217
218 pub fn with_return_type(mut self, return_type: Value) -> Self {
220 self.return_type = Some(return_type);
221 self
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227pub struct CallToolRequest {
228 pub name: String,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub arguments: Option<Value>,
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238pub struct CallToolResponse {
239 #[serde(default)]
241 pub content: Vec<ToolResult>,
242
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub is_error: Option<bool>,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
250#[serde(tag = "type")]
251pub enum ToolResult {
252 #[serde(rename = "text")]
254 Text {
255 text: String,
257 },
258
259 #[serde(rename = "image")]
261 Image {
262 data: String,
264
265 #[serde(rename = "mimeType")]
267 mime_type: String,
268 },
269
270 #[serde(rename = "resource")]
272 Resource {
273 resource: ResourceReference,
275 },
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
280pub struct ResourceReference {
281 pub uri: String,
283
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub text: Option<String>,
287}
288
289#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
291pub struct ToolListChangedNotification {
292 #[serde(flatten)]
294 pub metadata: HashMap<String, Value>,
295}
296
297impl ToolListChangedNotification {
298 pub fn new() -> Self {
300 Self::default()
301 }
302
303 pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
305 self.metadata.insert(key.into(), value);
306 self
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use serde_json::json;
314
315 #[test]
316 fn test_tool_creation() {
317 let tool = Tool::new("calculator", "A simple calculator tool").with_input_schema(json!({
318 "type": "object",
319 "properties": {
320 "expression": {"type": "string"}
321 },
322 "required": ["expression"]
323 }));
324
325 assert_eq!(tool.name, "calculator");
326 assert_eq!(tool.description, "A simple calculator tool");
327 assert!(tool.input_schema.is_some());
328 assert_eq!(tool.extensions, None);
329 assert_eq!(tool.read_only, None);
330 assert_eq!(tool.return_type, None);
331 }
332
333 #[test]
334 fn test_list_tools_request() {
335 let request = ListToolsRequest { cursor: None };
336 let json = serde_json::to_string(&request).unwrap();
337 let deserialized: ListToolsRequest = serde_json::from_str(&json).unwrap();
338 assert_eq!(request, deserialized);
339 }
340
341 #[test]
342 fn test_call_tool_request() {
343 let request = CallToolRequest {
344 name: "calculator".to_string(),
345 arguments: Some(json!({"expression": "2 + 2"})),
346 };
347
348 let json = serde_json::to_string(&request).unwrap();
349 let deserialized: CallToolRequest = serde_json::from_str(&json).unwrap();
350 assert_eq!(request, deserialized);
351 }
352
353 #[test]
354 fn test_tool_result_text() {
355 let result = ToolResult::Text {
356 text: "The answer is 4".to_string(),
357 };
358
359 let json = serde_json::to_value(&result).unwrap();
360 assert_eq!(json["type"], "text");
361 assert_eq!(json["text"], "The answer is 4");
362 }
363
364 #[test]
365 fn test_tool_result_image() {
366 let result = ToolResult::Image {
367 data: "base64data".to_string(),
368 mime_type: "image/png".to_string(),
369 };
370
371 let json = serde_json::to_value(&result).unwrap();
372 assert_eq!(json["type"], "image");
373 assert_eq!(json["mimeType"], "image/png");
374 }
375
376 #[test]
377 fn test_tool_deserialization_with_camel_case() {
378 let json_str = r#"{
380 "name": "test-tool",
381 "description": "A test tool",
382 "inputSchema": {
383 "type": "object",
384 "properties": {
385 "param1": {"type": "string"}
386 }
387 },
388 "readOnly": true
389 }"#;
390
391 let tool: Tool = serde_json::from_str(json_str).unwrap();
392 assert_eq!(tool.name, "test-tool");
393 assert_eq!(tool.description, "A test tool");
394 assert!(tool.input_schema.is_some());
395 assert_eq!(tool.read_only, Some(true));
396 assert_eq!(tool.return_type, None);
397 }
398
399 #[test]
400 fn test_tool_deserialization_with_parameters_schema() {
401 let json_str = r#"{
403 "name": "test-tool",
404 "description": "A test tool",
405 "parametersSchema": {
406 "type": "object",
407 "properties": {
408 "param1": {"type": "string"}
409 }
410 }
411 }"#;
412
413 let tool: Tool = serde_json::from_str(json_str).unwrap();
414 assert_eq!(tool.name, "test-tool");
415 assert_eq!(tool.description, "A test tool");
416 assert!(tool.input_schema.is_some());
417 }
418}