mcp_sync/targets/
antigravity.rs1use crate::{
4 canon::{canon_names, Canon},
5 target::{SyncOptions, Target},
6 utils::{home, json_get_obj_mut, log_verbose, read_json_file, write_json_file},
7};
8use anyhow::{anyhow, Result};
9use serde_json::Value as JsonValue;
10use std::path::{Path, PathBuf};
11
12pub struct AntigravityTarget;
16
17impl Target for AntigravityTarget {
18 fn name(&self) -> &'static str {
19 "Antigravity"
20 }
21
22 fn global_path(&self) -> Result<PathBuf> {
23 let home = home()?;
24
25 #[cfg(target_os = "macos")]
26 let path = home
27 .join("Library")
28 .join("Application Support")
29 .join("Antigravity")
30 .join("User")
31 .join("mcp.json");
32
33 #[cfg(target_os = "windows")]
34 let path = home
35 .join("AppData")
36 .join("Roaming")
37 .join("Antigravity")
38 .join("User")
39 .join("mcp.json");
40
41 #[cfg(target_os = "linux")]
42 let path = home
43 .join(".config")
44 .join("Antigravity")
45 .join("User")
46 .join("mcp.json");
47
48 #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
49 let path = home.join(".antigravity").join("mcp.json");
50
51 Ok(path)
52 }
53
54 fn project_path(&self, project_root: &Path) -> PathBuf {
55 project_root.join(".vscode").join("mcp.json")
56 }
57
58 fn sync(&self, path: &Path, canon: &Canon, opts: &SyncOptions) -> Result<()> {
59 let mut root = read_json_file(path)?;
60 let servers_map = json_get_obj_mut(&mut root, "servers")?;
61
62 for (name, s) in &canon.servers {
63 let cfg = if s.kind() == "http" {
64 let url = s.url.clone().ok_or_else(|| anyhow!("server {}: http missing url", name))?;
65 let mut obj = serde_json::Map::new();
66 obj.insert("url".into(), JsonValue::String(url));
67 if let Some(h) = &s.headers {
68 obj.insert("headers".into(), serde_json::to_value(h)?);
69 }
70 JsonValue::Object(obj)
71 } else {
72 let cmd = s.command.clone().ok_or_else(|| anyhow!("server {}: stdio missing command", name))?;
73 let mut obj = serde_json::Map::new();
74 obj.insert("command".into(), JsonValue::String(cmd));
75 obj.insert(
76 "args".into(),
77 JsonValue::Array(
78 s.args
79 .clone()
80 .unwrap_or_default()
81 .into_iter()
82 .map(JsonValue::String)
83 .collect(),
84 ),
85 );
86 if let Some(env) = &s.env {
87 obj.insert("env".into(), serde_json::to_value(env)?);
88 }
89 JsonValue::Object(obj)
90 };
91
92 servers_map.insert(name.clone(), cfg);
93 }
94
95 if opts.clean {
96 let canon_set = canon_names(canon);
97 let existing: Vec<String> = servers_map.keys().cloned().collect();
98 for k in existing {
99 if !canon_set.contains(&k) {
100 servers_map.remove(&k);
101 }
102 }
103 }
104
105 let label = if path.starts_with(home().unwrap_or_default()) {
106 "global"
107 } else {
108 "project"
109 };
110 log_verbose(opts.verbose, format!("{} {} -> {:?}", self.name(), label, path));
111 write_json_file(path, &root, opts.dry_run)
112 }
113}