1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
39pub struct InternalTool {
40 pub name: String,
42
43 pub description: String,
45
46 pub parameters: Value,
58
59 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
65 pub metadata: HashMap<String, Value>,
66}
67
68impl InternalTool {
69 pub fn new(name: impl Into<String>, description: impl Into<String>, parameters: Value) -> Self {
71 Self {
72 name: name.into(),
73 description: description.into(),
74 parameters,
75 metadata: HashMap::new(),
76 }
77 }
78
79 pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
81 self.metadata.insert(key.into(), value);
82 self
83 }
84
85 pub fn has_metadata(&self, key: &str) -> bool {
87 self.metadata.contains_key(key)
88 }
89
90 pub fn get_metadata(&self, key: &str) -> Option<&Value> {
92 self.metadata.get(key)
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
100pub struct InternalToolCall {
101 pub id: String,
105
106 pub name: String,
108
109 pub arguments: Value,
113}
114
115impl InternalToolCall {
116 pub fn new(id: impl Into<String>, name: impl Into<String>, arguments: Value) -> Self {
118 Self {
119 id: id.into(),
120 name: name.into(),
121 arguments,
122 }
123 }
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
130pub struct InternalToolResult {
131 pub tool_call_id: String,
133
134 pub content: String,
136
137 #[serde(default)]
139 pub is_error: bool,
140}
141
142impl InternalToolResult {
143 pub fn success(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
145 Self {
146 tool_call_id: tool_call_id.into(),
147 content: content.into(),
148 is_error: false,
149 }
150 }
151
152 pub fn error(tool_call_id: impl Into<String>, error: impl Into<String>) -> Self {
154 Self {
155 tool_call_id: tool_call_id.into(),
156 content: error.into(),
157 is_error: true,
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use serde_json::json;
166
167 #[test]
168 fn test_internal_tool_creation() {
169 let tool = InternalTool::new(
170 "get_weather",
171 "Get weather for a location",
172 json!({
173 "type": "object",
174 "properties": {
175 "location": { "type": "string" }
176 },
177 "required": ["location"]
178 }),
179 );
180
181 assert_eq!(tool.name, "get_weather");
182 assert_eq!(tool.description, "Get weather for a location");
183 assert!(tool.metadata.is_empty());
184 }
185
186 #[test]
187 fn test_internal_tool_with_metadata() {
188 let tool = InternalTool::new("test", "Test tool", json!({}))
189 .with_metadata("mcp_annotations", json!({"readOnlyHint": true}));
190
191 assert!(tool.has_metadata("mcp_annotations"));
192 assert_eq!(
193 tool.get_metadata("mcp_annotations"),
194 Some(&json!({"readOnlyHint": true}))
195 );
196 }
197
198 #[test]
199 fn test_internal_tool_call() {
200 let call = InternalToolCall::new("call_123", "get_weather", json!({"location": "SF"}));
201
202 assert_eq!(call.id, "call_123");
203 assert_eq!(call.name, "get_weather");
204 assert_eq!(call.arguments["location"], "SF");
205 }
206
207 #[test]
208 fn test_internal_tool_result() {
209 let success = InternalToolResult::success("call_123", "72°F, sunny");
210 assert!(!success.is_error);
211 assert_eq!(success.content, "72°F, sunny");
212
213 let error = InternalToolResult::error("call_456", "Location not found");
214 assert!(error.is_error);
215 assert_eq!(error.content, "Location not found");
216 }
217
218 #[test]
219 fn test_internal_tool_serialization() {
220 let tool = InternalTool::new("test", "Test", json!({"type": "object"}))
221 .with_metadata("source", json!("mcp"));
222
223 let json = serde_json::to_string(&tool).unwrap();
224 let deserialized: InternalTool = serde_json::from_str(&json).unwrap();
225
226 assert_eq!(deserialized, tool);
227 }
228}