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 {
570 tools: HashMap<String, Tool>,
572 tool_to_protocol: HashMap<String, String>,
574 protocols: HashMap<String, Arc<dyn ToolProtocol>>,
576 primary_protocol: Option<Arc<dyn ToolProtocol>>,
578}
579
580impl ToolRegistry {
581 pub fn new(protocol: Arc<dyn ToolProtocol>) -> Self {
586 Self {
587 tools: HashMap::new(),
588 tool_to_protocol: HashMap::new(),
589 protocols: {
590 let mut m = HashMap::new();
591 m.insert("primary".to_string(), protocol.clone());
592 m
593 },
594 primary_protocol: Some(protocol),
595 }
596 }
597
598 pub fn empty() -> Self {
602 Self {
603 tools: HashMap::new(),
604 tool_to_protocol: HashMap::new(),
605 protocols: HashMap::new(),
606 primary_protocol: None,
607 }
608 }
609
610 pub async fn add_protocol(
644 &mut self,
645 protocol_name: &str,
646 protocol: Arc<dyn ToolProtocol>,
647 ) -> Result<(), Box<dyn Error + Send + Sync>> {
648 let discovered_tools = protocol.list_tools().await?;
650
651 self.protocols
653 .insert(protocol_name.to_string(), protocol.clone());
654
655 for tool_meta in discovered_tools {
657 let tool_name = tool_meta.name.clone();
658
659 let tool = Tool::new(
661 tool_name.clone(),
662 tool_meta.description.clone(),
663 protocol.clone(),
664 );
665
666 let mut tool = tool;
668 for param in &tool_meta.parameters {
669 tool = tool.with_parameter(param.clone());
670 }
671 for (key, value) in &tool_meta.protocol_metadata {
672 tool = tool.with_protocol_metadata(key.clone(), value.clone());
673 }
674
675 self.tools.insert(tool_name.clone(), tool);
677 self.tool_to_protocol
678 .insert(tool_name, protocol_name.to_string());
679 }
680
681 Ok(())
682 }
683
684 pub fn remove_protocol(&mut self, protocol_name: &str) {
686 self.protocols.remove(protocol_name);
687
688 let tools_to_remove: Vec<String> = self
690 .tool_to_protocol
691 .iter()
692 .filter(|(_, pn)| *pn == protocol_name)
693 .map(|(tn, _)| tn.clone())
694 .collect();
695
696 for tool_name in tools_to_remove {
698 self.tools.remove(&tool_name);
699 self.tool_to_protocol.remove(&tool_name);
700 }
701 }
702
703 pub fn add_tool(&mut self, tool: Tool) {
705 self.tools.insert(tool.metadata.name.clone(), tool);
706 }
707
708 pub fn remove_tool(&mut self, name: &str) -> Option<Tool> {
710 self.tool_to_protocol.remove(name);
711 self.tools.remove(name)
712 }
713
714 pub fn get_tool(&self, name: &str) -> Option<&Tool> {
716 self.tools.get(name)
717 }
718
719 pub fn list_tools(&self) -> Vec<&ToolMetadata> {
721 self.tools.values().map(|t| &t.metadata).collect()
722 }
723
724 pub async fn discover_tools_from_primary(
729 &mut self,
730 ) -> Result<(), Box<dyn Error + Send + Sync>> {
731 if let Some(protocol) = &self.primary_protocol {
732 let discovered_tools = protocol.list_tools().await?;
733 for tool_meta in discovered_tools {
734 let tool_name = tool_meta.name.clone();
735 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);
751 self.tool_to_protocol
752 .insert(tool_name, "primary".to_string());
753 }
754 Ok(())
755 } else {
756 Err("No primary protocol available".into())
757 }
758 }
759
760 pub fn get_tool_protocol(&self, tool_name: &str) -> Option<&str> {
762 self.tool_to_protocol.get(tool_name).map(|s| s.as_str())
763 }
764
765 pub fn list_protocols(&self) -> Vec<&str> {
767 self.protocols.keys().map(|s| s.as_str()).collect()
768 }
769
770 pub async fn execute_tool(
772 &self,
773 tool_name: &str,
774 parameters: serde_json::Value,
775 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
776 let tool = self
777 .tools
778 .get(tool_name)
779 .ok_or_else(|| ToolError::NotFound(tool_name.to_string()))?;
780
781 tool.execute(parameters).await
782 }
783
784 pub fn protocol(&self) -> Option<&Arc<dyn ToolProtocol>> {
788 self.primary_protocol.as_ref()
789 }
790
791 pub fn to_tool_definitions(&self) -> Vec<ToolDefinition> {
818 self.tools
819 .values()
820 .map(|t| t.metadata.to_tool_definition())
821 .collect()
822 }
823}
824
825#[cfg(test)]
826mod tests {
827 use super::*;
828
829 struct MockProtocol;
830
831 #[async_trait]
832 impl ToolProtocol for MockProtocol {
833 async fn execute(
834 &self,
835 tool_name: &str,
836 _parameters: serde_json::Value,
837 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
838 Ok(ToolResult::success(serde_json::json!({
839 "tool": tool_name,
840 "result": "mock_result"
841 })))
842 }
843
844 async fn list_tools(&self) -> Result<Vec<ToolMetadata>, Box<dyn Error + Send + Sync>> {
845 Ok(vec![])
846 }
847
848 async fn get_tool_metadata(
849 &self,
850 _tool_name: &str,
851 ) -> Result<ToolMetadata, Box<dyn Error + Send + Sync>> {
852 Ok(ToolMetadata::new("mock_tool", "A mock tool"))
853 }
854
855 fn protocol_name(&self) -> &str {
856 "mock"
857 }
858 }
859
860 #[test]
861 fn test_tool_parameter_builder() {
862 let param = ToolParameter::new("test_param", ToolParameterType::String)
863 .with_description("A test parameter")
864 .required()
865 .with_default(serde_json::json!("default_value"));
866
867 assert_eq!(param.name, "test_param");
868 assert_eq!(param.param_type, ToolParameterType::String);
869 assert_eq!(param.description, Some("A test parameter".to_string()));
870 assert!(param.required);
871 assert_eq!(param.default, Some(serde_json::json!("default_value")));
872 }
873
874 #[tokio::test]
875 async fn test_tool_execution() {
876 let protocol = Arc::new(MockProtocol);
877 let tool = Tool::new("test_tool", "A test tool", protocol.clone());
878
879 let result = tool.execute(serde_json::json!({})).await.unwrap();
880 assert!(result.success);
881 assert_eq!(result.output["tool"], "test_tool");
882 }
883
884 #[tokio::test]
885 async fn test_tool_registry() {
886 let protocol = Arc::new(MockProtocol);
887 let mut registry = ToolRegistry::new(protocol.clone());
888
889 let tool = Tool::new("calculator", "Performs calculations", protocol.clone());
890 registry.add_tool(tool);
891
892 assert!(registry.get_tool("calculator").is_some());
893 assert_eq!(registry.list_tools().len(), 1);
894
895 let result = registry
896 .execute_tool("calculator", serde_json::json!({}))
897 .await
898 .unwrap();
899 assert!(result.success);
900 }
901
902 #[tokio::test]
903 async fn test_empty_registry_creation() {
904 let registry = ToolRegistry::empty();
905 assert_eq!(registry.list_tools().len(), 0);
906 assert_eq!(registry.list_protocols().len(), 0);
907 assert!(registry.protocol().is_none());
908 }
909
910 #[tokio::test]
911 async fn test_add_single_protocol_to_empty_registry() {
912 let protocol = Arc::new(MockProtocol);
913 let mut registry = ToolRegistry::empty();
914
915 registry
917 .add_protocol("mock", protocol.clone())
918 .await
919 .unwrap();
920
921 assert_eq!(registry.list_protocols().len(), 1);
923 assert!(registry.list_protocols().contains(&"mock"));
924 }
925
926 #[tokio::test]
927 async fn test_add_multiple_protocols() {
928 let protocol1 = Arc::new(MockProtocol);
929 let protocol2 = Arc::new(MockProtocol);
930 let mut registry = ToolRegistry::empty();
931
932 registry
934 .add_protocol("protocol1", protocol1.clone())
935 .await
936 .unwrap();
937 registry
938 .add_protocol("protocol2", protocol2.clone())
939 .await
940 .unwrap();
941
942 assert_eq!(registry.list_protocols().len(), 2);
944 assert!(registry.list_protocols().contains(&"protocol1"));
945 assert!(registry.list_protocols().contains(&"protocol2"));
946 }
947
948 #[tokio::test]
949 async fn test_remove_protocol() {
950 let protocol = Arc::new(MockProtocol);
951 let mut registry = ToolRegistry::empty();
952
953 registry
955 .add_protocol("protocol1", protocol.clone())
956 .await
957 .unwrap();
958 assert_eq!(registry.list_protocols().len(), 1);
959
960 registry.remove_protocol("protocol1");
962 assert_eq!(registry.list_protocols().len(), 0);
963 }
964
965 #[tokio::test]
966 async fn test_get_tool_protocol() {
967 let protocol = Arc::new(MockProtocol);
968 let mut registry = ToolRegistry::empty();
969
970 registry
972 .add_protocol("local", protocol.clone())
973 .await
974 .unwrap();
975
976 let tool = Tool::new("calculator", "Performs calculations", protocol.clone());
978 registry.add_tool(tool);
979 registry
980 .tool_to_protocol
981 .insert("calculator".to_string(), "local".to_string());
982
983 assert_eq!(registry.get_tool_protocol("calculator"), Some("local"));
985 assert_eq!(registry.get_tool_protocol("nonexistent"), None);
986 }
987
988 #[tokio::test]
989 async fn test_remove_protocol_removes_tools() {
990 let protocol = Arc::new(MockProtocol);
991 let mut registry = ToolRegistry::empty();
992
993 registry
995 .add_protocol("protocol1", protocol.clone())
996 .await
997 .unwrap();
998
999 let tool1 = Tool::new("tool1", "First tool", protocol.clone());
1000 registry.add_tool(tool1);
1001 registry
1002 .tool_to_protocol
1003 .insert("tool1".to_string(), "protocol1".to_string());
1004
1005 let tool2 = Tool::new("tool2", "Second tool", protocol.clone());
1006 registry.add_tool(tool2);
1007 registry
1008 .tool_to_protocol
1009 .insert("tool2".to_string(), "protocol1".to_string());
1010
1011 assert_eq!(registry.list_tools().len(), 2);
1012
1013 registry.remove_protocol("protocol1");
1015
1016 assert_eq!(registry.list_tools().len(), 0);
1018 assert_eq!(registry.get_tool_protocol("tool1"), None);
1019 assert_eq!(registry.get_tool_protocol("tool2"), None);
1020 }
1021
1022 #[tokio::test]
1023 async fn test_execute_tool_through_registry() {
1024 let protocol = Arc::new(MockProtocol);
1025 let mut registry = ToolRegistry::empty();
1026
1027 registry
1029 .add_protocol("mock", protocol.clone())
1030 .await
1031 .unwrap();
1032
1033 let tool = Tool::new("test_tool", "A test tool", protocol.clone());
1035 registry.add_tool(tool);
1036
1037 let result = registry
1038 .execute_tool("test_tool", serde_json::json!({}))
1039 .await
1040 .unwrap();
1041
1042 assert!(result.success);
1043 assert_eq!(result.output["tool"], "test_tool");
1044 }
1045
1046 #[tokio::test]
1047 async fn test_backwards_compatibility_single_protocol() {
1048 let protocol = Arc::new(MockProtocol);
1049 let registry = ToolRegistry::new(protocol.clone());
1050
1051 assert!(registry.protocol().is_some());
1053 assert_eq!(registry.list_protocols().len(), 1);
1054 assert!(registry.list_protocols().contains(&"primary"));
1055 }
1056
1057 #[tokio::test]
1058 async fn test_discover_tools_from_primary() {
1059 struct TestProtocol {
1060 tools: Vec<ToolMetadata>,
1061 }
1062
1063 #[async_trait]
1064 impl ToolProtocol for TestProtocol {
1065 async fn execute(
1066 &self,
1067 tool_name: &str,
1068 _parameters: serde_json::Value,
1069 ) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
1070 Ok(ToolResult::success(serde_json::json!({
1071 "tool": tool_name,
1072 })))
1073 }
1074
1075 async fn list_tools(&self) -> Result<Vec<ToolMetadata>, Box<dyn Error + Send + Sync>> {
1076 Ok(self.tools.clone())
1077 }
1078
1079 async fn get_tool_metadata(
1080 &self,
1081 tool_name: &str,
1082 ) -> Result<ToolMetadata, Box<dyn Error + Send + Sync>> {
1083 self.tools
1084 .iter()
1085 .find(|t| t.name == tool_name)
1086 .cloned()
1087 .ok_or_else(|| "Tool not found".into())
1088 }
1089
1090 fn protocol_name(&self) -> &str {
1091 "test"
1092 }
1093 }
1094
1095 let protocol = Arc::new(TestProtocol {
1097 tools: vec![
1098 ToolMetadata::new("tool1", "First tool"),
1099 ToolMetadata::new("tool2", "Second tool"),
1100 ],
1101 });
1102
1103 let mut registry = ToolRegistry::new(protocol.clone());
1104
1105 assert_eq!(registry.list_tools().len(), 0);
1107
1108 registry.discover_tools_from_primary().await.unwrap();
1110
1111 assert_eq!(registry.list_tools().len(), 2);
1113 assert!(registry.get_tool("tool1").is_some());
1114 assert!(registry.get_tool("tool2").is_some());
1115
1116 assert_eq!(registry.get_tool_protocol("tool1"), Some("primary"));
1118 assert_eq!(registry.get_tool_protocol("tool2"), Some("primary"));
1119 }
1120
1121 #[test]
1126 fn test_to_json_schema_string() {
1127 let param =
1128 ToolParameter::new("q", ToolParameterType::String).with_description("Search query");
1129 let schema = param.to_json_schema();
1130 assert_eq!(schema["type"], "string");
1131 assert_eq!(schema["description"], "Search query");
1132 }
1133
1134 #[test]
1135 fn test_to_json_schema_number() {
1136 let param = ToolParameter::new("value", ToolParameterType::Number);
1137 let schema = param.to_json_schema();
1138 assert_eq!(schema["type"], "number");
1139 assert!(schema.get("description").is_none());
1141 }
1142
1143 #[test]
1144 fn test_to_json_schema_integer() {
1145 let schema = ToolParameter::new("n", ToolParameterType::Integer).to_json_schema();
1146 assert_eq!(schema["type"], "integer");
1147 }
1148
1149 #[test]
1150 fn test_to_json_schema_boolean() {
1151 let schema = ToolParameter::new("flag", ToolParameterType::Boolean).to_json_schema();
1152 assert_eq!(schema["type"], "boolean");
1153 }
1154
1155 #[test]
1156 fn test_to_json_schema_array_with_items() {
1157 let param = ToolParameter::new("ids", ToolParameterType::Array)
1158 .with_items(ToolParameterType::Integer);
1159 let schema = param.to_json_schema();
1160 assert_eq!(schema["type"], "array");
1161 assert_eq!(schema["items"]["type"], "integer");
1162 }
1163
1164 #[test]
1165 fn test_to_json_schema_array_without_items() {
1166 let schema = ToolParameter::new("items", ToolParameterType::Array).to_json_schema();
1167 assert_eq!(schema["type"], "array");
1168 assert!(schema.get("items").is_some());
1170 }
1171
1172 #[test]
1173 fn test_to_json_schema_object_with_properties() {
1174 use std::collections::HashMap;
1175 let mut props = HashMap::new();
1176 props.insert(
1177 "name".to_string(),
1178 ToolParameter::new("name", ToolParameterType::String)
1179 .with_description("Person's name")
1180 .required(),
1181 );
1182 props.insert(
1183 "age".to_string(),
1184 ToolParameter::new("age", ToolParameterType::Integer),
1185 );
1186 let param = ToolParameter::new("person", ToolParameterType::Object).with_properties(props);
1187 let schema = param.to_json_schema();
1188 assert_eq!(schema["type"], "object");
1189 assert!(schema["properties"]["name"].is_object());
1190 assert!(schema["properties"]["age"].is_object());
1191 let required = schema["required"].as_array().unwrap();
1193 assert!(required.iter().any(|v| v.as_str() == Some("name")));
1194 assert!(!required.iter().any(|v| v.as_str() == Some("age")));
1195 }
1196
1197 #[test]
1198 fn test_to_tool_definition_roundtrip() {
1199 let meta = ToolMetadata::new("calculator", "Evaluates a math expression")
1200 .with_parameter(
1201 ToolParameter::new("expression", ToolParameterType::String)
1202 .with_description("The expression")
1203 .required(),
1204 )
1205 .with_parameter(
1206 ToolParameter::new("precision", ToolParameterType::Integer)
1207 .with_description("Decimal places"),
1208 );
1209
1210 let def = meta.to_tool_definition();
1211 assert_eq!(def.name, "calculator");
1212 assert_eq!(def.description, "Evaluates a math expression");
1213 assert_eq!(def.parameters_schema["type"], "object");
1214 assert!(def.parameters_schema["properties"]["expression"].is_object());
1215 assert!(def.parameters_schema["properties"]["precision"].is_object());
1216
1217 let required = def.parameters_schema["required"].as_array().unwrap();
1218 assert!(required.iter().any(|v| v.as_str() == Some("expression")));
1219 assert!(!required.iter().any(|v| v.as_str() == Some("precision")));
1220 }
1221
1222 #[test]
1223 fn test_to_tool_definitions_collects_all() {
1224 let protocol = Arc::new(MockProtocol);
1225 let mut registry = ToolRegistry::empty();
1226
1227 let tool_a = Tool::new("tool_a", "First tool", protocol.clone());
1228 registry.add_tool(tool_a);
1229 let tool_b = Tool::new("tool_b", "Second tool", protocol.clone());
1230 registry.add_tool(tool_b);
1231
1232 let defs = registry.to_tool_definitions();
1233 assert_eq!(defs.len(), 2);
1234 let names: Vec<&str> = defs.iter().map(|d| d.name.as_str()).collect();
1235 assert!(names.contains(&"tool_a"));
1236 assert!(names.contains(&"tool_b"));
1237 }
1238
1239 #[test]
1240 fn test_to_tool_definitions_empty_registry() {
1241 let registry = ToolRegistry::empty();
1242 let defs = registry.to_tool_definitions();
1243 assert!(defs.is_empty());
1244 }
1245}