agtrace_types/tool/
args.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct FileReadArgs {
6    #[serde(default, skip_serializing_if = "Option::is_none")]
7    pub file_path: Option<String>,
8    #[serde(default, skip_serializing_if = "Option::is_none")]
9    pub path: Option<String>,
10    #[serde(default, skip_serializing_if = "Option::is_none")]
11    pub pattern: Option<String>,
12    #[serde(flatten)]
13    pub extra: Value,
14}
15
16impl FileReadArgs {
17    /// Get file path from various field names
18    pub fn path(&self) -> Option<&str> {
19        self.file_path.as_deref().or(self.path.as_deref())
20    }
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct FileEditArgs {
25    pub file_path: String,
26    pub old_string: String,
27    pub new_string: String,
28    #[serde(default)]
29    pub replace_all: bool,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct FileWriteArgs {
34    pub file_path: String,
35    pub content: String,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct ExecuteArgs {
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub command: Option<String>,
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub description: Option<String>,
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub timeout: Option<u64>,
46    #[serde(flatten)]
47    pub extra: Value,
48}
49
50impl ExecuteArgs {
51    pub fn command(&self) -> Option<&str> {
52        self.command.as_deref()
53    }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct SearchArgs {
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub pattern: Option<String>,
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub query: Option<String>,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub input: Option<String>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub path: Option<String>,
66    #[serde(flatten)]
67    pub extra: Value,
68}
69
70impl SearchArgs {
71    /// Get search pattern from various field names
72    pub fn pattern(&self) -> Option<&str> {
73        self.pattern
74            .as_deref()
75            .or(self.query.as_deref())
76            .or(self.input.as_deref())
77    }
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct McpArgs {
82    #[serde(flatten)]
83    pub inner: Value,
84}
85
86impl McpArgs {
87    /// Parse MCP tool name from full name (e.g., "mcp__o3__o3-search" -> ("o3", "o3-search"))
88    pub fn parse_name(full_name: &str) -> Option<(String, String)> {
89        if !full_name.starts_with("mcp__") {
90            return None;
91        }
92
93        let rest = &full_name[5..]; // Remove "mcp__"
94        let parts: Vec<&str> = rest.splitn(2, "__").collect();
95
96        if parts.len() == 2 {
97            Some((parts[0].to_string(), parts[1].to_string()))
98        } else {
99            None
100        }
101    }
102
103    /// Get server name from full MCP tool name
104    pub fn server_name(full_name: &str) -> Option<String> {
105        Self::parse_name(full_name).map(|(server, _)| server)
106    }
107
108    /// Get tool name from full MCP tool name
109    pub fn tool_name(full_name: &str) -> Option<String> {
110        Self::parse_name(full_name).map(|(_, tool)| tool)
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_file_read_args_path_helper() {
120        let args1 = FileReadArgs {
121            file_path: Some("/path1".to_string()),
122            path: None,
123            pattern: None,
124            extra: serde_json::json!({}),
125        };
126        assert_eq!(args1.path(), Some("/path1"));
127
128        let args2 = FileReadArgs {
129            file_path: None,
130            path: Some("/path2".to_string()),
131            pattern: None,
132            extra: serde_json::json!({}),
133        };
134        assert_eq!(args2.path(), Some("/path2"));
135
136        let args3 = FileReadArgs {
137            file_path: Some("/path1".to_string()),
138            path: Some("/path2".to_string()),
139            pattern: None,
140            extra: serde_json::json!({}),
141        };
142        assert_eq!(args3.path(), Some("/path1"));
143    }
144
145    #[test]
146    fn test_search_args_pattern_helper() {
147        let args1 = SearchArgs {
148            pattern: Some("pattern1".to_string()),
149            query: None,
150            input: None,
151            path: None,
152            extra: serde_json::json!({}),
153        };
154        assert_eq!(args1.pattern(), Some("pattern1"));
155
156        let args2 = SearchArgs {
157            pattern: None,
158            query: Some("query2".to_string()),
159            input: None,
160            path: None,
161            extra: serde_json::json!({}),
162        };
163        assert_eq!(args2.pattern(), Some("query2"));
164
165        let args3 = SearchArgs {
166            pattern: None,
167            query: None,
168            input: Some("input3".to_string()),
169            path: None,
170            extra: serde_json::json!({}),
171        };
172        assert_eq!(args3.pattern(), Some("input3"));
173    }
174
175    #[test]
176    fn test_mcp_args_parse_name() {
177        assert_eq!(
178            McpArgs::parse_name("mcp__o3__o3-search"),
179            Some(("o3".to_string(), "o3-search".to_string()))
180        );
181
182        assert_eq!(
183            McpArgs::parse_name("mcp__sqlite__query"),
184            Some(("sqlite".to_string(), "query".to_string()))
185        );
186
187        assert_eq!(McpArgs::parse_name("not_mcp_tool"), None);
188        assert_eq!(McpArgs::parse_name("mcp__only_server"), None);
189    }
190
191    #[test]
192    fn test_mcp_args_server_and_tool_name() {
193        assert_eq!(
194            McpArgs::server_name("mcp__o3__o3-search"),
195            Some("o3".to_string())
196        );
197        assert_eq!(
198            McpArgs::tool_name("mcp__o3__o3-search"),
199            Some("o3-search".to_string())
200        );
201    }
202}