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 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 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 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..]; 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 pub fn server_name(full_name: &str) -> Option<String> {
105 Self::parse_name(full_name).map(|(server, _)| server)
106 }
107
108 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}