vtcode_utility_tool_specs/
tool_kind.rs1use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum ToolKind {
16 Read,
18 Edit,
20 Delete,
22 ListDir,
24 Move,
26 Search,
28 Lsp,
30 Execute,
32 Plan,
34 WebSearch,
36 WebFetch,
38 Background,
40 Skill,
42 Memory,
44 Goal,
46 Other,
48}
49
50impl ToolKind {
51 pub fn as_str(self) -> &'static str {
53 match self {
54 Self::Read => "read",
55 Self::Edit => "edit",
56 Self::Delete => "delete",
57 Self::ListDir => "list_dir",
58 Self::Move => "move",
59 Self::Search => "search",
60 Self::Lsp => "lsp",
61 Self::Execute => "execute",
62 Self::Plan => "plan",
63 Self::WebSearch => "web_search",
64 Self::WebFetch => "web_fetch",
65 Self::Background => "background",
66 Self::Skill => "skill",
67 Self::Memory => "memory",
68 Self::Goal => "goal",
69 Self::Other => "other",
70 }
71 }
72}
73
74impl std::fmt::Display for ToolKind {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 f.write_str(self.as_str())
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
82#[serde(rename_all = "snake_case")]
83pub enum ToolNamespace {
84 Builtin,
86 Mcp,
88 Skill,
90 Extension,
92 Alias,
94 Other,
96}
97
98impl ToolNamespace {
99 pub fn as_str(self) -> &'static str {
101 match self {
102 Self::Builtin => "builtin",
103 Self::Mcp => "mcp",
104 Self::Skill => "skill",
105 Self::Extension => "extension",
106 Self::Alias => "alias",
107 Self::Other => "other",
108 }
109 }
110}
111
112impl std::fmt::Display for ToolNamespace {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 f.write_str(self.as_str())
115 }
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct CanonicalToolMeta {
125 pub kind: ToolKind,
127 pub namespace: ToolNamespace,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub label: Option<String>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub side_effects: Option<String>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub token_bucket: Option<TokenBucket>,
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
142#[serde(rename_all = "snake_case")]
143pub enum TokenBucket {
144 Small,
145 Medium,
146 Large,
147}
148
149impl CanonicalToolMeta {
150 pub fn new(kind: ToolKind, namespace: ToolNamespace) -> Self {
152 Self {
153 kind,
154 namespace,
155 label: None,
156 side_effects: None,
157 token_bucket: None,
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn tool_kind_round_trips() {
168 for kind in [
169 ToolKind::Read,
170 ToolKind::Edit,
171 ToolKind::Delete,
172 ToolKind::ListDir,
173 ToolKind::Move,
174 ToolKind::Search,
175 ToolKind::Lsp,
176 ToolKind::Execute,
177 ToolKind::Plan,
178 ToolKind::WebSearch,
179 ToolKind::WebFetch,
180 ToolKind::Background,
181 ToolKind::Skill,
182 ToolKind::Memory,
183 ToolKind::Goal,
184 ToolKind::Other,
185 ] {
186 let json = serde_json::to_string(&kind).unwrap();
187 let back: ToolKind = serde_json::from_str(&json).unwrap();
188 assert_eq!(kind, back);
189 assert!(!kind.as_str().is_empty());
190 }
191 }
192
193 #[test]
194 fn tool_namespace_round_trips() {
195 for ns in [
196 ToolNamespace::Builtin,
197 ToolNamespace::Mcp,
198 ToolNamespace::Skill,
199 ToolNamespace::Extension,
200 ToolNamespace::Alias,
201 ToolNamespace::Other,
202 ] {
203 let json = serde_json::to_string(&ns).unwrap();
204 let back: ToolNamespace = serde_json::from_str(&json).unwrap();
205 assert_eq!(ns, back);
206 assert!(!ns.as_str().is_empty());
207 }
208 }
209
210 #[test]
211 fn canonical_tool_meta_serializes_without_label() {
212 let meta = CanonicalToolMeta::new(ToolKind::Search, ToolNamespace::Builtin);
213 let json = serde_json::to_string(&meta).unwrap();
214 assert!(!json.contains("label"));
215 assert!(json.contains("search"));
216 assert!(json.contains("builtin"));
217 }
218
219 #[test]
220 fn canonical_tool_meta_serializes_with_optional_fields() {
221 let mut meta = CanonicalToolMeta::new(ToolKind::Execute, ToolNamespace::Builtin);
222 meta.label = Some("Shell".to_string());
223 meta.side_effects = Some("mutating".to_string());
224 meta.token_bucket = Some(TokenBucket::Medium);
225 let json = serde_json::to_string(&meta).unwrap();
226 assert!(json.contains("Shell"));
227 assert!(json.contains("mutating"));
228 assert!(json.contains("medium"));
229 }
230}