1use serde_json::Value;
4
5#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
7pub struct ToolMetadata {
8 pub id: String,
9 pub name: String,
10 pub description: String,
11 pub category: String,
12 pub parameters: Vec<ParameterMetadata>,
13 pub return_type: String,
14 pub source: ToolSource,
15 pub server_id: Option<String>,
16}
17
18#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
20pub struct ParameterMetadata {
21 pub name: String,
22 pub type_: String,
23 pub description: String,
24 pub required: bool,
25 pub default: Option<Value>,
26}
27
28#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
30pub enum ToolSource {
31 BuiltIn,
32 Custom,
33 Mcp(String),
34}
35
36impl ToolMetadata {
37 pub fn new(
39 id: String,
40 name: String,
41 description: String,
42 category: String,
43 return_type: String,
44 source: ToolSource,
45 ) -> Self {
46 Self {
47 id,
48 name,
49 description,
50 category,
51 parameters: Vec::new(),
52 return_type,
53 source,
54 server_id: None,
55 }
56 }
57
58 pub fn add_parameter(&mut self, parameter: ParameterMetadata) {
60 self.parameters.push(parameter);
61 }
62
63 pub fn set_server_id(&mut self, server_id: String) {
65 self.server_id = Some(server_id);
66 }
67
68 pub fn get_documentation(&self) -> String {
70 let mut doc = format!("# {}\n\n", self.name);
71 doc.push_str(&format!("**Description**: {}\n\n", self.description));
72 doc.push_str(&format!("**Category**: {}\n\n", self.category));
73
74 if !self.parameters.is_empty() {
75 doc.push_str("## Parameters\n\n");
76 for param in &self.parameters {
77 doc.push_str(&format!(
78 "- **{}** ({}{}): {}\n",
79 param.name,
80 param.type_,
81 if param.required { ", required" } else { "" },
82 param.description
83 ));
84 if let Some(default) = ¶m.default {
85 doc.push_str(&format!(" - Default: {}\n", default));
86 }
87 }
88 doc.push('\n');
89 }
90
91 doc.push_str(&format!("## Returns\n\n{}\n", self.return_type));
92
93 doc
94 }
95
96 pub fn validate(&self) -> Result<(), String> {
98 if self.id.is_empty() {
99 return Err("Tool ID cannot be empty".to_string());
100 }
101
102 if self.name.is_empty() {
103 return Err("Tool name cannot be empty".to_string());
104 }
105
106 if self.description.is_empty() {
107 return Err("Tool description cannot be empty".to_string());
108 }
109
110 if self.category.is_empty() {
111 return Err("Tool category cannot be empty".to_string());
112 }
113
114 if self.return_type.is_empty() {
115 return Err("Tool return type cannot be empty".to_string());
116 }
117
118 for param in &self.parameters {
120 if param.name.is_empty() {
121 return Err("Parameter name cannot be empty".to_string());
122 }
123
124 if param.type_.is_empty() {
125 return Err("Parameter type cannot be empty".to_string());
126 }
127
128 if param.description.is_empty() {
129 return Err("Parameter description cannot be empty".to_string());
130 }
131 }
132
133 Ok(())
134 }
135}
136
137impl ParameterMetadata {
138 pub fn new(
140 name: String,
141 type_: String,
142 description: String,
143 required: bool,
144 ) -> Self {
145 Self {
146 name,
147 type_,
148 description,
149 required,
150 default: None,
151 }
152 }
153
154 pub fn with_default(mut self, default: Value) -> Self {
156 self.default = Some(default);
157 self
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_create_tool_metadata() {
167 let tool = ToolMetadata::new(
168 "test-tool".to_string(),
169 "Test Tool".to_string(),
170 "A test tool".to_string(),
171 "test".to_string(),
172 "string".to_string(),
173 ToolSource::Custom,
174 );
175
176 assert_eq!(tool.id, "test-tool");
177 assert_eq!(tool.name, "Test Tool");
178 assert_eq!(tool.description, "A test tool");
179 assert_eq!(tool.category, "test");
180 assert_eq!(tool.return_type, "string");
181 assert!(tool.parameters.is_empty());
182 assert!(tool.server_id.is_none());
183 }
184
185 #[test]
186 fn test_add_parameter() {
187 let mut tool = ToolMetadata::new(
188 "test-tool".to_string(),
189 "Test Tool".to_string(),
190 "A test tool".to_string(),
191 "test".to_string(),
192 "string".to_string(),
193 ToolSource::Custom,
194 );
195
196 let param = ParameterMetadata::new(
197 "param1".to_string(),
198 "string".to_string(),
199 "First parameter".to_string(),
200 true,
201 );
202
203 tool.add_parameter(param);
204 assert_eq!(tool.parameters.len(), 1);
205 assert_eq!(tool.parameters[0].name, "param1");
206 }
207
208 #[test]
209 fn test_set_server_id() {
210 let mut tool = ToolMetadata::new(
211 "test-tool".to_string(),
212 "Test Tool".to_string(),
213 "A test tool".to_string(),
214 "test".to_string(),
215 "string".to_string(),
216 ToolSource::Mcp("server-1".to_string()),
217 );
218
219 tool.set_server_id("server-1".to_string());
220 assert_eq!(tool.server_id, Some("server-1".to_string()));
221 }
222
223 #[test]
224 fn test_get_documentation() {
225 let mut tool = ToolMetadata::new(
226 "test-tool".to_string(),
227 "Test Tool".to_string(),
228 "A test tool".to_string(),
229 "test".to_string(),
230 "string".to_string(),
231 ToolSource::Custom,
232 );
233
234 let param = ParameterMetadata::new(
235 "param1".to_string(),
236 "string".to_string(),
237 "First parameter".to_string(),
238 true,
239 );
240
241 tool.add_parameter(param);
242
243 let doc = tool.get_documentation();
244 assert!(doc.contains("Test Tool"));
245 assert!(doc.contains("A test tool"));
246 assert!(doc.contains("param1"));
247 assert!(doc.contains("First parameter"));
248 }
249
250 #[test]
251 fn test_validate_valid_tool() {
252 let tool = ToolMetadata::new(
253 "test-tool".to_string(),
254 "Test Tool".to_string(),
255 "A test tool".to_string(),
256 "test".to_string(),
257 "string".to_string(),
258 ToolSource::Custom,
259 );
260
261 assert!(tool.validate().is_ok());
262 }
263
264 #[test]
265 fn test_validate_empty_id() {
266 let tool = ToolMetadata::new(
267 "".to_string(),
268 "Test Tool".to_string(),
269 "A test tool".to_string(),
270 "test".to_string(),
271 "string".to_string(),
272 ToolSource::Custom,
273 );
274
275 assert!(tool.validate().is_err());
276 }
277
278 #[test]
279 fn test_parameter_with_default() {
280 let param = ParameterMetadata::new(
281 "param1".to_string(),
282 "string".to_string(),
283 "First parameter".to_string(),
284 false,
285 )
286 .with_default(Value::String("default_value".to_string()));
287
288 assert_eq!(param.default, Some(Value::String("default_value".to_string())));
289 }
290}