1use crate::cli::*;
4use crate::error::{CliError, CliResult};
5use crate::formatter::Formatter;
6use crate::transport::{ClientType, create_client};
7use std::collections::HashMap;
8
9pub struct CommandExecutor {
11 pub formatter: Formatter,
12 verbose: bool,
13}
14
15impl CommandExecutor {
16 pub fn new(format: OutputFormat, colored: bool, verbose: bool) -> Self {
17 Self {
18 formatter: Formatter::new(format, colored),
19 verbose,
20 }
21 }
22
23 pub fn display_error(&self, error: &CliError) {
25 self.formatter.display_error(error);
26 }
27
28 pub async fn execute(&self, command: Commands) -> CliResult<()> {
30 match command {
31 Commands::Tools(cmd) => self.execute_tool_command(cmd).await,
32 Commands::Resources(cmd) => self.execute_resource_command(cmd).await,
33 Commands::Prompts(cmd) => self.execute_prompt_command(cmd).await,
34 Commands::Complete(cmd) => self.execute_completion_command(cmd).await,
35 Commands::Server(cmd) => self.execute_server_command(cmd).await,
36 Commands::Sample(cmd) => self.execute_sampling_command(cmd).await,
37 Commands::Connect(conn) => self.execute_connect(conn).await,
38 Commands::Status(conn) => self.execute_status(conn).await,
39 }
40 }
41
42 async fn execute_tool_command(&self, command: ToolCommands) -> CliResult<()> {
45 match command {
46 ToolCommands::List { conn } => {
47 let client_type = create_client(&conn).await?;
48 let tools = match client_type {
49 ClientType::Stdio(client) => {
50 client.initialize().await?;
51 client.list_tools().await?
52 }
53 ClientType::Tcp(client) => {
54 client.initialize().await?;
55 client.list_tools().await?
56 }
57 ClientType::Unix(client) => {
58 client.initialize().await?;
59 client.list_tools().await?
60 }
61 };
62 self.formatter.display_tools(&tools)
63 }
64
65 ToolCommands::Call {
66 conn,
67 name,
68 arguments,
69 } => {
70 let args: HashMap<String, serde_json::Value> =
71 if arguments.trim().is_empty() || arguments == "{}" {
72 HashMap::new()
73 } else {
74 serde_json::from_str(&arguments).map_err(|e| {
75 CliError::InvalidArguments(format!("Invalid JSON arguments: {}", e))
76 })?
77 };
78
79 let client_type = create_client(&conn).await?;
80 let result = match client_type {
81 ClientType::Stdio(client) => {
82 client.initialize().await?;
83 client.call_tool(&name, Some(args)).await?
84 }
85 ClientType::Tcp(client) => {
86 client.initialize().await?;
87 client.call_tool(&name, Some(args)).await?
88 }
89 ClientType::Unix(client) => {
90 client.initialize().await?;
91 client.call_tool(&name, Some(args)).await?
92 }
93 };
94 self.formatter.display(&result)
95 }
96
97 ToolCommands::Schema { conn, name } => {
98 let client_type = create_client(&conn).await?;
99 let tools = match client_type {
100 ClientType::Stdio(client) => {
101 client.initialize().await?;
102 client.list_tools().await?
103 }
104 ClientType::Tcp(client) => {
105 client.initialize().await?;
106 client.list_tools().await?
107 }
108 ClientType::Unix(client) => {
109 client.initialize().await?;
110 client.list_tools().await?
111 }
112 };
113
114 if let Some(tool_name) = name {
115 let tool = tools.iter().find(|t| t.name == tool_name).ok_or_else(|| {
116 CliError::Other(format!("Tool '{}' not found", tool_name))
117 })?;
118
119 self.formatter.display(&tool.input_schema)
120 } else {
121 let schemas: Vec<_> = tools
122 .iter()
123 .map(|t| {
124 serde_json::json!({
125 "name": t.name,
126 "schema": t.input_schema
127 })
128 })
129 .collect();
130
131 self.formatter.display(&schemas)
132 }
133 }
134
135 ToolCommands::Export { conn, output } => {
136 let client_type = create_client(&conn).await?;
137 let tools = match client_type {
138 ClientType::Stdio(client) => {
139 client.initialize().await?;
140 client.list_tools().await?
141 }
142 ClientType::Tcp(client) => {
143 client.initialize().await?;
144 client.list_tools().await?
145 }
146 ClientType::Unix(client) => {
147 client.initialize().await?;
148 client.list_tools().await?
149 }
150 };
151
152 std::fs::create_dir_all(&output)?;
153
154 for tool in tools {
155 let filename = format!("{}.json", tool.name);
156 let filepath = output.join(filename);
157 let schema = serde_json::to_string_pretty(&tool.input_schema)?;
158 std::fs::write(&filepath, schema)?;
159
160 if self.verbose {
161 println!("Exported: {}", filepath.display());
162 }
163 }
164
165 println!("✓ Exported schemas to: {}", output.display());
166 Ok(())
167 }
168 }
169 }
170
171 async fn execute_resource_command(&self, command: ResourceCommands) -> CliResult<()> {
174 match command {
175 ResourceCommands::List { conn } => {
176 let client_type = create_client(&conn).await?;
177 let resource_uris = match client_type {
178 ClientType::Stdio(client) => {
179 client.initialize().await?;
180 client.list_resources().await?
181 }
182 ClientType::Tcp(client) => {
183 client.initialize().await?;
184 client.list_resources().await?
185 }
186 ClientType::Unix(client) => {
187 client.initialize().await?;
188 client.list_resources().await?
189 }
190 };
191
192 self.formatter.display(&resource_uris)
195 }
196
197 ResourceCommands::Read { conn, uri } => {
198 let client_type = create_client(&conn).await?;
199 let result = match client_type {
200 ClientType::Stdio(client) => {
201 client.initialize().await?;
202 client.read_resource(&uri).await?
203 }
204 ClientType::Tcp(client) => {
205 client.initialize().await?;
206 client.read_resource(&uri).await?
207 }
208 ClientType::Unix(client) => {
209 client.initialize().await?;
210 client.read_resource(&uri).await?
211 }
212 };
213 self.formatter.display(&result)
214 }
215
216 ResourceCommands::Templates { conn } => {
217 let client_type = create_client(&conn).await?;
218 let templates = match client_type {
219 ClientType::Stdio(client) => {
220 client.initialize().await?;
221 client.list_resource_templates().await?
222 }
223 ClientType::Tcp(client) => {
224 client.initialize().await?;
225 client.list_resource_templates().await?
226 }
227 ClientType::Unix(client) => {
228 client.initialize().await?;
229 client.list_resource_templates().await?
230 }
231 };
232 self.formatter.display(&templates)
233 }
234
235 ResourceCommands::Subscribe { conn, uri } => {
236 let client_type = create_client(&conn).await?;
237 match client_type {
238 ClientType::Stdio(client) => {
239 client.initialize().await?;
240 client.subscribe(&uri).await?;
241 }
242 ClientType::Tcp(client) => {
243 client.initialize().await?;
244 client.subscribe(&uri).await?;
245 }
246 ClientType::Unix(client) => {
247 client.initialize().await?;
248 client.subscribe(&uri).await?;
249 }
250 }
251 println!("✓ Subscribed to: {uri}");
252 Ok(())
253 }
254
255 ResourceCommands::Unsubscribe { conn, uri } => {
256 let client_type = create_client(&conn).await?;
257 match client_type {
258 ClientType::Stdio(client) => {
259 client.initialize().await?;
260 client.unsubscribe(&uri).await?;
261 }
262 ClientType::Tcp(client) => {
263 client.initialize().await?;
264 client.unsubscribe(&uri).await?;
265 }
266 ClientType::Unix(client) => {
267 client.initialize().await?;
268 client.unsubscribe(&uri).await?;
269 }
270 }
271 println!("✓ Unsubscribed from: {uri}");
272 Ok(())
273 }
274 }
275 }
276
277 async fn execute_prompt_command(&self, command: PromptCommands) -> CliResult<()> {
280 match command {
281 PromptCommands::List { conn } => {
282 let client_type = create_client(&conn).await?;
283 let prompts = match client_type {
284 ClientType::Stdio(client) => {
285 client.initialize().await?;
286 client.list_prompts().await?
287 }
288 ClientType::Tcp(client) => {
289 client.initialize().await?;
290 client.list_prompts().await?
291 }
292 ClientType::Unix(client) => {
293 client.initialize().await?;
294 client.list_prompts().await?
295 }
296 };
297 self.formatter.display_prompts(&prompts)
298 }
299
300 PromptCommands::Get {
301 conn,
302 name,
303 arguments,
304 } => {
305 let args: HashMap<String, serde_json::Value> =
307 if arguments.trim().is_empty() || arguments == "{}" {
308 HashMap::new()
309 } else {
310 serde_json::from_str(&arguments).map_err(|e| {
311 CliError::InvalidArguments(format!("Invalid JSON arguments: {}", e))
312 })?
313 };
314
315 let args_option = if args.is_empty() { None } else { Some(args) };
316
317 let client_type = create_client(&conn).await?;
318 let result = match client_type {
319 ClientType::Stdio(client) => {
320 client.initialize().await?;
321 client.get_prompt(&name, args_option).await?
322 }
323 ClientType::Tcp(client) => {
324 client.initialize().await?;
325 client.get_prompt(&name, args_option.clone()).await?
326 }
327 ClientType::Unix(client) => {
328 client.initialize().await?;
329 client.get_prompt(&name, args_option.clone()).await?
330 }
331 };
332 self.formatter.display(&result)
333 }
334
335 PromptCommands::Schema { conn, name } => {
336 let client_type = create_client(&conn).await?;
337 let prompts = match client_type {
338 ClientType::Stdio(client) => {
339 client.initialize().await?;
340 client.list_prompts().await?
341 }
342 ClientType::Tcp(client) => {
343 client.initialize().await?;
344 client.list_prompts().await?
345 }
346 ClientType::Unix(client) => {
347 client.initialize().await?;
348 client.list_prompts().await?
349 }
350 };
351
352 let prompt = prompts
353 .iter()
354 .find(|p| p.name == name)
355 .ok_or_else(|| CliError::Other(format!("Prompt '{}' not found", name)))?;
356
357 self.formatter.display(&prompt.arguments)
358 }
359 }
360 }
361
362 async fn execute_completion_command(&self, command: CompletionCommands) -> CliResult<()> {
365 match command {
366 CompletionCommands::Get {
367 conn,
368 ref_type,
369 ref_value,
370 argument,
371 } => {
372 let client_type = create_client(&conn).await?;
373
374 let result = match ref_type {
376 RefType::Prompt => {
377 let arg_name = argument.as_deref().unwrap_or("value");
378 match client_type {
379 ClientType::Stdio(client) => {
380 client.initialize().await?;
381 client
382 .complete_prompt(&ref_value, arg_name, "", None)
383 .await?
384 }
385 ClientType::Tcp(client) => {
386 client.initialize().await?;
387 client
388 .complete_prompt(&ref_value, arg_name, "", None)
389 .await?
390 }
391 ClientType::Unix(client) => {
392 client.initialize().await?;
393 client
394 .complete_prompt(&ref_value, arg_name, "", None)
395 .await?
396 }
397 }
398 }
399 RefType::Resource => {
400 let arg_name = argument.as_deref().unwrap_or("uri");
401 match client_type {
402 ClientType::Stdio(client) => {
403 client.initialize().await?;
404 client
405 .complete_resource(&ref_value, arg_name, "", None)
406 .await?
407 }
408 ClientType::Tcp(client) => {
409 client.initialize().await?;
410 client
411 .complete_resource(&ref_value, arg_name, "", None)
412 .await?
413 }
414 ClientType::Unix(client) => {
415 client.initialize().await?;
416 client
417 .complete_resource(&ref_value, arg_name, "", None)
418 .await?
419 }
420 }
421 }
422 };
423
424 self.formatter.display(&result)
425 }
426 }
427 }
428
429 async fn execute_server_command(&self, command: ServerCommands) -> CliResult<()> {
432 match command {
433 ServerCommands::Info { conn } => {
434 let client_type = create_client(&conn).await?;
435 let info = match client_type {
436 ClientType::Stdio(client) => {
437 let result = client.initialize().await?;
438 result.server_info
439 }
440 ClientType::Tcp(client) => {
441 let result = client.initialize().await?;
442 result.server_info
443 }
444 ClientType::Unix(client) => {
445 let result = client.initialize().await?;
446 result.server_info
447 }
448 };
449 self.formatter.display_server_info(&info)
450 }
451
452 ServerCommands::Ping { conn } => {
453 let client_type = create_client(&conn).await?;
454 let start = std::time::Instant::now();
455
456 match client_type {
457 ClientType::Stdio(client) => {
458 client.initialize().await?;
459 client.ping().await?;
460 }
461 ClientType::Tcp(client) => {
462 client.initialize().await?;
463 client.ping().await?;
464 }
465 ClientType::Unix(client) => {
466 client.initialize().await?;
467 client.ping().await?;
468 }
469 }
470
471 let elapsed = start.elapsed();
472 println!("✓ Pong! ({:.2}ms)", elapsed.as_secs_f64() * 1000.0);
473 Ok(())
474 }
475
476 ServerCommands::LogLevel { conn, level } => {
477 let protocol_level: turbomcp_protocol::types::LogLevel = level.clone().into();
479
480 let client_type = create_client(&conn).await?;
481 match client_type {
482 ClientType::Stdio(client) => {
483 client.initialize().await?;
484 client.set_log_level(protocol_level).await?;
485 }
486 ClientType::Tcp(client) => {
487 client.initialize().await?;
488 client.set_log_level(protocol_level).await?;
489 }
490 ClientType::Unix(client) => {
491 client.initialize().await?;
492 client.set_log_level(protocol_level).await?;
493 }
494 }
495 println!("✓ Log level set to: {:?}", level);
496 Ok(())
497 }
498
499 ServerCommands::Roots { conn } => {
500 let client_type = create_client(&conn).await?;
502 let result = match client_type {
503 ClientType::Stdio(client) => client.initialize().await?,
504 ClientType::Tcp(client) => client.initialize().await?,
505 ClientType::Unix(client) => client.initialize().await?,
506 };
507
508 self.formatter.display(&result.server_capabilities)
510 }
511 }
512 }
513
514 async fn execute_sampling_command(&self, _command: SamplingCommands) -> CliResult<()> {
517 Err(CliError::NotSupported(
518 "Sampling commands require LLM handler implementation".to_string(),
519 ))
520 }
521
522 async fn execute_connect(&self, conn: Connection) -> CliResult<()> {
525 println!("Connecting to server...");
526 let client_type = create_client(&conn).await?;
527
528 let info = match client_type {
529 ClientType::Stdio(client) => {
530 let result = client.initialize().await?;
531 result.server_info
532 }
533 ClientType::Tcp(client) => {
534 let result = client.initialize().await?;
535 result.server_info
536 }
537 ClientType::Unix(client) => {
538 let result = client.initialize().await?;
539 result.server_info
540 }
541 };
542
543 println!("✓ Connected successfully!");
544 self.formatter.display_server_info(&info)
545 }
546
547 async fn execute_status(&self, conn: Connection) -> CliResult<()> {
548 let client_type = create_client(&conn).await?;
549
550 let info = match client_type {
551 ClientType::Stdio(client) => {
552 let result = client.initialize().await?;
553 result.server_info
554 }
555 ClientType::Tcp(client) => {
556 let result = client.initialize().await?;
557 result.server_info
558 }
559 ClientType::Unix(client) => {
560 let result = client.initialize().await?;
561 result.server_info
562 }
563 };
564
565 println!("Status: Connected");
566 self.formatter.display_server_info(&info)
567 }
568}