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 resource_uris = 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(&resource_uris)
196 }
197
198 ResourceCommands::Read { conn, uri } => {
199 let client_type = create_client(&conn).await?;
200 let result = match client_type {
201 ClientType::Stdio(client) => {
202 client.initialize().await?;
203 client.read_resource(&uri).await?
204 }
205 ClientType::Tcp(client) => {
206 client.initialize().await?;
207 client.read_resource(&uri).await?
208 }
209 ClientType::Unix(client) => {
210 client.initialize().await?;
211 client.read_resource(&uri).await?
212 }
213 };
214 self.formatter.display(&result)
215 }
216
217 ResourceCommands::Templates { conn } => {
218 let client_type = create_client(&conn).await?;
219 let templates = match client_type {
220 ClientType::Stdio(client) => {
221 client.initialize().await?;
222 client.list_resource_templates().await?
223 }
224 ClientType::Tcp(client) => {
225 client.initialize().await?;
226 client.list_resource_templates().await?
227 }
228 ClientType::Unix(client) => {
229 client.initialize().await?;
230 client.list_resource_templates().await?
231 }
232 };
233 self.formatter.display(&templates)
234 }
235
236 ResourceCommands::Subscribe { conn, uri } => {
237 let client_type = create_client(&conn).await?;
238 match client_type {
239 ClientType::Stdio(client) => {
240 client.initialize().await?;
241 client.subscribe(&uri).await?;
242 }
243 ClientType::Tcp(client) => {
244 client.initialize().await?;
245 client.subscribe(&uri).await?;
246 }
247 ClientType::Unix(client) => {
248 client.initialize().await?;
249 client.subscribe(&uri).await?;
250 }
251 }
252 println!("✓ Subscribed to: {uri}");
253 Ok(())
254 }
255
256 ResourceCommands::Unsubscribe { conn, uri } => {
257 let client_type = create_client(&conn).await?;
258 match client_type {
259 ClientType::Stdio(client) => {
260 client.initialize().await?;
261 client.unsubscribe(&uri).await?;
262 }
263 ClientType::Tcp(client) => {
264 client.initialize().await?;
265 client.unsubscribe(&uri).await?;
266 }
267 ClientType::Unix(client) => {
268 client.initialize().await?;
269 client.unsubscribe(&uri).await?;
270 }
271 }
272 println!("✓ Unsubscribed from: {uri}");
273 Ok(())
274 }
275 }
276 }
277
278 async fn execute_prompt_command(&self, command: PromptCommands) -> CliResult<()> {
281 match command {
282 PromptCommands::List { conn } => {
283 let client_type = create_client(&conn).await?;
284 let prompts = match client_type {
285 ClientType::Stdio(client) => {
286 client.initialize().await?;
287 client.list_prompts().await?
288 }
289 ClientType::Tcp(client) => {
290 client.initialize().await?;
291 client.list_prompts().await?
292 }
293 ClientType::Unix(client) => {
294 client.initialize().await?;
295 client.list_prompts().await?
296 }
297 };
298 self.formatter.display_prompts(&prompts)
299 }
300
301 PromptCommands::Get {
302 conn,
303 name,
304 arguments,
305 } => {
306 let args: HashMap<String, serde_json::Value> =
308 if arguments.trim().is_empty() || arguments == "{}" {
309 HashMap::new()
310 } else {
311 serde_json::from_str(&arguments).map_err(|e| {
312 CliError::InvalidArguments(format!("Invalid JSON arguments: {}", e))
313 })?
314 };
315
316 let args_option = if args.is_empty() { None } else { Some(args) };
317
318 let client_type = create_client(&conn).await?;
319 let result = match client_type {
320 ClientType::Stdio(client) => {
321 client.initialize().await?;
322 client.get_prompt(&name, args_option).await?
323 }
324 ClientType::Tcp(client) => {
325 client.initialize().await?;
326 client.get_prompt(&name, args_option.clone()).await?
327 }
328 ClientType::Unix(client) => {
329 client.initialize().await?;
330 client.get_prompt(&name, args_option.clone()).await?
331 }
332 };
333 self.formatter.display(&result)
334 }
335
336 PromptCommands::Schema { conn, name } => {
337 let client_type = create_client(&conn).await?;
338 let prompts = match client_type {
339 ClientType::Stdio(client) => {
340 client.initialize().await?;
341 client.list_prompts().await?
342 }
343 ClientType::Tcp(client) => {
344 client.initialize().await?;
345 client.list_prompts().await?
346 }
347 ClientType::Unix(client) => {
348 client.initialize().await?;
349 client.list_prompts().await?
350 }
351 };
352
353 let prompt = prompts
354 .iter()
355 .find(|p| p.name == name)
356 .ok_or_else(|| CliError::Other(format!("Prompt '{}' not found", name)))?;
357
358 self.formatter.display(&prompt.arguments)
359 }
360 }
361 }
362
363 async fn execute_completion_command(&self, command: CompletionCommands) -> CliResult<()> {
366 match command {
367 CompletionCommands::Get {
368 conn,
369 ref_type,
370 ref_value,
371 argument,
372 } => {
373 let client_type = create_client(&conn).await?;
374
375 let result = match ref_type {
377 RefType::Prompt => {
378 let arg_name = argument.as_deref().unwrap_or("value");
379 match client_type {
380 ClientType::Stdio(client) => {
381 client.initialize().await?;
382 client
383 .complete_prompt(&ref_value, arg_name, "", None)
384 .await?
385 }
386 ClientType::Tcp(client) => {
387 client.initialize().await?;
388 client
389 .complete_prompt(&ref_value, arg_name, "", None)
390 .await?
391 }
392 ClientType::Unix(client) => {
393 client.initialize().await?;
394 client
395 .complete_prompt(&ref_value, arg_name, "", None)
396 .await?
397 }
398 }
399 }
400 RefType::Resource => {
401 let arg_name = argument.as_deref().unwrap_or("uri");
402 match client_type {
403 ClientType::Stdio(client) => {
404 client.initialize().await?;
405 client
406 .complete_resource(&ref_value, arg_name, "", None)
407 .await?
408 }
409 ClientType::Tcp(client) => {
410 client.initialize().await?;
411 client
412 .complete_resource(&ref_value, arg_name, "", None)
413 .await?
414 }
415 ClientType::Unix(client) => {
416 client.initialize().await?;
417 client
418 .complete_resource(&ref_value, arg_name, "", None)
419 .await?
420 }
421 }
422 }
423 };
424
425 self.formatter.display(&result)
426 }
427 }
428 }
429
430 async fn execute_server_command(&self, command: ServerCommands) -> CliResult<()> {
433 match command {
434 ServerCommands::Info { conn } => {
435 let client_type = create_client(&conn).await?;
436 let info = match client_type {
437 ClientType::Stdio(client) => {
438 let result = client.initialize().await?;
439 result.server_info
440 }
441 ClientType::Tcp(client) => {
442 let result = client.initialize().await?;
443 result.server_info
444 }
445 ClientType::Unix(client) => {
446 let result = client.initialize().await?;
447 result.server_info
448 }
449 };
450 self.formatter.display_server_info(&info)
451 }
452
453 ServerCommands::Ping { conn } => {
454 let client_type = create_client(&conn).await?;
455 let start = std::time::Instant::now();
456
457 match client_type {
458 ClientType::Stdio(client) => {
459 client.initialize().await?;
460 client.ping().await?;
461 }
462 ClientType::Tcp(client) => {
463 client.initialize().await?;
464 client.ping().await?;
465 }
466 ClientType::Unix(client) => {
467 client.initialize().await?;
468 client.ping().await?;
469 }
470 }
471
472 let elapsed = start.elapsed();
473 println!("✓ Pong! ({:.2}ms)", elapsed.as_secs_f64() * 1000.0);
474 Ok(())
475 }
476
477 ServerCommands::LogLevel { conn, level } => {
478 let protocol_level: turbomcp_protocol::types::LogLevel = level.clone().into();
480
481 let client_type = create_client(&conn).await?;
482 match client_type {
483 ClientType::Stdio(client) => {
484 client.initialize().await?;
485 client.set_log_level(protocol_level).await?;
486 }
487 ClientType::Tcp(client) => {
488 client.initialize().await?;
489 client.set_log_level(protocol_level).await?;
490 }
491 ClientType::Unix(client) => {
492 client.initialize().await?;
493 client.set_log_level(protocol_level).await?;
494 }
495 }
496 println!("✓ Log level set to: {:?}", level);
497 Ok(())
498 }
499
500 ServerCommands::Roots { conn } => {
501 let client_type = create_client(&conn).await?;
503 let result = match client_type {
504 ClientType::Stdio(client) => client.initialize().await?,
505 ClientType::Tcp(client) => client.initialize().await?,
506 ClientType::Unix(client) => client.initialize().await?,
507 };
508
509 self.formatter.display(&result.server_capabilities)
511 }
512 }
513 }
514
515 async fn execute_sampling_command(&self, _command: SamplingCommands) -> CliResult<()> {
518 Err(CliError::NotSupported(
519 "Sampling commands require LLM handler implementation".to_string(),
520 ))
521 }
522
523 async fn execute_connect(&self, conn: Connection) -> CliResult<()> {
526 println!("Connecting to server...");
527 let client_type = create_client(&conn).await?;
528
529 let info = match client_type {
530 ClientType::Stdio(client) => {
531 let result = client.initialize().await?;
532 result.server_info
533 }
534 ClientType::Tcp(client) => {
535 let result = client.initialize().await?;
536 result.server_info
537 }
538 ClientType::Unix(client) => {
539 let result = client.initialize().await?;
540 result.server_info
541 }
542 };
543
544 println!("✓ Connected successfully!");
545 self.formatter.display_server_info(&info)
546 }
547
548 async fn execute_status(&self, conn: Connection) -> CliResult<()> {
549 let client_type = create_client(&conn).await?;
550
551 let info = match client_type {
552 ClientType::Stdio(client) => {
553 let result = client.initialize().await?;
554 result.server_info
555 }
556 ClientType::Tcp(client) => {
557 let result = client.initialize().await?;
558 result.server_info
559 }
560 ClientType::Unix(client) => {
561 let result = client.initialize().await?;
562 result.server_info
563 }
564 };
565
566 println!("Status: Connected");
567 self.formatter.display_server_info(&info)
568 }
569}