mcp_sync/commands/
init.rs1use anyhow::Result;
4use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};
5use std::fs;
6use std::path::Path;
7
8const SERVER_KINDS: &[&str] = &["stdio (local process)", "http (remote server)"];
10
11pub fn run(output_path: &Path) -> Result<()> {
13 let theme = ColorfulTheme::default();
14
15 println!("š MCP-Sync Configuration Wizard\n");
16
17 if output_path.exists() {
18 let overwrite = Confirm::with_theme(&theme)
19 .with_prompt(format!("{} already exists. Overwrite?", output_path.display()))
20 .default(false)
21 .interact()?;
22
23 if !overwrite {
24 println!("Aborted.");
25 return Ok(());
26 }
27 }
28
29 let mut servers = Vec::new();
30
31 loop {
32 println!("\n--- Add Server ---");
33
34 let name: String = Input::with_theme(&theme)
35 .with_prompt("Server name")
36 .interact_text()?;
37
38 let kind_idx = Select::with_theme(&theme)
39 .with_prompt("Server kind")
40 .items(SERVER_KINDS)
41 .default(0)
42 .interact()?;
43
44 let server_config = if kind_idx == 0 {
45 let command: String = Input::with_theme(&theme)
47 .with_prompt("Command")
48 .default("npx".to_string())
49 .interact_text()?;
50
51 let args_str: String = Input::with_theme(&theme)
52 .with_prompt("Arguments (space-separated)")
53 .default("-y @modelcontextprotocol/server".to_string())
54 .interact_text()?;
55
56 let args: Vec<String> = args_str
57 .split_whitespace()
58 .map(|s| s.to_string())
59 .collect();
60
61 format!(
62 " {}:\n command: {}\n args: [{}]",
63 name,
64 command,
65 args.iter()
66 .map(|a| format!("\"{}\"", a))
67 .collect::<Vec<_>>()
68 .join(", ")
69 )
70 } else {
71 let url: String = Input::with_theme(&theme)
73 .with_prompt("URL")
74 .default("https://mcp.example.com/v1".to_string())
75 .interact_text()?;
76
77 format!(" {}:\n kind: http\n url: {}", name, url)
78 };
79
80 servers.push(server_config);
81
82 let add_more = Confirm::with_theme(&theme)
83 .with_prompt("Add another server?")
84 .default(false)
85 .interact()?;
86
87 if !add_more {
88 break;
89 }
90 }
91
92 let use_env_expander = Confirm::with_theme(&theme)
94 .with_prompt("Enable env-expander plugin (expand ${VAR} in values)?")
95 .default(true)
96 .interact()?;
97
98 let mut yaml = String::from("version: 1\n\n");
100
101 if use_env_expander {
102 yaml.push_str("plugins:\n - name: env-expander\n\n");
103 }
104
105 yaml.push_str("servers:\n");
106 for server in servers {
107 yaml.push_str(&server);
108 yaml.push('\n');
109 }
110
111 if let Some(parent) = output_path.parent() {
113 fs::create_dir_all(parent)?;
114 }
115 fs::write(output_path, &yaml)?;
116
117 println!("\nā
Created {}", output_path.display());
118 println!("\nNext steps:");
119 println!(" mcp-sync validate --canon {}", output_path.display());
120 println!(" mcp-sync sync --canon {}", output_path.display());
121
122 Ok(())
123}