1use crate::resources::ResourceMetadata;
81use async_trait::async_trait;
82use serde::{Deserialize, Serialize};
83use std::collections::HashMap;
84use std::error::Error;
85use std::fmt;
86use std::sync::Arc;
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct ToolDefinition {
91 pub name: String,
93 pub description: String,
95 pub parameters_schema: serde_json::Value,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ToolResult {
102 pub success: bool,
104 pub output: serde_json::Value,
106 pub error: Option<String>,
108 pub metadata: HashMap<String, serde_json::Value>,
110}
111
112impl ToolResult {
113 pub fn success(output: serde_json::Value) -> Self {
115 Self {
116 success: true,
117 output,
118 error: None,
119 metadata: HashMap::new(),
120 }
121 }
122
123 pub fn failure(error: String) -> Self {
125 Self {
126 success: false,
127 output: serde_json::Value::Null,
128 error: Some(error),
129 metadata: HashMap::new(),
130 }
131 }
132
133 pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
135 self.metadata.insert(key.into(), value);
136 self
137 }
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142#[serde(rename_all = "lowercase")]
143pub enum ToolParameterType {
144 String,
146 Number,
148 Integer,
150 Boolean,
152 Array,
154 Object,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct ToolParameter {
161 pub name: String,
163 #[serde(rename = "type")]
165 pub param_type: ToolParameterType,
166 pub description: Option<String>,
168 pub required: bool,
170 pub default: Option<serde_json::Value>,
172 pub items: Option<Box<ToolParameterType>>,
174 pub properties: Option<HashMap<String, ToolParameter>>,
176}
177
178impl ToolParameter {
179 pub fn new(name: impl Into<String>, param_type: ToolParameterType) -> Self {
181 Self {
182 name: name.into(),
183 param_type,
184 description: None,
185 required: false,
186 default: None,
187 items: None,
188 properties: None,
189 }
190 }
191
192 pub fn with_description(mut self, description: impl Into<String>) -> Self {
194 self.description = Some(description.into());
195 self
196 }
197
198 pub fn required(mut self) -> Self {
200 self.required = true;
201 self
202 }
203
204 pub fn with_default(mut self, default: serde_json::Value) -> Self {
206 self.default = Some(default);
207 self
208 }
209
210 pub fn with_items(mut self, item_type: ToolParameterType) -> Self {
212 self.items = Some(Box::new(item_type));
213 self
214 }
215
216 pub fn with_properties(mut self, properties: HashMap<String, ToolParameter>) -> Self {
218 self.properties = Some(properties);
219 self
220 }
221
222 pub fn to_json_schema(&self) -> serde_json::Value {
250 let mut schema = match &self.param_type {
251 ToolParameterType::String => serde_json::json!({"type": "string"}),
252 ToolParameterType::Number => serde_json::json!({"type": "number"}),
253 ToolParameterType::Integer => serde_json::json!({"type": "integer"}),
254 ToolParameterType::Boolean => serde_json::json!({"type": "boolean"}),
255 ToolParameterType::Array => {
256 let items_schema = self
257 .items
258 .as_ref()
259 .map(|t| param_type_to_schema(t))
260 .unwrap_or_else(|| serde_json::json!({}));
261 serde_json::json!({"type": "array", "items": items_schema})
262 }
263 ToolParameterType::Object => {
264 if let Some(props) = &self.properties {
265 let properties: serde_json::Map<String, serde_json::Value> = props
266 .iter()
267 .map(|(k, v)| (k.clone(), v.to_json_schema()))
268 .collect();
269 let required: Vec<&str> = props
270 .values()
271 .filter(|p| p.required)
272 .map(|p| p.name.as_str())
273 .collect();
274 serde_json::json!({
275 "type": "object",
276 "properties": properties,
277 "required": required
278 })
279 } else {
280 serde_json::json!({"type": "object"})
281 }
282 }
283 };
284
285 if let Some(desc) = &self.description {
287 schema["description"] = serde_json::Value::String(desc.clone());
288 }
289
290 schema
291 }
292}
293
294fn param_type_to_schema(t: &ToolParameterType) -> serde_json::Value {
298 match t {
299 ToolParameterType::String => serde_json::json!({"type": "string"}),
300 ToolParameterType::Number => serde_json::json!({"type": "number"}),
301 ToolParameterType::Integer => serde_json::json!({"type": "integer"}),
302 ToolParameterType::Boolean => serde_json::json!({"type": "boolean"}),
303 ToolParameterType::Array => serde_json::json!({"type": "array"}),
304 ToolParameterType::Object => serde_json::json!({"type": "object"}),
305 }
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct ToolMetadata {
311 pub name: String,
313 pub description: String,
315 pub parameters: Vec<ToolParameter>,
317 pub protocol_metadata: HashMap<String, serde_json::Value>,
319}
320
321impl ToolMetadata {
322 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
324 Self {
325 name: name.into(),
326 description: description.into(),
327 parameters: Vec::new(),
328 protocol_metadata: HashMap::new(),
329 }
330 }
331
332 pub fn with_parameter(mut self, param: ToolParameter) -> Self {
334 self.parameters.push(param);
335 self
336 }
337
338 pub fn with_protocol_metadata(
340 mut self,
341 key: impl Into<String>,
342 value: serde_json::Value,
343 ) -> Self {
344 self.protocol_metadata.insert(key.into(), value);
345 self
346 }
347
348 pub fn to_tool_definition(&self) -> ToolDefinition {
374 let mut properties = serde_json::Map::new();
375 let mut required: Vec<String> = Vec::new();
376
377 for param in &self.parameters {
378 properties.insert(param.name.clone(), param.to_json_schema());
379 if param.required {
380 required.push(param.name.clone());
381 }
382 }
383
384 let parameters_schema = serde_json::json!({
385 "type": "object",
386 "properties": properties,
387 "required": required
388 });
389
390 ToolDefinition {
391 name: self.name.clone(),
392 description: self.description.clone(),
393 parameters_schema,
394 }
395 }
396}
397
398#[async_trait]
400pub trait ToolProtocol: Send + Sync {
401 async fn execute(
403 &self,
404 tool_name: &str,
405 parameters: serde_json::Value,
406 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>>;
407
408 async fn list_tools(&self) -> Result<Vec<ToolMetadata>, Box<dyn Error + Send + Sync>>;
410
411 async fn get_tool_metadata(
413 &self,
414 tool_name: &str,
415 ) -> Result<ToolMetadata, Box<dyn Error + Send + Sync>>;
416
417 fn protocol_name(&self) -> &str;
419
420 async fn initialize(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
422 Ok(())
423 }
424
425 async fn shutdown(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
427 Ok(())
428 }
429
430 async fn list_resources(&self) -> Result<Vec<ResourceMetadata>, Box<dyn Error + Send + Sync>> {
435 Ok(Vec::new())
436 }
437
438 async fn read_resource(&self, uri: &str) -> Result<String, Box<dyn Error + Send + Sync>> {
442 Err(format!("Resource not found: {}", uri).into())
443 }
444
445 fn supports_resources(&self) -> bool {
447 false
448 }
449}
450
451#[derive(Debug, Clone)]
453pub enum ToolError {
454 NotFound(String),
456 ExecutionFailed(String),
458 InvalidParameters(String),
460 ProtocolError(String),
462}
463
464impl fmt::Display for ToolError {
465 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466 match self {
467 ToolError::NotFound(name) => write!(f, "Tool not found: {}", name),
468 ToolError::ExecutionFailed(msg) => write!(f, "Tool execution failed: {}", msg),
469 ToolError::InvalidParameters(msg) => write!(f, "Invalid parameters: {}", msg),
470 ToolError::ProtocolError(msg) => write!(f, "Protocol error: {}", msg),
471 }
472 }
473}
474
475impl Error for ToolError {}
476
477pub struct Tool {
479 metadata: ToolMetadata,
481 protocol: Arc<dyn ToolProtocol>,
483}
484
485impl Tool {
486 pub fn new(
488 name: impl Into<String>,
489 description: impl Into<String>,
490 protocol: Arc<dyn ToolProtocol>,
491 ) -> Self {
492 Self {
493 metadata: ToolMetadata::new(name, description),
494 protocol,
495 }
496 }
497
498 pub fn with_parameter(mut self, param: ToolParameter) -> Self {
500 self.metadata.parameters.push(param);
501 self
502 }
503
504 pub fn with_protocol_metadata(
506 mut self,
507 key: impl Into<String>,
508 value: serde_json::Value,
509 ) -> Self {
510 self.metadata.protocol_metadata.insert(key.into(), value);
511 self
512 }
513
514 pub fn metadata(&self) -> &ToolMetadata {
516 &self.metadata
517 }
518
519 pub async fn execute(
521 &self,
522 parameters: serde_json::Value,
523 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
524 self.protocol.execute(&self.metadata.name, parameters).await
525 }
526}
527
528pub struct ToolRegistry {
626 tools: HashMap<String, Tool>,
628 tool_to_protocol: HashMap<String, String>,
630 protocols: HashMap<String, Arc<dyn ToolProtocol>>,
632 primary_protocol: Option<Arc<dyn ToolProtocol>>,
634}
635
636impl ToolRegistry {
637 pub fn new(protocol: Arc<dyn ToolProtocol>) -> Self {
642 Self {
643 tools: HashMap::new(),
644 tool_to_protocol: HashMap::new(),
645 protocols: {
646 let mut m = HashMap::new();
647 m.insert("primary".to_string(), protocol.clone());
648 m
649 },
650 primary_protocol: Some(protocol),
651 }
652 }
653
654 pub fn empty() -> Self {
658 Self {
659 tools: HashMap::new(),
660 tool_to_protocol: HashMap::new(),
661 protocols: HashMap::new(),
662 primary_protocol: None,
663 }
664 }
665
666 pub async fn add_protocol(
719 &mut self,
720 protocol_name: &str,
721 protocol: Arc<dyn ToolProtocol>,
722 ) -> Result<(), Box<dyn Error + Send + Sync>> {
723 let discovered_tools = protocol.list_tools().await?;
725
726 self.protocols
728 .insert(protocol_name.to_string(), protocol.clone());
729
730 for tool_meta in discovered_tools {
732 let tool_name = tool_meta.name.clone();
733
734 let tool = Tool::new(
736 tool_name.clone(),
737 tool_meta.description.clone(),
738 protocol.clone(),
739 );
740
741 let mut tool = tool;
743 for param in &tool_meta.parameters {
744 tool = tool.with_parameter(param.clone());
745 }
746 for (key, value) in &tool_meta.protocol_metadata {
747 tool = tool.with_protocol_metadata(key.clone(), value.clone());
748 }
749
750 self.tools.insert(tool_name.clone(), tool);
752 self.tool_to_protocol
753 .insert(tool_name, protocol_name.to_string());
754 }
755
756 Ok(())
757 }
758
759 pub fn remove_protocol(&mut self, protocol_name: &str) {
761 self.protocols.remove(protocol_name);
762
763 let tools_to_remove: Vec<String> = self
765 .tool_to_protocol
766 .iter()
767 .filter(|(_, pn)| *pn == protocol_name)
768 .map(|(tn, _)| tn.clone())
769 .collect();
770
771 for tool_name in tools_to_remove {
773 self.tools.remove(&tool_name);
774 self.tool_to_protocol.remove(&tool_name);
775 }
776 }
777
778 pub fn add_tool(&mut self, tool: Tool) {
780 self.tools.insert(tool.metadata.name.clone(), tool);
781 }
782
783 pub fn remove_tool(&mut self, name: &str) -> Option<Tool> {
785 self.tool_to_protocol.remove(name);
786 self.tools.remove(name)
787 }
788
789 pub fn get_tool(&self, name: &str) -> Option<&Tool> {
791 self.tools.get(name)
792 }
793
794 pub fn list_tools(&self) -> Vec<&ToolMetadata> {
796 self.tools.values().map(|t| &t.metadata).collect()
797 }
798
799 pub async fn discover_tools_from_primary(
804 &mut self,
805 ) -> Result<(), Box<dyn Error + Send + Sync>> {
806 if let Some(protocol) = &self.primary_protocol {
807 let discovered_tools = protocol.list_tools().await?;
808 for tool_meta in discovered_tools {
809 let tool_name = tool_meta.name.clone();
810 let tool = Tool::new(
811 tool_name.clone(),
812 tool_meta.description.clone(),
813 protocol.clone(),
814 );
815
816 let mut tool = tool;
818 for param in &tool_meta.parameters {
819 tool = tool.with_parameter(param.clone());
820 }
821 for (key, value) in &tool_meta.protocol_metadata {
822 tool = tool.with_protocol_metadata(key.clone(), value.clone());
823 }
824
825 self.tools.insert(tool_name.clone(), tool);
826 self.tool_to_protocol
827 .insert(tool_name, "primary".to_string());
828 }
829 Ok(())
830 } else {
831 Err("No primary protocol available".into())
832 }
833 }
834
835 pub fn get_tool_protocol(&self, tool_name: &str) -> Option<&str> {
837 self.tool_to_protocol.get(tool_name).map(|s| s.as_str())
838 }
839
840 pub fn list_protocols(&self) -> Vec<&str> {
842 self.protocols.keys().map(|s| s.as_str()).collect()
843 }
844
845 pub async fn execute_tool(
847 &self,
848 tool_name: &str,
849 parameters: serde_json::Value,
850 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
851 let tool = self
852 .tools
853 .get(tool_name)
854 .ok_or_else(|| ToolError::NotFound(tool_name.to_string()))?;
855
856 tool.execute(parameters).await
857 }
858
859 pub fn protocol(&self) -> Option<&Arc<dyn ToolProtocol>> {
863 self.primary_protocol.as_ref()
864 }
865
866 pub fn to_tool_definitions(&self) -> Vec<ToolDefinition> {
912 self.tools
913 .values()
914 .map(|t| t.metadata.to_tool_definition())
915 .collect()
916 }
917}
918
919#[cfg(test)]
920mod tests {
921 use super::*;
922
923 struct MockProtocol;
924
925 #[async_trait]
926 impl ToolProtocol for MockProtocol {
927 async fn execute(
928 &self,
929 tool_name: &str,
930 _parameters: serde_json::Value,
931 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
932 Ok(ToolResult::success(serde_json::json!({
933 "tool": tool_name,
934 "result": "mock_result"
935 })))
936 }
937
938 async fn list_tools(&self) -> Result<Vec<ToolMetadata>, Box<dyn Error + Send + Sync>> {
939 Ok(vec![])
940 }
941
942 async fn get_tool_metadata(
943 &self,
944 _tool_name: &str,
945 ) -> Result<ToolMetadata, Box<dyn Error + Send + Sync>> {
946 Ok(ToolMetadata::new("mock_tool", "A mock tool"))
947 }
948
949 fn protocol_name(&self) -> &str {
950 "mock"
951 }
952 }
953
954 #[test]
955 fn test_tool_parameter_builder() {
956 let param = ToolParameter::new("test_param", ToolParameterType::String)
957 .with_description("A test parameter")
958 .required()
959 .with_default(serde_json::json!("default_value"));
960
961 assert_eq!(param.name, "test_param");
962 assert_eq!(param.param_type, ToolParameterType::String);
963 assert_eq!(param.description, Some("A test parameter".to_string()));
964 assert!(param.required);
965 assert_eq!(param.default, Some(serde_json::json!("default_value")));
966 }
967
968 #[tokio::test]
969 async fn test_tool_execution() {
970 let protocol = Arc::new(MockProtocol);
971 let tool = Tool::new("test_tool", "A test tool", protocol.clone());
972
973 let result = tool.execute(serde_json::json!({})).await.unwrap();
974 assert!(result.success);
975 assert_eq!(result.output["tool"], "test_tool");
976 }
977
978 #[tokio::test]
979 async fn test_tool_registry() {
980 let protocol = Arc::new(MockProtocol);
981 let mut registry = ToolRegistry::new(protocol.clone());
982
983 let tool = Tool::new("calculator", "Performs calculations", protocol.clone());
984 registry.add_tool(tool);
985
986 assert!(registry.get_tool("calculator").is_some());
987 assert_eq!(registry.list_tools().len(), 1);
988
989 let result = registry
990 .execute_tool("calculator", serde_json::json!({}))
991 .await
992 .unwrap();
993 assert!(result.success);
994 }
995
996 #[tokio::test]
997 async fn test_empty_registry_creation() {
998 let registry = ToolRegistry::empty();
999 assert_eq!(registry.list_tools().len(), 0);
1000 assert_eq!(registry.list_protocols().len(), 0);
1001 assert!(registry.protocol().is_none());
1002 }
1003
1004 #[tokio::test]
1005 async fn test_add_single_protocol_to_empty_registry() {
1006 let protocol = Arc::new(MockProtocol);
1007 let mut registry = ToolRegistry::empty();
1008
1009 registry
1011 .add_protocol("mock", protocol.clone())
1012 .await
1013 .unwrap();
1014
1015 assert_eq!(registry.list_protocols().len(), 1);
1017 assert!(registry.list_protocols().contains(&"mock"));
1018 }
1019
1020 #[tokio::test]
1021 async fn test_add_multiple_protocols() {
1022 let protocol1 = Arc::new(MockProtocol);
1023 let protocol2 = Arc::new(MockProtocol);
1024 let mut registry = ToolRegistry::empty();
1025
1026 registry
1028 .add_protocol("protocol1", protocol1.clone())
1029 .await
1030 .unwrap();
1031 registry
1032 .add_protocol("protocol2", protocol2.clone())
1033 .await
1034 .unwrap();
1035
1036 assert_eq!(registry.list_protocols().len(), 2);
1038 assert!(registry.list_protocols().contains(&"protocol1"));
1039 assert!(registry.list_protocols().contains(&"protocol2"));
1040 }
1041
1042 #[tokio::test]
1043 async fn test_remove_protocol() {
1044 let protocol = Arc::new(MockProtocol);
1045 let mut registry = ToolRegistry::empty();
1046
1047 registry
1049 .add_protocol("protocol1", protocol.clone())
1050 .await
1051 .unwrap();
1052 assert_eq!(registry.list_protocols().len(), 1);
1053
1054 registry.remove_protocol("protocol1");
1056 assert_eq!(registry.list_protocols().len(), 0);
1057 }
1058
1059 #[tokio::test]
1060 async fn test_get_tool_protocol() {
1061 let protocol = Arc::new(MockProtocol);
1062 let mut registry = ToolRegistry::empty();
1063
1064 registry
1066 .add_protocol("local", protocol.clone())
1067 .await
1068 .unwrap();
1069
1070 let tool = Tool::new("calculator", "Performs calculations", protocol.clone());
1072 registry.add_tool(tool);
1073 registry
1074 .tool_to_protocol
1075 .insert("calculator".to_string(), "local".to_string());
1076
1077 assert_eq!(registry.get_tool_protocol("calculator"), Some("local"));
1079 assert_eq!(registry.get_tool_protocol("nonexistent"), None);
1080 }
1081
1082 #[tokio::test]
1083 async fn test_remove_protocol_removes_tools() {
1084 let protocol = Arc::new(MockProtocol);
1085 let mut registry = ToolRegistry::empty();
1086
1087 registry
1089 .add_protocol("protocol1", protocol.clone())
1090 .await
1091 .unwrap();
1092
1093 let tool1 = Tool::new("tool1", "First tool", protocol.clone());
1094 registry.add_tool(tool1);
1095 registry
1096 .tool_to_protocol
1097 .insert("tool1".to_string(), "protocol1".to_string());
1098
1099 let tool2 = Tool::new("tool2", "Second tool", protocol.clone());
1100 registry.add_tool(tool2);
1101 registry
1102 .tool_to_protocol
1103 .insert("tool2".to_string(), "protocol1".to_string());
1104
1105 assert_eq!(registry.list_tools().len(), 2);
1106
1107 registry.remove_protocol("protocol1");
1109
1110 assert_eq!(registry.list_tools().len(), 0);
1112 assert_eq!(registry.get_tool_protocol("tool1"), None);
1113 assert_eq!(registry.get_tool_protocol("tool2"), None);
1114 }
1115
1116 #[tokio::test]
1117 async fn test_execute_tool_through_registry() {
1118 let protocol = Arc::new(MockProtocol);
1119 let mut registry = ToolRegistry::empty();
1120
1121 registry
1123 .add_protocol("mock", protocol.clone())
1124 .await
1125 .unwrap();
1126
1127 let tool = Tool::new("test_tool", "A test tool", protocol.clone());
1129 registry.add_tool(tool);
1130
1131 let result = registry
1132 .execute_tool("test_tool", serde_json::json!({}))
1133 .await
1134 .unwrap();
1135
1136 assert!(result.success);
1137 assert_eq!(result.output["tool"], "test_tool");
1138 }
1139
1140 #[tokio::test]
1141 async fn test_backwards_compatibility_single_protocol() {
1142 let protocol = Arc::new(MockProtocol);
1143 let registry = ToolRegistry::new(protocol.clone());
1144
1145 assert!(registry.protocol().is_some());
1147 assert_eq!(registry.list_protocols().len(), 1);
1148 assert!(registry.list_protocols().contains(&"primary"));
1149 }
1150
1151 #[tokio::test]
1152 async fn test_discover_tools_from_primary() {
1153 struct TestProtocol {
1154 tools: Vec<ToolMetadata>,
1155 }
1156
1157 #[async_trait]
1158 impl ToolProtocol for TestProtocol {
1159 async fn execute(
1160 &self,
1161 tool_name: &str,
1162 _parameters: serde_json::Value,
1163 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
1164 Ok(ToolResult::success(serde_json::json!({
1165 "tool": tool_name,
1166 })))
1167 }
1168
1169 async fn list_tools(&self) -> Result<Vec<ToolMetadata>, Box<dyn Error + Send + Sync>> {
1170 Ok(self.tools.clone())
1171 }
1172
1173 async fn get_tool_metadata(
1174 &self,
1175 tool_name: &str,
1176 ) -> Result<ToolMetadata, Box<dyn Error + Send + Sync>> {
1177 self.tools
1178 .iter()
1179 .find(|t| t.name == tool_name)
1180 .cloned()
1181 .ok_or_else(|| "Tool not found".into())
1182 }
1183
1184 fn protocol_name(&self) -> &str {
1185 "test"
1186 }
1187 }
1188
1189 let protocol = Arc::new(TestProtocol {
1191 tools: vec![
1192 ToolMetadata::new("tool1", "First tool"),
1193 ToolMetadata::new("tool2", "Second tool"),
1194 ],
1195 });
1196
1197 let mut registry = ToolRegistry::new(protocol.clone());
1198
1199 assert_eq!(registry.list_tools().len(), 0);
1201
1202 registry.discover_tools_from_primary().await.unwrap();
1204
1205 assert_eq!(registry.list_tools().len(), 2);
1207 assert!(registry.get_tool("tool1").is_some());
1208 assert!(registry.get_tool("tool2").is_some());
1209
1210 assert_eq!(registry.get_tool_protocol("tool1"), Some("primary"));
1212 assert_eq!(registry.get_tool_protocol("tool2"), Some("primary"));
1213 }
1214
1215 #[test]
1220 fn test_to_json_schema_string() {
1221 let param =
1222 ToolParameter::new("q", ToolParameterType::String).with_description("Search query");
1223 let schema = param.to_json_schema();
1224 assert_eq!(schema["type"], "string");
1225 assert_eq!(schema["description"], "Search query");
1226 }
1227
1228 #[test]
1229 fn test_to_json_schema_number() {
1230 let param = ToolParameter::new("value", ToolParameterType::Number);
1231 let schema = param.to_json_schema();
1232 assert_eq!(schema["type"], "number");
1233 assert!(schema.get("description").is_none());
1235 }
1236
1237 #[test]
1238 fn test_to_json_schema_integer() {
1239 let schema = ToolParameter::new("n", ToolParameterType::Integer).to_json_schema();
1240 assert_eq!(schema["type"], "integer");
1241 }
1242
1243 #[test]
1244 fn test_to_json_schema_boolean() {
1245 let schema = ToolParameter::new("flag", ToolParameterType::Boolean).to_json_schema();
1246 assert_eq!(schema["type"], "boolean");
1247 }
1248
1249 #[test]
1250 fn test_to_json_schema_array_with_items() {
1251 let param = ToolParameter::new("ids", ToolParameterType::Array)
1252 .with_items(ToolParameterType::Integer);
1253 let schema = param.to_json_schema();
1254 assert_eq!(schema["type"], "array");
1255 assert_eq!(schema["items"]["type"], "integer");
1256 }
1257
1258 #[test]
1259 fn test_to_json_schema_array_without_items() {
1260 let schema = ToolParameter::new("items", ToolParameterType::Array).to_json_schema();
1261 assert_eq!(schema["type"], "array");
1262 assert!(schema.get("items").is_some());
1264 }
1265
1266 #[test]
1267 fn test_to_json_schema_object_with_properties() {
1268 use std::collections::HashMap;
1269 let mut props = HashMap::new();
1270 props.insert(
1271 "name".to_string(),
1272 ToolParameter::new("name", ToolParameterType::String)
1273 .with_description("Person's name")
1274 .required(),
1275 );
1276 props.insert(
1277 "age".to_string(),
1278 ToolParameter::new("age", ToolParameterType::Integer),
1279 );
1280 let param = ToolParameter::new("person", ToolParameterType::Object).with_properties(props);
1281 let schema = param.to_json_schema();
1282 assert_eq!(schema["type"], "object");
1283 assert!(schema["properties"]["name"].is_object());
1284 assert!(schema["properties"]["age"].is_object());
1285 let required = schema["required"].as_array().unwrap();
1287 assert!(required.iter().any(|v| v.as_str() == Some("name")));
1288 assert!(!required.iter().any(|v| v.as_str() == Some("age")));
1289 }
1290
1291 #[test]
1292 fn test_to_tool_definition_roundtrip() {
1293 let meta = ToolMetadata::new("calculator", "Evaluates a math expression")
1294 .with_parameter(
1295 ToolParameter::new("expression", ToolParameterType::String)
1296 .with_description("The expression")
1297 .required(),
1298 )
1299 .with_parameter(
1300 ToolParameter::new("precision", ToolParameterType::Integer)
1301 .with_description("Decimal places"),
1302 );
1303
1304 let def = meta.to_tool_definition();
1305 assert_eq!(def.name, "calculator");
1306 assert_eq!(def.description, "Evaluates a math expression");
1307 assert_eq!(def.parameters_schema["type"], "object");
1308 assert!(def.parameters_schema["properties"]["expression"].is_object());
1309 assert!(def.parameters_schema["properties"]["precision"].is_object());
1310
1311 let required = def.parameters_schema["required"].as_array().unwrap();
1312 assert!(required.iter().any(|v| v.as_str() == Some("expression")));
1313 assert!(!required.iter().any(|v| v.as_str() == Some("precision")));
1314 }
1315
1316 #[test]
1317 fn test_to_tool_definitions_collects_all() {
1318 let protocol = Arc::new(MockProtocol);
1319 let mut registry = ToolRegistry::empty();
1320
1321 let tool_a = Tool::new("tool_a", "First tool", protocol.clone());
1322 registry.add_tool(tool_a);
1323 let tool_b = Tool::new("tool_b", "Second tool", protocol.clone());
1324 registry.add_tool(tool_b);
1325
1326 let defs = registry.to_tool_definitions();
1327 assert_eq!(defs.len(), 2);
1328 let names: Vec<&str> = defs.iter().map(|d| d.name.as_str()).collect();
1329 assert!(names.contains(&"tool_a"));
1330 assert!(names.contains(&"tool_b"));
1331 }
1332
1333 #[test]
1334 fn test_to_tool_definitions_empty_registry() {
1335 let registry = ToolRegistry::empty();
1336 let defs = registry.to_tool_definitions();
1337 assert!(defs.is_empty());
1338 }
1339}