1use crate::cli::*;
4use crate::error::{CliError, CliResult};
5use crate::formatter::Formatter;
6use crate::transport::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 = create_client(&conn).await?;
49 client.initialize().await?;
50 let tools = client.list_tools().await?;
51 self.formatter.display_tools(&tools)
52 }
53
54 ToolCommands::Call {
55 conn,
56 name,
57 arguments,
58 } => {
59 let args: HashMap<String, serde_json::Value> =
60 if arguments.trim().is_empty() || arguments == "{}" {
61 HashMap::new()
62 } else {
63 serde_json::from_str(&arguments).map_err(|e| {
64 CliError::InvalidArguments(format!("Invalid JSON arguments: {}", e))
65 })?
66 };
67
68 let client = create_client(&conn).await?;
69 client.initialize().await?;
70 let result = client.call_tool(&name, Some(args)).await?;
71 self.formatter.display(&result)
72 }
73
74 ToolCommands::Schema { conn, name } => {
75 let client = create_client(&conn).await?;
76 client.initialize().await?;
77 let tools = client.list_tools().await?;
78
79 if let Some(tool_name) = name {
80 let tool = tools.iter().find(|t| t.name == tool_name).ok_or_else(|| {
81 CliError::Other(format!("Tool '{}' not found", tool_name))
82 })?;
83
84 self.formatter.display(&tool.input_schema)
85 } else {
86 let schemas: Vec<_> = tools
87 .iter()
88 .map(|t| {
89 serde_json::json!({
90 "name": t.name,
91 "schema": t.input_schema
92 })
93 })
94 .collect();
95
96 self.formatter.display(&schemas)
97 }
98 }
99
100 ToolCommands::Export { conn, output } => {
101 let client = create_client(&conn).await?;
102 client.initialize().await?;
103 let tools = client.list_tools().await?;
104
105 std::fs::create_dir_all(&output)?;
106
107 for tool in tools {
108 let filename = format!("{}.json", tool.name);
109 let filepath = output.join(filename);
110 let schema = serde_json::to_string_pretty(&tool.input_schema)?;
111 std::fs::write(&filepath, schema)?;
112
113 if self.verbose {
114 println!("Exported: {}", filepath.display());
115 }
116 }
117
118 println!("✓ Exported schemas to: {}", output.display());
119 Ok(())
120 }
121 }
122 }
123
124 async fn execute_resource_command(&self, command: ResourceCommands) -> CliResult<()> {
127 match command {
128 ResourceCommands::List { conn } => {
129 let client = create_client(&conn).await?;
130 client.initialize().await?;
131 let resources = client.list_resources().await?;
132 self.formatter.display(&resources)
133 }
134
135 ResourceCommands::Read { conn, uri } => {
136 let client = create_client(&conn).await?;
137 client.initialize().await?;
138 let result = client.read_resource(&uri).await?;
139 self.formatter.display(&result)
140 }
141
142 ResourceCommands::Templates { conn } => {
143 let client = create_client(&conn).await?;
144 client.initialize().await?;
145 let templates = client.list_resource_templates().await?;
146 self.formatter.display(&templates)
147 }
148
149 ResourceCommands::Subscribe { conn, uri } => {
150 let client = create_client(&conn).await?;
151 client.initialize().await?;
152 client.subscribe(&uri).await?;
153 println!("✓ Subscribed to: {uri}");
154 Ok(())
155 }
156
157 ResourceCommands::Unsubscribe { conn, uri } => {
158 let client = create_client(&conn).await?;
159 client.initialize().await?;
160 client.unsubscribe(&uri).await?;
161 println!("✓ Unsubscribed from: {uri}");
162 Ok(())
163 }
164 }
165 }
166
167 async fn execute_prompt_command(&self, command: PromptCommands) -> CliResult<()> {
170 match command {
171 PromptCommands::List { conn } => {
172 let client = create_client(&conn).await?;
173 client.initialize().await?;
174 let prompts = client.list_prompts().await?;
175 self.formatter.display_prompts(&prompts)
176 }
177
178 PromptCommands::Get {
179 conn,
180 name,
181 arguments,
182 } => {
183 let args: HashMap<String, serde_json::Value> =
185 if arguments.trim().is_empty() || arguments == "{}" {
186 HashMap::new()
187 } else {
188 serde_json::from_str(&arguments).map_err(|e| {
189 CliError::InvalidArguments(format!("Invalid JSON arguments: {}", e))
190 })?
191 };
192
193 let args_option = if args.is_empty() { None } else { Some(args) };
194
195 let client = create_client(&conn).await?;
196 client.initialize().await?;
197 let result = client.get_prompt(&name, args_option).await?;
198 self.formatter.display(&result)
199 }
200
201 PromptCommands::Schema { conn, name } => {
202 let client = create_client(&conn).await?;
203 client.initialize().await?;
204 let prompts = client.list_prompts().await?;
205
206 let prompt = prompts
207 .iter()
208 .find(|p| p.name == name)
209 .ok_or_else(|| CliError::Other(format!("Prompt '{}' not found", name)))?;
210
211 self.formatter.display(&prompt.arguments)
212 }
213 }
214 }
215
216 async fn execute_completion_command(&self, command: CompletionCommands) -> CliResult<()> {
219 match command {
220 CompletionCommands::Get {
221 conn,
222 ref_type,
223 ref_value,
224 argument,
225 } => {
226 let client = create_client(&conn).await?;
227 client.initialize().await?;
228
229 let result = match ref_type {
231 RefType::Prompt => {
232 let arg_name = argument.as_deref().unwrap_or("value");
233 client
234 .complete_prompt(&ref_value, arg_name, "", None)
235 .await?
236 }
237 RefType::Resource => {
238 let arg_name = argument.as_deref().unwrap_or("uri");
239 client
240 .complete_resource(&ref_value, arg_name, "", None)
241 .await?
242 }
243 };
244
245 self.formatter.display(&result)
246 }
247 }
248 }
249
250 async fn execute_server_command(&self, command: ServerCommands) -> CliResult<()> {
253 match command {
254 ServerCommands::Info { conn } => {
255 let client = create_client(&conn).await?;
256 let result = client.initialize().await?;
257 self.formatter.display_server_info(&result.server_info)
258 }
259
260 ServerCommands::Ping { conn } => {
261 let client = create_client(&conn).await?;
262 let start = std::time::Instant::now();
263
264 client.initialize().await?;
265 client.ping().await?;
266
267 let elapsed = start.elapsed();
268 println!("✓ Pong! ({:.2}ms)", elapsed.as_secs_f64() * 1000.0);
269 Ok(())
270 }
271
272 ServerCommands::LogLevel { conn, level } => {
273 let protocol_level: turbomcp_protocol::types::LogLevel = level.clone().into();
275
276 let client = create_client(&conn).await?;
277 client.initialize().await?;
278 client.set_log_level(protocol_level).await?;
279 println!("✓ Log level set to: {:?}", level);
280 Ok(())
281 }
282
283 ServerCommands::Roots { conn } => {
284 let client = create_client(&conn).await?;
286 let result = client.initialize().await?;
287
288 self.formatter.display(&result.server_capabilities)
290 }
291 }
292 }
293
294 async fn execute_sampling_command(&self, _command: SamplingCommands) -> CliResult<()> {
297 Err(CliError::NotSupported(
298 "Sampling commands require LLM handler implementation".to_string(),
299 ))
300 }
301
302 async fn execute_connect(&self, conn: Connection) -> CliResult<()> {
305 println!("Connecting to server...");
306 let client = create_client(&conn).await?;
307
308 let result = client.initialize().await?;
309
310 println!("✓ Connected successfully!");
311 self.formatter.display_server_info(&result.server_info)
312 }
313
314 async fn execute_status(&self, conn: Connection) -> CliResult<()> {
315 let client = create_client(&conn).await?;
316
317 let result = client.initialize().await?;
318
319 println!("Status: Connected");
320 self.formatter.display_server_info(&result.server_info)
321 }
322}