1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use super::{content::ContentBlock, core::Cursor};
10
11#[derive(Debug, Clone, Serialize, Deserialize, Default)]
16pub struct ToolAnnotations {
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub title: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub audience: Option<Vec<String>>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub priority: Option<f64>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 #[serde(rename = "destructiveHint")]
29 pub destructive_hint: Option<bool>,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 #[serde(rename = "idempotentHint")]
33 pub idempotent_hint: Option<bool>,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 #[serde(rename = "openWorldHint")]
37 pub open_world_hint: Option<bool>,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 #[serde(rename = "readOnlyHint")]
41 pub read_only_hint: Option<bool>,
42 #[serde(flatten)]
44 pub custom: HashMap<String, serde_json::Value>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct Tool {
53 pub name: String,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub title: Option<String>,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub description: Option<String>,
63
64 #[serde(rename = "inputSchema")]
66 pub input_schema: ToolInputSchema,
67
68 #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
70 pub output_schema: Option<ToolOutputSchema>,
71
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub annotations: Option<ToolAnnotations>,
75
76 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
78 pub meta: Option<HashMap<String, serde_json::Value>>,
79}
80
81impl Default for Tool {
82 fn default() -> Self {
83 Self {
84 name: "unnamed_tool".to_string(), title: None,
86 description: None,
87 input_schema: ToolInputSchema::default(),
88 output_schema: None,
89 annotations: None,
90 meta: None,
91 }
92 }
93}
94
95impl Tool {
96 pub fn new(name: impl Into<String>) -> Self {
101 let name = name.into();
102 assert!(!name.trim().is_empty(), "Tool name cannot be empty");
103 Self {
104 name,
105 title: None,
106 description: None,
107 input_schema: ToolInputSchema::default(),
108 output_schema: None,
109 annotations: None,
110 meta: None,
111 }
112 }
113
114 pub fn with_description(name: impl Into<String>, description: impl Into<String>) -> Self {
119 let name = name.into();
120 assert!(!name.trim().is_empty(), "Tool name cannot be empty");
121 Self {
122 name,
123 title: None,
124 description: Some(description.into()),
125 input_schema: ToolInputSchema::default(),
126 output_schema: None,
127 annotations: None,
128 meta: None,
129 }
130 }
131
132 pub fn with_input_schema(mut self, schema: ToolInputSchema) -> Self {
141 self.input_schema = schema;
142 self
143 }
144
145 pub fn with_output_schema(mut self, schema: ToolOutputSchema) -> Self {
147 self.output_schema = Some(schema);
148 self
149 }
150
151 pub fn with_title(mut self, title: impl Into<String>) -> Self {
153 self.title = Some(title.into());
154 self
155 }
156
157 pub fn with_annotations(mut self, annotations: ToolAnnotations) -> Self {
159 self.annotations = Some(annotations);
160 self
161 }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct ToolInputSchema {
167 #[serde(rename = "type")]
169 pub schema_type: String,
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub properties: Option<HashMap<String, serde_json::Value>>,
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub required: Option<Vec<String>>,
176 #[serde(
178 rename = "additionalProperties",
179 skip_serializing_if = "Option::is_none"
180 )]
181 pub additional_properties: Option<bool>,
182}
183
184impl Default for ToolInputSchema {
185 fn default() -> Self {
187 Self {
188 schema_type: "object".to_string(),
189 properties: None,
190 required: None,
191 additional_properties: None,
192 }
193 }
194}
195
196impl ToolInputSchema {
197 pub fn empty() -> Self {
199 Self::default()
200 }
201
202 pub fn with_properties(properties: HashMap<String, serde_json::Value>) -> Self {
204 Self {
205 schema_type: "object".to_string(),
206 properties: Some(properties),
207 required: None,
208 additional_properties: None,
209 }
210 }
211
212 pub fn with_required_properties(
214 properties: HashMap<String, serde_json::Value>,
215 required: Vec<String>,
216 ) -> Self {
217 Self {
218 schema_type: "object".to_string(),
219 properties: Some(properties),
220 required: Some(required),
221 additional_properties: Some(false),
222 }
223 }
224
225 pub fn add_property(mut self, name: String, property: serde_json::Value) -> Self {
235 self.properties
236 .get_or_insert_with(HashMap::new)
237 .insert(name, property);
238 self
239 }
240
241 pub fn require_property(mut self, name: String) -> Self {
252 let required = self.required.get_or_insert_with(Vec::new);
253 if !required.contains(&name) {
254 required.push(name);
255 }
256 self
257 }
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct ToolOutputSchema {
263 #[serde(rename = "type")]
265 pub schema_type: String,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub properties: Option<HashMap<String, serde_json::Value>>,
269 #[serde(skip_serializing_if = "Option::is_none")]
271 pub required: Option<Vec<String>>,
272 #[serde(
274 rename = "additionalProperties",
275 skip_serializing_if = "Option::is_none"
276 )]
277 pub additional_properties: Option<bool>,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize, Default)]
282pub struct ListToolsRequest {
283 #[serde(skip_serializing_if = "Option::is_none")]
286 pub cursor: Option<Cursor>,
287 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
289 pub _meta: Option<serde_json::Value>,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct ListToolsResult {
295 pub tools: Vec<Tool>,
297 #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
300 pub next_cursor: Option<Cursor>,
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub _meta: Option<serde_json::Value>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct CallToolRequest {
309 pub name: String,
311 #[serde(skip_serializing_if = "Option::is_none")]
313 pub arguments: Option<HashMap<String, serde_json::Value>>,
314 #[serde(skip_serializing_if = "Option::is_none")]
316 pub _meta: Option<serde_json::Value>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct CallToolResult {
322 pub content: Vec<ContentBlock>,
324 #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
326 pub is_error: Option<bool>,
327 #[serde(rename = "structuredContent", skip_serializing_if = "Option::is_none")]
329 pub structured_content: Option<serde_json::Value>,
330 #[serde(skip_serializing_if = "Option::is_none")]
332 pub _meta: Option<serde_json::Value>,
333}