1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::types::content::Content;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct Tool {
10 pub name: String,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub description: Option<String>,
13 pub input_schema: Value,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub annotations: Option<ToolAnnotations>,
17}
18
19impl Tool {
20 pub fn new(
21 name: impl Into<String>,
22 description: impl Into<String>,
23 input_schema: Value,
24 ) -> Self {
25 Self {
26 name: name.into(),
27 description: Some(description.into()),
28 input_schema,
29 annotations: None,
30 }
31 }
32
33 pub fn no_params(name: impl Into<String>, description: impl Into<String>) -> Self {
35 Self::new(
36 name,
37 description,
38 serde_json::json!({ "type": "object", "properties": {} }),
39 )
40 }
41}
42
43#[derive(Debug, Clone, Default, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct ToolAnnotations {
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub title: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub read_only_hint: Option<bool>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub destructive_hint: Option<bool>,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub idempotent_hint: Option<bool>,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub open_world_hint: Option<bool>,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct CallToolResult {
63 pub content: Vec<Content>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub is_error: Option<bool>,
66}
67
68impl CallToolResult {
69 pub fn text(text: impl Into<String>) -> Self {
70 Self {
71 content: vec![Content::text(text)],
72 is_error: None,
73 }
74 }
75
76 pub fn success(content: Vec<Content>) -> Self {
77 Self {
78 content,
79 is_error: None,
80 }
81 }
82
83 pub fn error(message: impl Into<String>) -> Self {
84 Self {
85 content: vec![Content::text(message)],
86 is_error: Some(true),
87 }
88 }
89
90 pub fn with_content(mut self, content: impl Into<Content>) -> Self {
91 self.content.push(content.into());
92 self
93 }
94
95 pub fn is_error(&self) -> bool {
96 self.is_error.unwrap_or(false)
97 }
98}
99
100impl From<String> for CallToolResult {
101 fn from(s: String) -> Self {
102 Self::text(s)
103 }
104}
105
106impl From<&str> for CallToolResult {
107 fn from(s: &str) -> Self {
108 Self::text(s)
109 }
110}