1use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::collections::HashMap;
13
14pub const PROTOCOL_VERSION: &str = "2025-01-01";
16
17pub const SUPPORTED_VERSIONS: &[&str] = &["2025-01-01", "2024-11-01"];
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct InitializeParams {
28 pub protocol_versions: Vec<String>,
30
31 pub capabilities: ClientCapabilities,
33
34 pub client_info: ClientInfo,
36}
37
38impl Default for InitializeParams {
39 fn default() -> Self {
40 Self {
41 protocol_versions: SUPPORTED_VERSIONS.iter().map(|s| s.to_string()).collect(),
42 capabilities: ClientCapabilities::default(),
43 client_info: ClientInfo::default(),
44 }
45 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub struct InitializeResult {
52 pub protocol_version: String,
54
55 pub capabilities: AgentCapabilities,
57
58 pub agent_info: AgentInfo,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub auth_requirements: Option<AuthRequirements>,
64}
65
66#[derive(Debug, Clone, Default, Serialize, Deserialize)]
72#[serde(rename_all = "camelCase")]
73pub struct ClientCapabilities {
74 #[serde(default)]
76 pub filesystem: FilesystemCapabilities,
77
78 #[serde(default)]
80 pub terminal: TerminalCapabilities,
81
82 #[serde(default)]
84 pub ui: UiCapabilities,
85
86 #[serde(default, skip_serializing_if = "Vec::is_empty")]
88 pub mcp_servers: Vec<McpServerCapability>,
89
90 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
92 pub extensions: HashMap<String, Value>,
93}
94
95#[derive(Debug, Clone, Default, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct FilesystemCapabilities {
99 #[serde(default)]
101 pub read: bool,
102
103 #[serde(default)]
105 pub write: bool,
106
107 #[serde(default)]
109 pub list: bool,
110
111 #[serde(default)]
113 pub search: bool,
114
115 #[serde(default)]
117 pub watch: bool,
118}
119
120#[derive(Debug, Clone, Default, Serialize, Deserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct TerminalCapabilities {
124 #[serde(default)]
126 pub create: bool,
127
128 #[serde(default)]
130 pub input: bool,
131
132 #[serde(default)]
134 pub output: bool,
135
136 #[serde(default)]
138 pub pty: bool,
139}
140
141#[derive(Debug, Clone, Default, Serialize, Deserialize)]
143#[serde(rename_all = "camelCase")]
144pub struct UiCapabilities {
145 #[serde(default)]
147 pub notifications: bool,
148
149 #[serde(default)]
151 pub progress: bool,
152
153 #[serde(default)]
155 pub input_prompt: bool,
156
157 #[serde(default)]
159 pub diff_view: bool,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164#[serde(rename_all = "camelCase")]
165pub struct McpServerCapability {
166 pub name: String,
168
169 pub transport: String,
171
172 #[serde(default, skip_serializing_if = "Vec::is_empty")]
174 pub tools: Vec<String>,
175}
176
177#[derive(Debug, Clone, Default, Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct AgentCapabilities {
185 #[serde(default, skip_serializing_if = "Vec::is_empty")]
187 pub tools: Vec<ToolCapability>,
188
189 #[serde(default)]
191 pub features: AgentFeatures,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
195 pub model: Option<ModelInfo>,
196
197 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
199 pub extensions: HashMap<String, Value>,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
204#[serde(rename_all = "camelCase")]
205pub struct ToolCapability {
206 pub name: String,
208
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub description: Option<String>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub input_schema: Option<Value>,
216
217 #[serde(default)]
219 pub requires_confirmation: bool,
220}
221
222#[derive(Debug, Clone, Default, Serialize, Deserialize)]
224#[serde(rename_all = "camelCase")]
225pub struct AgentFeatures {
226 #[serde(default)]
228 pub streaming: bool,
229
230 #[serde(default)]
232 pub multi_turn: bool,
233
234 #[serde(default)]
236 pub session_persistence: bool,
237
238 #[serde(default)]
240 pub vision: bool,
241
242 #[serde(default)]
244 pub code_execution: bool,
245
246 #[serde(default)]
248 pub subagents: bool,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize)]
253#[serde(rename_all = "camelCase")]
254pub struct ModelInfo {
255 pub id: String,
257
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub name: Option<String>,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub provider: Option<String>,
265
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub context_window: Option<u32>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct ClientInfo {
278 pub name: String,
280
281 pub version: String,
283
284 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
286 pub metadata: HashMap<String, Value>,
287}
288
289impl Default for ClientInfo {
290 fn default() -> Self {
291 Self {
292 name: "vtcode".to_string(),
293 version: env!("CARGO_PKG_VERSION").to_string(),
294 metadata: HashMap::new(),
295 }
296 }
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct AgentInfo {
302 pub name: String,
304
305 pub version: String,
307
308 #[serde(skip_serializing_if = "Option::is_none")]
310 pub description: Option<String>,
311
312 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
314 pub metadata: HashMap<String, Value>,
315}
316
317impl Default for AgentInfo {
318 fn default() -> Self {
319 Self {
320 name: "vtcode-agent".to_string(),
321 version: env!("CARGO_PKG_VERSION").to_string(),
322 description: Some("VT Code AI coding agent".to_string()),
323 metadata: HashMap::new(),
324 }
325 }
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
334#[serde(rename_all = "camelCase")]
335pub struct AuthRequirements {
336 pub required: bool,
338
339 #[serde(default, skip_serializing_if = "Vec::is_empty")]
341 pub methods: Vec<AuthMethod>,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
346#[serde(rename_all = "snake_case")]
347pub enum AuthMethod {
348 ApiKey,
350 OAuth2,
352 Bearer,
354 Custom(String),
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
360#[serde(rename_all = "camelCase")]
361pub struct AuthenticateParams {
362 pub method: AuthMethod,
364
365 pub credentials: AuthCredentials,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
371#[serde(tag = "type", rename_all = "snake_case")]
372pub enum AuthCredentials {
373 ApiKey { key: String },
375
376 Bearer { token: String },
378
379 OAuth2 {
381 access_token: String,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 refresh_token: Option<String>,
384 },
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize)]
389#[serde(rename_all = "camelCase")]
390pub struct AuthenticateResult {
391 pub authenticated: bool,
393
394 #[serde(skip_serializing_if = "Option::is_none")]
396 pub session_token: Option<String>,
397
398 #[serde(skip_serializing_if = "Option::is_none")]
400 pub expires_at: Option<String>,
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406
407 #[test]
408 fn test_initialize_params_default() {
409 let params = InitializeParams::default();
410 assert!(!params.protocol_versions.is_empty());
411 assert!(
412 params
413 .protocol_versions
414 .contains(&PROTOCOL_VERSION.to_string())
415 );
416 }
417
418 #[test]
419 fn test_client_info_default() {
420 let info = ClientInfo::default();
421 assert_eq!(info.name, "vtcode");
422 assert!(!info.version.is_empty());
423 }
424
425 #[test]
426 fn test_capabilities_serialization() {
427 let caps = ClientCapabilities {
428 filesystem: FilesystemCapabilities {
429 read: true,
430 write: true,
431 list: true,
432 search: true,
433 watch: false,
434 },
435 terminal: TerminalCapabilities {
436 create: true,
437 input: true,
438 output: true,
439 pty: true,
440 },
441 ..Default::default()
442 };
443
444 let json = serde_json::to_value(&caps).unwrap();
445 assert_eq!(json["filesystem"]["read"], true);
446 assert_eq!(json["terminal"]["pty"], true);
447 }
448
449 #[test]
450 fn test_auth_credentials() {
451 let creds = AuthCredentials::ApiKey {
452 key: "sk-test123".to_string(),
453 };
454 let json = serde_json::to_value(&creds).unwrap();
455 assert_eq!(json["type"], "api_key");
456 assert_eq!(json["key"], "sk-test123");
457 }
458}