1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use crate::protocol::{
10 Cursor,
11 Request,
12 Notification,
13 sampling::{TextContent, ImageContent, AudioContent},
14 prompts::EmbeddedResource,
15};
16use crate::protocol::messages::MessageResult;
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
20pub struct Tool {
21 pub name: String,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub description: Option<String>,
26 pub input_schema: ToolInputSchema,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub annotations: Option<ToolAnnotations>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
35pub struct ToolInputSchema {
36 pub r#type: String,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub properties: Option<HashMap<String, serde_json::Value>>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub required: Option<Vec<String>>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
55pub struct ToolAnnotations {
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub title: Option<String>,
59 #[serde(skip_serializing_if = "Option::is_none")]
63 pub read_only_hint: Option<bool>,
64 #[serde(skip_serializing_if = "Option::is_none")]
71 pub destructive_hint: Option<bool>,
72 #[serde(skip_serializing_if = "Option::is_none")]
79 pub idempotent_hint: Option<bool>,
80 #[serde(skip_serializing_if = "Option::is_none")]
87 pub open_world_hint: Option<bool>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
92pub struct ListToolsRequest {
93 pub method: String,
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub params: Option<ListToolsParams>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102pub struct ListToolsParams {
103 #[serde(skip_serializing_if = "Option::is_none")]
106 pub cursor: Option<Cursor>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
111pub struct ListToolsResult {
112 pub tools: Vec<Tool>,
114 #[serde(skip_serializing_if = "Option::is_none")]
117 pub next_cursor: Option<Cursor>,
118 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
120 pub meta: Option<serde_json::Value>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
125pub struct CallToolRequest {
126 pub method: String,
128 pub params: CallToolParams,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
134pub struct CallToolParams {
135 pub name: String,
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub arguments: Option<serde_json::Value>,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
153pub struct CallToolResult {
154 pub content: Vec<ToolCallContent>,
156 #[serde(skip_serializing_if = "Option::is_none")]
160 pub is_error: Option<bool>,
161 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
163 pub meta: Option<serde_json::Value>,
164}
165
166impl MessageResult for CallToolResult {}
167
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
170#[serde(untagged)]
171pub enum ToolCallContent {
172 Text(TextContent),
174 Image(ImageContent),
176 Audio(AudioContent),
178 Resource(EmbeddedResource),
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
185pub struct ToolListChangedNotification {
186 pub method: String,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub params: Option<serde_json::Value>,
191}
192
193impl Request for ListToolsRequest {
195 const METHOD: &'static str = "tools/list";
196
197 fn method(&self) -> &str {
198 &self.method
199 }
200
201 fn params(&self) -> Option<&serde_json::Value> {
202 None
203 }
204}
205
206impl Request for CallToolRequest {
207 const METHOD: &'static str = "tools/call";
208
209 fn method(&self) -> &str {
210 &self.method
211 }
212
213 fn params(&self) -> Option<&serde_json::Value> {
214 None
215 }
216}
217
218impl Notification for ToolListChangedNotification {
220 const METHOD: &'static str = "notifications/tools/list_changed";
221
222 fn method(&self) -> &str {
223 &self.method
224 }
225
226 fn params(&self) -> Option<&serde_json::Value> {
227 None
228 }
229}
230
231impl MessageResult for ListToolsResult {}
233
234impl ListToolsRequest {
236 pub fn new() -> Self {
238 Self {
239 method: Self::METHOD.to_string(),
240 params: None,
241 }
242 }
243
244 pub fn with_cursor(cursor: impl Into<String>) -> Self {
246 Self {
247 method: Self::METHOD.to_string(),
248 params: Some(ListToolsParams {
249 cursor: Some(cursor.into()),
250 }),
251 }
252 }
253}
254
255impl CallToolRequest {
256 pub fn new(name: impl Into<String>) -> Self {
258 Self {
259 method: Self::METHOD.to_string(),
260 params: CallToolParams {
261 name: name.into(),
262 arguments: None,
263 },
264 }
265 }
266
267 pub fn with_arguments(
269 name: impl Into<String>,
270 arguments: serde_json::Value,
271 ) -> Self {
272 Self {
273 method: Self::METHOD.to_string(),
274 params: CallToolParams {
275 name: name.into(),
276 arguments: Some(arguments),
277 },
278 }
279 }
280}
281
282impl ToolListChangedNotification {
283 pub fn new() -> Self {
285 Self {
286 method: Self::METHOD.to_string(),
287 params: None,
288 }
289 }
290}
291
292impl Tool {
293 pub fn new(
295 name: impl Into<String>,
296 description: impl Into<String>,
297 ) -> Self {
298 Self {
299 name: name.into(),
300 description: Some(description.into()),
301 input_schema: ToolInputSchema {
302 r#type: "object".to_string(),
303 properties: None,
304 required: None,
305 },
306 annotations: None,
307 }
308 }
309
310 pub fn with_schema(mut self, properties: HashMap<String, serde_json::Value>, required: Vec<String>) -> Self {
312 self.input_schema = ToolInputSchema {
313 r#type: "object".to_string(),
314 properties: Some(properties),
315 required: Some(required),
316 };
317 self
318 }
319
320 pub fn with_annotations(mut self, annotations: ToolAnnotations) -> Self {
322 self.annotations = Some(annotations);
323 self
324 }
325}
326
327impl ToolAnnotations {
328 pub fn new() -> Self {
330 Self {
331 title: None,
332 read_only_hint: None,
333 destructive_hint: None,
334 idempotent_hint: None,
335 open_world_hint: None,
336 }
337 }
338
339 pub fn with_title(mut self, title: impl Into<String>) -> Self {
341 self.title = Some(title.into());
342 self
343 }
344
345 pub fn read_only(mut self) -> Self {
347 self.read_only_hint = Some(true);
348 self
349 }
350
351 pub fn destructive(mut self, is_destructive: bool) -> Self {
353 self.destructive_hint = Some(is_destructive);
354 self
355 }
356
357 pub fn idempotent(mut self, is_idempotent: bool) -> Self {
359 self.idempotent_hint = Some(is_idempotent);
360 self
361 }
362
363 pub fn open_world(mut self, is_open_world: bool) -> Self {
365 self.open_world_hint = Some(is_open_world);
366 self
367 }
368}