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