1use crate::meta::Cursor;
6use crate::schema::JsonSchema;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use std::collections::HashMap;
10
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct ToolAnnotations {
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub title: Option<String>,
22 #[serde(rename = "readOnlyHint", skip_serializing_if = "Option::is_none")]
24 pub read_only_hint: Option<bool>,
25 #[serde(rename = "destructiveHint", skip_serializing_if = "Option::is_none")]
29 pub destructive_hint: Option<bool>,
30 #[serde(rename = "idempotentHint", skip_serializing_if = "Option::is_none")]
34 pub idempotent_hint: Option<bool>,
35 #[serde(rename = "openWorldHint", skip_serializing_if = "Option::is_none")]
40 pub open_world_hint: Option<bool>,
41}
42
43impl Default for ToolAnnotations {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl ToolAnnotations {
50 pub fn new() -> Self {
51 Self {
52 title: None,
53 read_only_hint: None,
54 destructive_hint: None,
55 idempotent_hint: None,
56 open_world_hint: None,
57 }
58 }
59
60 pub fn with_title(mut self, title: impl Into<String>) -> Self {
61 self.title = Some(title.into());
62 self
63 }
64
65 pub fn with_read_only_hint(mut self, read_only: bool) -> Self {
66 self.read_only_hint = Some(read_only);
67 self
68 }
69
70 pub fn with_destructive_hint(mut self, destructive: bool) -> Self {
71 self.destructive_hint = Some(destructive);
72 self
73 }
74
75 pub fn with_idempotent_hint(mut self, idempotent: bool) -> Self {
76 self.idempotent_hint = Some(idempotent);
77 self
78 }
79
80 pub fn with_open_world_hint(mut self, open_world: bool) -> Self {
81 self.open_world_hint = Some(open_world);
82 self
83 }
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ToolSchema {
92 #[serde(rename = "type")]
94 pub schema_type: String,
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub properties: Option<HashMap<String, JsonSchema>>,
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub required: Option<Vec<String>>,
101 #[serde(flatten)]
103 pub additional: HashMap<String, Value>,
104}
105
106impl ToolSchema {
107 pub fn object() -> Self {
108 Self {
109 schema_type: "object".to_string(),
110 properties: None,
111 required: None,
112 additional: HashMap::new(),
113 }
114 }
115
116 pub fn with_properties(mut self, properties: HashMap<String, JsonSchema>) -> Self {
117 self.properties = Some(properties);
118 self
119 }
120
121 pub fn with_required(mut self, required: Vec<String>) -> Self {
122 self.required = Some(required);
123 self
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct Tool {
131 pub name: String,
133 #[serde(skip_serializing_if = "Option::is_none")]
136 pub title: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub description: Option<String>,
140 pub input_schema: ToolSchema,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub output_schema: Option<ToolSchema>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub annotations: Option<ToolAnnotations>,
148
149 #[serde(
150 default,
151 skip_serializing_if = "Option::is_none",
152 alias = "_meta",
153 rename = "_meta"
154 )]
155 pub meta: Option<HashMap<String, Value>>,
156}
157
158impl Tool {
159 pub fn new(name: impl Into<String>, input_schema: ToolSchema) -> Self {
160 Self {
161 name: name.into(),
162 title: None,
163 description: None,
164 input_schema,
165 output_schema: None,
166 annotations: None,
167 meta: None,
168 }
169 }
170
171 pub fn with_title(mut self, title: impl Into<String>) -> Self {
172 self.title = Some(title.into());
173 self
174 }
175
176 pub fn with_description(mut self, description: impl Into<String>) -> Self {
177 self.description = Some(description.into());
178 self
179 }
180
181 pub fn with_output_schema(mut self, output_schema: ToolSchema) -> Self {
182 self.output_schema = Some(output_schema);
183 self
184 }
185
186 pub fn with_annotations(mut self, annotations: ToolAnnotations) -> Self {
187 self.annotations = Some(annotations);
188 self
189 }
190
191 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
192 self.meta = Some(meta);
193 self
194 }
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct ListToolsParams {
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub cursor: Option<Cursor>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub limit: Option<u32>,
207 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
209 pub meta: Option<HashMap<String, Value>>,
210}
211
212impl ListToolsParams {
213 pub fn new() -> Self {
214 Self {
215 cursor: None,
216 limit: None,
217 meta: None,
218 }
219 }
220
221 pub fn with_cursor(mut self, cursor: Cursor) -> Self {
222 self.cursor = Some(cursor);
223 self
224 }
225
226 pub fn with_limit(mut self, limit: u32) -> Self {
227 self.limit = Some(limit);
228 self
229 }
230
231 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
232 self.meta = Some(meta);
233 self
234 }
235}
236
237impl Default for ListToolsParams {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(rename_all = "camelCase")]
246pub struct ListToolsRequest {
247 pub method: String,
249 pub params: ListToolsParams,
251}
252
253impl Default for ListToolsRequest {
254 fn default() -> Self {
255 Self::new()
256 }
257}
258
259impl ListToolsRequest {
260 pub fn new() -> Self {
261 Self {
262 method: "tools/list".to_string(),
263 params: ListToolsParams::new(),
264 }
265 }
266
267 pub fn with_cursor(mut self, cursor: Cursor) -> Self {
268 self.params = self.params.with_cursor(cursor);
269 self
270 }
271
272 pub fn with_limit(mut self, limit: u32) -> Self {
273 self.params = self.params.with_limit(limit);
274 self
275 }
276
277 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
278 self.params = self.params.with_meta(meta);
279 self
280 }
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
285#[serde(rename_all = "camelCase")]
286pub struct ListToolsResult {
287 pub tools: Vec<Tool>,
289 #[serde(skip_serializing_if = "Option::is_none")]
291 pub next_cursor: Option<Cursor>,
292 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
294 pub meta: Option<HashMap<String, Value>>,
295}
296
297impl ListToolsResult {
298 pub fn new(tools: Vec<Tool>) -> Self {
299 Self {
300 tools,
301 next_cursor: None,
302 meta: None,
303 }
304 }
305
306 pub fn with_next_cursor(mut self, cursor: Cursor) -> Self {
307 self.next_cursor = Some(cursor);
308 self
309 }
310
311 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
312 self.meta = Some(meta);
313 self
314 }
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
319#[serde(rename_all = "camelCase")]
320pub struct CallToolParams {
321 pub name: String,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub arguments: Option<HashMap<String, Value>>,
326 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
328 pub meta: Option<HashMap<String, Value>>,
329}
330
331impl CallToolParams {
332 pub fn new(name: impl Into<String>) -> Self {
333 Self {
334 name: name.into(),
335 arguments: None,
336 meta: None,
337 }
338 }
339
340 pub fn get_arguments(&self) -> Option<&HashMap<String, Value>> {
343 self.arguments.as_ref()
344 }
345
346 pub fn get_arguments_as_value(&self) -> Option<Value> {
348 self.arguments
349 .as_ref()
350 .map(|map| Value::Object(map.clone().into_iter().collect()))
351 }
352
353 pub fn with_arguments(mut self, arguments: HashMap<String, Value>) -> Self {
354 self.arguments = Some(arguments);
355 self
356 }
357
358 pub fn with_arguments_value(mut self, arguments: Value) -> Self {
359 if let Value::Object(map) = arguments {
361 self.arguments = Some(map.into_iter().collect());
362 }
363 self
364 }
365
366 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
367 self.meta = Some(meta);
368 self
369 }
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
374#[serde(rename_all = "camelCase")]
375pub struct CallToolRequest {
376 pub method: String,
378 pub params: CallToolParams,
380}
381
382impl CallToolRequest {
383 pub fn new(name: impl Into<String>) -> Self {
384 Self {
385 method: "tools/call".to_string(),
386 params: CallToolParams::new(name),
387 }
388 }
389
390 pub fn with_arguments(mut self, arguments: HashMap<String, Value>) -> Self {
391 self.params = self.params.with_arguments(arguments);
392 self
393 }
394
395 pub fn with_arguments_value(mut self, arguments: Value) -> Self {
396 self.params = self.params.with_arguments_value(arguments);
397 self
398 }
399
400 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
401 self.params = self.params.with_meta(meta);
402 self
403 }
404}
405
406pub type ToolResult = crate::content::ContentBlock;
409
410#[derive(Debug, Clone, Serialize, Deserialize)]
412#[serde(rename_all = "camelCase")]
413pub struct CallToolResult {
414 pub content: Vec<ToolResult>,
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub is_error: Option<bool>,
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub structured_content: Option<Value>,
422 #[serde(
424 default,
425 skip_serializing_if = "Option::is_none",
426 alias = "_meta",
427 rename = "_meta"
428 )]
429 pub meta: Option<HashMap<String, Value>>,
430}
431
432impl CallToolResult {
433 pub fn new(content: Vec<ToolResult>) -> Self {
434 Self {
435 content,
436 is_error: None,
437 structured_content: None,
438 meta: None,
439 }
440 }
441
442 pub fn success(content: Vec<ToolResult>) -> Self {
443 Self {
444 content,
445 is_error: Some(false),
446 structured_content: None,
447 meta: None,
448 }
449 }
450
451 pub fn error(content: Vec<ToolResult>) -> Self {
452 Self {
453 content,
454 is_error: Some(true),
455 structured_content: None,
456 meta: None,
457 }
458 }
459
460 pub fn with_error_flag(mut self, is_error: bool) -> Self {
461 self.is_error = Some(is_error);
462 self
463 }
464
465 pub fn with_structured_content(mut self, structured_content: Value) -> Self {
466 self.structured_content = Some(structured_content);
467 self
468 }
469
470 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
471 self.meta = Some(meta);
472 self
473 }
474
475 pub fn from_result<T: serde::Serialize>(result: &T) -> Result<Self, crate::McpError> {
481 let text_content = serde_json::to_string(result)
482 .map_err(|e| crate::McpError::tool_execution(&format!("Serialization error: {}", e)))?;
483
484 Ok(Self::success(vec![ToolResult::text(text_content)]))
485 }
486
487 pub fn from_result_with_structured<T: serde::Serialize>(
489 result: &T,
490 ) -> Result<Self, crate::McpError> {
491 let text_content = serde_json::to_string(result)
492 .map_err(|e| crate::McpError::tool_execution(&format!("Serialization error: {}", e)))?;
493
494 let structured = serde_json::to_value(result).map_err(|e| {
495 crate::McpError::tool_execution(&format!("Structured content error: {}", e))
496 })?;
497
498 Ok(Self::success(vec![ToolResult::text(text_content)])
499 .with_structured_content(structured))
500 }
501
502 pub fn from_result_with_schema<T: serde::Serialize>(
504 result: &T,
505 schema: Option<&ToolSchema>,
506 ) -> Result<Self, crate::McpError> {
507 let text_content = serde_json::to_string(result)
508 .map_err(|e| crate::McpError::tool_execution(&format!("Serialization error: {}", e)))?;
509
510 let response = Self::success(vec![ToolResult::text(text_content)]);
511
512 if schema.is_some() {
514 let structured = serde_json::to_value(result).map_err(|e| {
515 crate::McpError::tool_execution(&format!("Structured content error: {}", e))
516 })?;
517 Ok(response.with_structured_content(structured))
518 } else {
519 Ok(response)
520 }
521 }
522
523 pub fn from_result_auto<T: serde::Serialize>(
525 result: &T,
526 schema: Option<&ToolSchema>,
527 ) -> Result<Self, crate::McpError> {
528 let text_content = serde_json::to_string(result)
529 .map_err(|e| crate::McpError::tool_execution(&format!("Serialization error: {}", e)))?;
530
531 let response = Self::success(vec![ToolResult::text(text_content)]);
532
533 let structured = serde_json::to_value(result).map_err(|e| {
535 crate::McpError::tool_execution(&format!("Structured content error: {}", e))
536 })?;
537
538 let should_add_structured = schema.is_some()
539 || match &structured {
540 Value::Number(_) | Value::Bool(_) => true,
542 Value::Array(_) | Value::Object(_) => true,
544 Value::String(_) => false,
546 Value::Null => false,
547 };
548
549 if should_add_structured {
550 Ok(response.with_structured_content(structured))
551 } else {
552 Ok(response)
553 }
554 }
555
556 pub fn from_json_with_schema(json_result: Value, schema: Option<&ToolSchema>) -> Self {
558 let text_content = json_result.to_string();
559 let response = Self::success(vec![ToolResult::text(text_content)]);
560
561 if schema.is_some() {
562 response.with_structured_content(json_result)
563 } else {
564 response
565 }
566 }
567}
568
569use crate::traits::*;
572
573impl HasData for CallToolResult {
574 fn data(&self) -> HashMap<String, Value> {
575 let mut data = HashMap::new();
576 data.insert(
577 "content".to_string(),
578 serde_json::to_value(&self.content).unwrap_or(Value::Null),
579 );
580 if let Some(is_error) = self.is_error {
581 data.insert("isError".to_string(), Value::Bool(is_error));
582 }
583 if let Some(ref structured_content) = self.structured_content {
584 data.insert("structuredContent".to_string(), structured_content.clone());
585 }
586 data
587 }
588}
589
590impl HasMeta for CallToolResult {
591 fn meta(&self) -> Option<HashMap<String, Value>> {
592 self.meta.clone()
593 }
594}
595
596impl RpcResult for CallToolResult {}
597
598impl crate::traits::CallToolResult for CallToolResult {
599 fn content(&self) -> &Vec<ToolResult> {
600 &self.content
601 }
602
603 fn is_error(&self) -> Option<bool> {
604 self.is_error
605 }
606
607 fn structured_content(&self) -> Option<&Value> {
608 self.structured_content.as_ref()
609 }
610}
611
612impl Params for ListToolsParams {}
614
615impl HasListToolsParams for ListToolsParams {
616 fn cursor(&self) -> Option<&Cursor> {
617 self.cursor.as_ref()
618 }
619}
620
621impl HasMetaParam for ListToolsParams {
622 fn meta(&self) -> Option<&HashMap<String, Value>> {
623 self.meta.as_ref()
624 }
625}
626
627impl HasMethod for ListToolsRequest {
629 fn method(&self) -> &str {
630 &self.method
631 }
632}
633
634impl HasParams for ListToolsRequest {
635 fn params(&self) -> Option<&dyn Params> {
636 Some(&self.params)
637 }
638}
639
640impl HasData for ListToolsResult {
642 fn data(&self) -> HashMap<String, Value> {
643 let mut data = HashMap::new();
644 data.insert(
645 "tools".to_string(),
646 serde_json::to_value(&self.tools).unwrap_or(Value::Null),
647 );
648 if let Some(ref next_cursor) = self.next_cursor {
649 data.insert(
650 "nextCursor".to_string(),
651 Value::String(next_cursor.as_str().to_string()),
652 );
653 }
654 data
655 }
656}
657
658impl HasMeta for ListToolsResult {
659 fn meta(&self) -> Option<HashMap<String, Value>> {
660 self.meta.clone()
661 }
662}
663
664impl RpcResult for ListToolsResult {}
665
666impl crate::traits::ListToolsResult for ListToolsResult {
667 fn tools(&self) -> &Vec<Tool> {
668 &self.tools
669 }
670
671 fn next_cursor(&self) -> Option<&Cursor> {
672 self.next_cursor.as_ref()
673 }
674}
675
676impl Params for CallToolParams {}
678
679impl HasCallToolParams for CallToolParams {
680 fn name(&self) -> &String {
681 &self.name
682 }
683
684 fn arguments(&self) -> Option<&Value> {
685 None
693 }
694
695 fn meta(&self) -> Option<&HashMap<String, Value>> {
696 self.meta.as_ref()
697 }
698}
699
700impl HasMethod for CallToolRequest {
702 fn method(&self) -> &str {
703 &self.method
704 }
705}
706
707impl HasParams for CallToolRequest {
708 fn params(&self) -> Option<&dyn Params> {
709 Some(&self.params)
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use super::*;
716 use crate::content::ResourceContents;
717 use serde_json::json;
718
719 #[test]
720 fn test_tool_creation() {
721 let schema = ToolSchema::object()
722 .with_properties(HashMap::from([("text".to_string(), JsonSchema::string())]))
723 .with_required(vec!["text".to_string()]);
724
725 let tool = Tool::new("test_tool", schema).with_description("A test tool");
726
727 assert_eq!(tool.name, "test_tool");
728 assert!(tool.description.is_some());
729 assert_eq!(tool.input_schema.schema_type, "object");
730 }
731
732 #[test]
733 fn test_tool_result_creation() {
734 let text_result = ToolResult::text("Hello, world!");
735 let image_result = ToolResult::image("base64data", "image/png");
736 let resource_result = ToolResult::resource(ResourceContents::text(
737 "file:///test/resource.json",
738 serde_json::to_string(&json!({"key": "value"})).unwrap(),
739 ));
740
741 assert!(matches!(text_result, ToolResult::Text { .. }));
742 assert!(matches!(image_result, ToolResult::Image { .. }));
743 assert!(matches!(resource_result, ToolResult::Resource { .. }));
744 }
745
746 #[test]
747 fn test_call_tool_response() {
748 let response =
749 CallToolResult::success(vec![ToolResult::text("Operation completed successfully")]);
750
751 assert_eq!(response.is_error, Some(false));
752 assert_eq!(response.content.len(), 1);
753 assert!(response.structured_content.is_none());
754 }
755
756 #[test]
757 fn test_call_tool_response_with_structured_content() {
758 let structured_data = serde_json::json!({
759 "result": "success",
760 "value": 42
761 });
762
763 let response =
764 CallToolResult::success(vec![ToolResult::text("Operation completed successfully")])
765 .with_structured_content(structured_data.clone());
766
767 assert_eq!(response.is_error, Some(false));
768 assert_eq!(response.content.len(), 1);
769 assert_eq!(response.structured_content, Some(structured_data));
770 }
771
772 #[test]
773 fn test_serialization() {
774 let tool = Tool::new("echo", ToolSchema::object()).with_description("Echo tool");
775
776 let json = serde_json::to_string(&tool).unwrap();
777 assert!(json.contains("echo"));
778 assert!(json.contains("Echo tool"));
779
780 let parsed: Tool = serde_json::from_str(&json).unwrap();
781 assert_eq!(parsed.name, "echo");
782 }
783}