1use alloc::format;
36use alloc::string::{String, ToString};
37use alloc::vec;
38use alloc::vec::Vec;
39use core::fmt::Display;
40
41use serde::Serialize;
42
43use crate::types::content::Content;
44use crate::types::tools::CallToolResult;
45
46pub trait IntoToolResponse {
76 fn into_tool_response(self) -> CallToolResult;
78}
79
80impl IntoToolResponse for CallToolResult {
85 #[inline]
86 fn into_tool_response(self) -> CallToolResult {
87 self
88 }
89}
90
91impl IntoToolResponse for String {
92 #[inline]
93 fn into_tool_response(self) -> CallToolResult {
94 CallToolResult::text(self)
95 }
96}
97
98impl IntoToolResponse for &str {
99 #[inline]
100 fn into_tool_response(self) -> CallToolResult {
101 CallToolResult::text(self)
102 }
103}
104
105impl IntoToolResponse for () {
106 #[inline]
107 fn into_tool_response(self) -> CallToolResult {
108 CallToolResult {
109 content: vec![],
110 is_error: None,
111 _meta: None,
112 }
113 }
114}
115
116macro_rules! impl_into_tool_response_for_numeric {
118 ($($t:ty),*) => {
119 $(
120 impl IntoToolResponse for $t {
121 #[inline]
122 fn into_tool_response(self) -> CallToolResult {
123 CallToolResult::text(self.to_string())
124 }
125 }
126 )*
127 };
128}
129
130impl_into_tool_response_for_numeric!(
131 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64
132);
133
134impl IntoToolResponse for bool {
135 #[inline]
136 fn into_tool_response(self) -> CallToolResult {
137 CallToolResult::text(self.to_string())
138 }
139}
140
141impl IntoToolResponse for Content {
142 #[inline]
143 fn into_tool_response(self) -> CallToolResult {
144 CallToolResult {
145 content: vec![self],
146 is_error: None,
147 _meta: None,
148 }
149 }
150}
151
152impl IntoToolResponse for Vec<Content> {
153 #[inline]
154 fn into_tool_response(self) -> CallToolResult {
155 CallToolResult {
156 content: self,
157 is_error: None,
158 _meta: None,
159 }
160 }
161}
162
163impl<T, E> IntoToolResponse for Result<T, E>
168where
169 T: IntoToolResponse,
170 E: Into<ToolError>,
171{
172 fn into_tool_response(self) -> CallToolResult {
173 match self {
174 Ok(v) => v.into_tool_response(),
175 Err(e) => {
176 let error: ToolError = e.into();
177 error.into_tool_response()
178 }
179 }
180 }
181}
182
183#[derive(Debug, Clone)]
210pub struct Json<T>(pub T);
211
212impl<T: Serialize> IntoToolResponse for Json<T> {
213 fn into_tool_response(self) -> CallToolResult {
214 match serde_json::to_string_pretty(&self.0) {
215 Ok(json) => {
216 if json.len() > crate::MAX_MESSAGE_SIZE {
218 return ToolError::new(format!(
219 "JSON output too large: {} bytes exceeds {} byte limit",
220 json.len(),
221 crate::MAX_MESSAGE_SIZE
222 ))
223 .into_tool_response();
224 }
225 CallToolResult::text(json)
226 }
227 Err(e) => {
228 ToolError::new(format!("JSON serialization failed: {e}")).into_tool_response()
229 }
230 }
231 }
232}
233
234impl<T: Serialize> turbomcp_types::IntoToolResult for Json<T> {
235 fn into_tool_result(self) -> turbomcp_types::ToolResult {
236 match serde_json::to_string_pretty(&self.0) {
237 Ok(json) => {
238 if json.len() > crate::MAX_MESSAGE_SIZE {
240 return turbomcp_types::ToolResult::error(format!(
241 "JSON output too large: {} bytes exceeds {} byte limit",
242 json.len(),
243 crate::MAX_MESSAGE_SIZE
244 ));
245 }
246 turbomcp_types::ToolResult::text(json)
247 }
248 Err(e) => turbomcp_types::ToolResult::error(format!("JSON serialization failed: {e}")),
249 }
250 }
251}
252
253#[derive(Debug, Clone)]
265pub struct Text<T>(pub T);
266
267impl<T: Into<String>> IntoToolResponse for Text<T> {
268 #[inline]
269 fn into_tool_response(self) -> CallToolResult {
270 CallToolResult::text(self.0)
271 }
272}
273
274#[derive(Debug, Clone)]
287pub struct Image<D, M> {
288 pub data: D,
290 pub mime_type: M,
292}
293
294impl<D: Into<String>, M: Into<String>> IntoToolResponse for Image<D, M> {
295 #[inline]
296 fn into_tool_response(self) -> CallToolResult {
297 CallToolResult {
298 content: vec![Content::image(self.data, self.mime_type)],
299 is_error: None,
300 _meta: None,
301 }
302 }
303}
304
305#[derive(Debug, Clone)]
334pub struct ToolError {
335 message: String,
336 code: Option<i32>,
337}
338
339impl ToolError {
340 pub fn new(message: impl Into<String>) -> Self {
342 Self {
343 message: message.into(),
344 code: None,
345 }
346 }
347
348 pub fn with_code(code: i32, message: impl Into<String>) -> Self {
350 Self {
351 message: message.into(),
352 code: Some(code),
353 }
354 }
355
356 pub fn message(&self) -> &str {
358 &self.message
359 }
360
361 pub fn code(&self) -> Option<i32> {
363 self.code
364 }
365}
366
367impl IntoToolResponse for ToolError {
368 #[inline]
369 fn into_tool_response(self) -> CallToolResult {
370 CallToolResult::error(self.message)
371 }
372}
373
374impl Display for ToolError {
375 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
376 write!(f, "{}", self.message)
377 }
378}
379
380#[cfg(feature = "std")]
382impl std::error::Error for ToolError {}
383
384impl From<&str> for ToolError {
389 fn from(s: &str) -> Self {
390 Self {
391 message: s.into(),
392 code: None,
393 }
394 }
395}
396
397impl From<String> for ToolError {
398 fn from(s: String) -> Self {
399 Self {
400 message: s,
401 code: None,
402 }
403 }
404}
405
406impl From<serde_json::Error> for ToolError {
407 fn from(e: serde_json::Error) -> Self {
408 Self {
409 message: e.to_string(),
410 code: None,
411 }
412 }
413}
414
415impl From<crate::error::McpError> for ToolError {
417 fn from(e: crate::error::McpError) -> Self {
418 Self {
419 message: e.to_string(),
420 code: Some(e.jsonrpc_code()),
421 }
422 }
423}
424
425#[cfg(feature = "std")]
427impl From<std::io::Error> for ToolError {
428 fn from(e: std::io::Error) -> Self {
429 Self {
430 message: e.to_string(),
431 code: None,
432 }
433 }
434}
435
436#[cfg(feature = "std")]
437impl From<std::string::FromUtf8Error> for ToolError {
438 fn from(e: std::string::FromUtf8Error) -> Self {
439 Self {
440 message: e.to_string(),
441 code: None,
442 }
443 }
444}
445
446#[cfg(feature = "std")]
447impl From<std::num::ParseIntError> for ToolError {
448 fn from(e: std::num::ParseIntError) -> Self {
449 Self {
450 message: e.to_string(),
451 code: None,
452 }
453 }
454}
455
456#[cfg(feature = "std")]
457impl From<std::num::ParseFloatError> for ToolError {
458 fn from(e: std::num::ParseFloatError) -> Self {
459 Self {
460 message: e.to_string(),
461 code: None,
462 }
463 }
464}
465
466#[cfg(feature = "std")]
467impl From<Box<dyn std::error::Error>> for ToolError {
468 fn from(e: Box<dyn std::error::Error>) -> Self {
469 Self {
470 message: e.to_string(),
471 code: None,
472 }
473 }
474}
475
476#[cfg(feature = "std")]
477impl From<Box<dyn std::error::Error + Send + Sync>> for ToolError {
478 fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
479 Self {
480 message: e.to_string(),
481 code: None,
482 }
483 }
484}
485
486pub trait IntoToolError {
502 fn tool_err(self, context: impl Display) -> ToolError;
504}
505
506impl<E: Display> IntoToolError for E {
507 fn tool_err(self, context: impl Display) -> ToolError {
508 ToolError::new(format!("{}: {}", context, self))
509 }
510}
511
512impl<A, B> IntoToolResponse for (A, B)
517where
518 A: IntoToolResponse,
519 B: IntoToolResponse,
520{
521 fn into_tool_response(self) -> CallToolResult {
522 let a = self.0.into_tool_response();
523 let b = self.1.into_tool_response();
524
525 let mut content = a.content;
526 content.extend(b.content);
527
528 CallToolResult {
529 content,
530 is_error: a.is_error.or(b.is_error),
531 _meta: None,
532 }
533 }
534}
535
536impl<T: IntoToolResponse> IntoToolResponse for Option<T> {
541 fn into_tool_response(self) -> CallToolResult {
542 match self {
543 Some(v) => v.into_tool_response(),
544 None => CallToolResult::text("No result"),
545 }
546 }
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552
553 #[test]
554 fn test_string_into_response() {
555 let response = "hello".into_tool_response();
556 assert_eq!(response.content.len(), 1);
557 assert!(response.is_error.is_none());
558 }
559
560 #[test]
561 fn test_owned_string_into_response() {
562 let response = String::from("hello").into_tool_response();
563 assert_eq!(response.content.len(), 1);
564 }
565
566 #[test]
567 fn test_json_into_response() {
568 let data = serde_json::json!({"key": "value"});
569 let response = Json(data).into_tool_response();
570 assert_eq!(response.content.len(), 1);
571 }
572
573 #[test]
574 fn test_tool_error_into_response() {
575 let error = ToolError::new("something went wrong");
576 let response = error.into_tool_response();
577 assert_eq!(response.is_error, Some(true));
578 }
579
580 #[test]
581 fn test_result_ok_into_response() {
582 let result: Result<String, ToolError> = Ok("success".into());
583 let response = result.into_tool_response();
584 assert!(response.is_error.is_none());
585 }
586
587 #[test]
588 fn test_result_err_into_response() {
589 let result: Result<String, ToolError> = Err(ToolError::new("failed"));
590 let response = result.into_tool_response();
591 assert_eq!(response.is_error, Some(true));
592 }
593
594 #[test]
595 fn test_unit_into_response() {
596 let response = ().into_tool_response();
597 assert!(response.content.is_empty());
598 }
599
600 #[test]
601 fn test_option_some_into_response() {
602 let response = Some("value").into_tool_response();
603 assert_eq!(response.content.len(), 1);
604 }
605
606 #[test]
607 fn test_option_none_into_response() {
608 let response: CallToolResult = None::<String>.into_tool_response();
609 assert_eq!(response.content.len(), 1);
610 }
611
612 #[test]
613 fn test_tuple_into_response() {
614 let response = ("first", "second").into_tool_response();
615 assert_eq!(response.content.len(), 2);
616 }
617
618 #[test]
619 fn test_text_wrapper() {
620 let response = Text("explicit text").into_tool_response();
621 assert_eq!(response.content.len(), 1);
622 }
623
624 #[test]
625 fn test_image_wrapper() {
626 let response = Image {
627 data: "base64data",
628 mime_type: "image/png",
629 }
630 .into_tool_response();
631 assert_eq!(response.content.len(), 1);
632 }
633
634 #[test]
635 fn test_numeric_types() {
636 assert_eq!(42i32.into_tool_response().content.len(), 1);
637 assert_eq!(42i64.into_tool_response().content.len(), 1);
638 assert_eq!(2.5f64.into_tool_response().content.len(), 1);
639 }
640
641 #[test]
642 fn test_bool_into_response() {
643 let true_response = true.into_tool_response();
644 let false_response = false.into_tool_response();
645 assert_eq!(true_response.content.len(), 1);
646 assert_eq!(false_response.content.len(), 1);
647 }
648
649 #[test]
650 fn test_json_size_limit_enforcement() {
651 let large_string = "x".repeat(crate::MAX_MESSAGE_SIZE + 100);
653 let large_data = serde_json::json!({ "data": large_string });
654 let response = Json(large_data).into_tool_response();
655
656 assert_eq!(response.is_error, Some(true));
658 assert_eq!(response.content.len(), 1);
659
660 if let Content::Text { text, .. } = &response.content[0] {
662 assert!(text.contains("too large"));
663 assert!(text.contains("byte limit"));
664 } else {
665 panic!("Expected text content in error response");
666 }
667 }
668
669 #[test]
670 fn test_json_within_size_limit() {
671 let small_data = serde_json::json!({ "key": "value" });
673 let response = Json(small_data).into_tool_response();
674
675 assert!(response.is_error.is_none() || response.is_error == Some(false));
677 assert_eq!(response.content.len(), 1);
678 }
679}