1use crate::cli::{McpCommands, McpServerType as CliMcpServerType};
4use anyhow::Result;
5use colored::*;
6use std::collections::HashMap;
7
8pub async fn handle(command: McpCommands) -> Result<()> {
10 use crate::services::mcp::{McpConfig, McpServerType as ConfigMcpServerType};
11
12 match command {
13 McpCommands::Add {
14 name,
15 command_or_url,
16 server_type,
17 env,
18 } => {
19 let mut config = McpConfig::load().await?;
20
21 let config_server_type = match server_type {
23 CliMcpServerType::Stdio => ConfigMcpServerType::Stdio,
24 CliMcpServerType::Sse => ConfigMcpServerType::Sse,
25 CliMcpServerType::Streamable => ConfigMcpServerType::Streamable,
26 };
27
28 let env_map: HashMap<String, String> = env.into_iter().collect();
30
31 let final_command_or_url =
33 if command_or_url.starts_with("npx ") && !command_or_url.contains(" -y ") {
34 command_or_url.replacen("npx ", "npx -y ", 1)
35 } else {
36 command_or_url.clone()
37 };
38
39 config.add_server_with_env(
40 name.clone(),
41 final_command_or_url.clone(),
42 config_server_type,
43 env_map.clone(),
44 )?;
45 config.save().await?;
46
47 println!("{} MCP server '{}' added successfully", "✓".green(), name);
48 println!(" Type: {:?}", server_type);
49 println!(" Command/URL: {}", final_command_or_url);
50 if !env_map.is_empty() {
51 println!(" Environment variables:");
52 for (key, _) in env_map {
53 println!(" - {}", key);
54 }
55 }
56 }
57 McpCommands::Delete { name } => {
58 let mut config = McpConfig::load().await?;
59
60 if config.get_server(&name).is_none() {
61 anyhow::bail!("MCP server '{}' not found", name);
62 }
63
64 config.delete_server(&name)?;
65 config.save().await?;
66
67 println!("{} MCP server '{}' deleted successfully", "✓".green(), name);
68 }
69 McpCommands::Functions { name } => {
70 let config = McpConfig::load().await?;
71
72 if config.get_server(&name).is_some() {
73 println!(
74 "{} Listing functions for MCP server '{}'...",
75 "🔍".blue(),
76 name
77 );
78
79 let daemon_client = crate::services::mcp_daemon::DaemonClient::new()?;
80
81 match daemon_client.ensure_server_connected(&name).await {
83 Ok(_) => {
84 match daemon_client.list_tools(&name).await {
86 Ok(tools_map) => {
87 if let Some(tools) = tools_map.get(&name) {
88 if tools.is_empty() {
89 println!(" No functions exposed by this server.");
90 } else {
91 println!("\n{} Functions exposed by '{}':", "Functions:".bold().blue(), name);
92 for tool in tools {
93 let description = tool.description
94 .as_ref()
95 .map(|d| d.to_string())
96 .unwrap_or_else(|| "No description available".to_string());
97 println!(" {} {} - {}", "•".blue(), tool.name.bold(), description);
98 if !tool.input_schema.is_empty() {
99 let schema_value = serde_json::Value::Object((*tool.input_schema).clone());
101 if let Ok(pretty) = serde_json::to_string_pretty(&schema_value) {
102 for line in pretty.lines() {
103 println!(" {}", line.dimmed());
104 }
105 }
106 }
107 }
108 }
109 } else {
110 println!(" No functions exposed by this server.");
111 }
112 }
113 Err(e) => {
114 anyhow::bail!("Failed to list functions for '{}': {}", name, e);
115 }
116 }
117 }
118 Err(e) => {
119 anyhow::bail!("Failed to connect to MCP server '{}': {}", name, e);
120 }
121 }
122 } else {
123 anyhow::bail!("MCP server '{}' not found in configuration", name);
124 }
125 }
126 McpCommands::Invoke {
127 name,
128 function,
129 args,
130 } => {
131 let config = McpConfig::load().await?;
132
133 if config.get_server(&name).is_some() {
134 println!(
135 "{} Invoking function '{}' on MCP server '{}'...",
136 "⚡".yellow(),
137 function.bold(),
138 name.bold()
139 );
140
141 let daemon_client = crate::services::mcp_daemon::DaemonClient::new()?;
142
143 match daemon_client.ensure_server_connected(&name).await {
145 Ok(_) => {
146 let args_json = if args.is_empty() {
148 serde_json::json!({})
149 } else if args.len() == 1 {
150 match serde_json::from_str::<serde_json::Value>(&args[0]) {
152 Ok(json) => json,
153 Err(_) => {
154 if args[0].contains('=') {
156 let mut obj = serde_json::Map::new();
157 let parts: Vec<&str> = args[0].splitn(2, '=').collect();
158 if parts.len() == 2 {
159 obj.insert(
160 parts[0].to_string(),
161 serde_json::Value::String(parts[1].to_string())
162 );
163 }
164 serde_json::Value::Object(obj)
165 } else {
166 serde_json::json!({ "value": args[0] })
168 }
169 }
170 }
171 } else {
172 let mut obj = serde_json::Map::new();
174 let mut all_key_value = true;
175
176 for arg in args.iter() {
177 if arg.contains('=') {
178 let parts: Vec<&str> = arg.splitn(2, '=').collect();
179 if parts.len() == 2 {
180 let value = match serde_json::from_str::<serde_json::Value>(parts[1]) {
182 Ok(json_val) => json_val,
183 Err(_) => serde_json::Value::String(parts[1].to_string()),
184 };
185 obj.insert(parts[0].to_string(), value);
186 } else {
187 all_key_value = false;
188 break;
189 }
190 } else {
191 all_key_value = false;
192 break;
193 }
194 }
195
196 if !all_key_value {
197 obj.clear();
199 for (i, arg) in args.iter().enumerate() {
200 obj.insert(format!("arg{}", i), serde_json::Value::String(arg.clone()));
201 }
202 }
203
204 serde_json::Value::Object(obj)
205 };
206
207 match daemon_client.call_tool(&name, &function, args_json).await {
209 Ok(result) => {
210 println!("{} Function invoked successfully\n", "✓".green());
211
212 if let Ok(pretty) = serde_json::to_string_pretty(&result) {
214 println!("Result:");
215 for line in pretty.lines() {
216 println!(" {}", line);
217 }
218 } else {
219 println!("Result: {:?}", result);
220 }
221 }
222 Err(e) => {
223 anyhow::bail!("Failed to invoke function: {}", e);
224 }
225 }
226 }
227 Err(e) => {
228 anyhow::bail!("Failed to connect to MCP server '{}': {}", name, e);
229 }
230 }
231 } else {
232 anyhow::bail!("MCP server '{}' not found in configuration", name);
233 }
234 }
235 McpCommands::Start { name, command, args } => {
236 use crate::services::mcp::{McpConfig, McpServerType};
237 use std::collections::HashMap;
238
239 let mut config = McpConfig::load().await?;
240
241 let (full_command, server_type) = if let Some(cmd) = command {
243 println!(
245 "{} Configuring and starting MCP server '{}'...",
246 "🚀".cyan(),
247 name.bold()
248 );
249
250 let full_cmd = if args.is_empty() {
251 cmd.clone()
252 } else {
253 format!("{} {}", cmd, args.join(" "))
254 };
255
256 let srv_type = if cmd.starts_with("http://") || cmd.starts_with("https://") {
258 McpServerType::Sse
259 } else {
260 McpServerType::Stdio
261 };
262
263 config.add_server_with_env(
265 name.clone(),
266 full_cmd.clone(),
267 srv_type.clone(),
268 HashMap::new(),
269 )?;
270 config.save().await?;
271
272 (full_cmd, srv_type)
273 } else {
274 if let Some(server_config) = config.get_server(&name) {
276 println!(
277 "{} Starting MCP server '{}'...",
278 "🚀".cyan(),
279 name.bold()
280 );
281 (server_config.command_or_url.clone(), server_config.server_type.clone())
282 } else {
283 anyhow::bail!(
284 "MCP server '{}' not found in configuration. Use 'lc mcp add' to configure it first, or provide a command.",
285 name
286 );
287 }
288 };
289
290 let daemon_client = crate::services::mcp_daemon::DaemonClient::new()?;
292
293 match daemon_client.ensure_server_connected(&name).await {
294 Ok(_) => {
295 println!(
296 "{} MCP server '{}' started successfully",
297 "✓".green(),
298 name
299 );
300 println!(" Command: {}", full_command.dimmed());
301 println!(" Type: {:?}", server_type);
302 }
303 Err(e) => {
304 anyhow::bail!("Failed to start MCP server '{}': {}", name, e);
305 }
306 }
307 }
308 McpCommands::Stop { name } => {
309 println!(
310 "{} Stopping MCP server '{}'...",
311 "🛑".red(),
312 name.bold()
313 );
314
315 let daemon_client = crate::services::mcp_daemon::DaemonClient::new()?;
316 match daemon_client.close_server(&name).await {
317 Ok(_) => {
318 println!(
319 "{} MCP server '{}' stopped successfully",
320 "✓".green(),
321 name
322 );
323
324 use crate::services::mcp::McpConfig;
326 let mut config = McpConfig::load().await?;
327 if config.delete_server(&name).is_ok() {
328 let _ = config.save().await;
329 }
330 }
331 Err(e) => {
332 println!(
333 "{} Failed to stop MCP server '{}': {}",
334 "⚠️".yellow(),
335 name,
336 e
337 );
338 }
339 }
340 }
341 McpCommands::List => {
342 println!("{} MCP servers:", "📋".blue());
343
344 use crate::services::mcp::McpConfig;
346 let config = McpConfig::load().await?;
347 let servers = config.list_servers();
348
349 if servers.is_empty() {
350 println!(" No MCP servers configured.");
351 println!(
352 "\n{}",
353 "Add one with: lc mcp start <name> <command>".italic().dimmed()
354 );
355 } else {
356 let daemon_client = crate::services::mcp_daemon::DaemonClient::new();
358 let mut active_servers = vec![];
359
360 if let Ok(client) = daemon_client {
362 for (name, _) in &servers {
363 if client.list_tools(name).await.is_ok() {
365 active_servers.push(name.clone());
366 }
367 }
368 }
369
370 for (name, server_config) in servers {
371 let status = if active_servers.contains(&name) {
372 format!("{} (connected)", "✓".green())
373 } else {
374 "".to_string()
375 };
376
377 println!(
378 " {} {} - {:?} ({}) {}",
379 "•".blue(),
380 name.bold(),
381 server_config.server_type,
382 server_config.command_or_url.dimmed(),
383 status
384 );
385 }
386 }
387 }
388 McpCommands::Status { name } => {
389 if let Some(server_name) = name {
390 println!(
391 "{} Checking status of MCP server '{}'...",
392 "🔍".blue(),
393 server_name.bold()
394 );
395
396 use crate::services::mcp::McpConfig;
398 let config = McpConfig::load().await?;
399
400 if let Some(server_config) = config.get_server(&server_name) {
401 println!(" Configuration:");
402 println!(" Type: {:?}", server_config.server_type);
403 println!(" Command/URL: {}", server_config.command_or_url);
404
405 let daemon_client = crate::services::mcp_daemon::DaemonClient::new()?;
407
408 match daemon_client.list_tools(&server_name).await {
410 Ok(tools_map) => {
411 println!(" Status: {} Connected", "✓".green());
412 if let Some(tools) = tools_map.get(&server_name) {
413 println!(" Available tools: {}", tools.len());
414 if !tools.is_empty() {
415 println!(" Tools:");
416 for tool in tools.iter().take(5) {
417 println!(" - {}", tool.name);
418 }
419 if tools.len() > 5 {
420 println!(" ... and {} more", tools.len() - 5);
421 }
422 }
423 }
424 }
425 Err(_) => {
426 println!(" Status: {} Not connected", "✗".red());
427 println!(" Use 'lc mcp start {}' to connect", server_name);
428 }
429 }
430 } else {
431 println!("{} MCP server '{}' not found", "✗".red(), server_name);
432 println!("\nAvailable servers:");
433 let servers = config.list_servers();
434 if servers.is_empty() {
435 println!(" No servers configured");
436 } else {
437 for (name, _) in servers {
438 println!(" - {}", name);
439 }
440 }
441 }
442 } else {
443 println!("{} MCP server status:", "📊".blue());
445
446 use crate::services::mcp::McpConfig;
447 let config = McpConfig::load().await?;
448 let servers = config.list_servers();
449
450 if servers.is_empty() {
451 println!(" No MCP servers configured.");
452 } else {
453 let daemon_client = crate::services::mcp_daemon::DaemonClient::new();
454
455 for (name, server_config) in servers {
456 let status = if let Ok(client) = &daemon_client {
457 if client.list_tools(&name).await.is_ok() {
458 format!("{} Connected", "✓".green())
459 } else {
460 format!("{} Not connected", "✗".red())
461 }
462 } else {
463 format!("{} Daemon unavailable", "⚠️".yellow())
464 };
465
466 println!(
467 " {} {} ({:?}) - {}",
468 "•".blue(),
469 name.bold(),
470 server_config.server_type,
471 status
472 );
473 }
474 }
475 }
476 }
477 }
478 Ok(())
479}