Skip to main content

tauri_mcp/
server.rs

1use crate::{Result, TauriMcpError};
2use crate::tools::{
3    process::ProcessManager,
4    window::WindowManager,
5    input::InputSimulator,
6    debug::DebugTools,
7    ipc::IpcManager,
8};
9use jsonrpc_core::{IoHandler, Params, Value, Error as RpcError};
10use serde::{Deserialize, Serialize};
11use serde_json::json;
12use std::path::PathBuf;
13use std::sync::Arc;
14use tokio::sync::RwLock;
15use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
16use tracing::{debug, error, info};
17
18pub struct TauriMcpServer {
19    process_manager: Arc<RwLock<ProcessManager>>,
20    window_manager: Arc<WindowManager>,
21    input_simulator: Arc<InputSimulator>,
22    debug_tools: Arc<DebugTools>,
23    ipc_manager: Arc<IpcManager>,
24    config: ServerConfig,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ServerConfig {
29    pub auto_discover: bool,
30    pub session_management: bool,
31    pub event_streaming: bool,
32    pub performance_profiling: bool,
33    pub network_interception: bool,
34}
35
36impl Default for ServerConfig {
37    fn default() -> Self {
38        Self {
39            auto_discover: true,
40            session_management: true,
41            event_streaming: false,
42            performance_profiling: false,
43            network_interception: false,
44        }
45    }
46}
47
48
49impl TauriMcpServer {
50    pub async fn new(config_path: PathBuf) -> Result<Self> {
51        let config = if config_path.exists() {
52            let config_str = tokio::fs::read_to_string(&config_path).await?;
53            toml::from_str(&config_str).map_err(|e| TauriMcpError::ConfigError(e.to_string()))?
54        } else {
55            ServerConfig::default()
56        };
57        
58        debug!("Initializing Tauri MCP server with config: {:?}", config);
59        
60        Ok(Self {
61            process_manager: Arc::new(RwLock::new(ProcessManager::new())),
62            window_manager: Arc::new(WindowManager::new()),
63            input_simulator: Arc::new(InputSimulator::new()),
64            debug_tools: Arc::new(DebugTools::new()),
65            ipc_manager: Arc::new(IpcManager::new()),
66            config,
67        })
68    }
69    
70    pub async fn serve(&self, host: &str, port: u16) -> Result<()> {
71        debug!("Starting MCP server on {}:{}", host, port);
72        
73        let mut io = IoHandler::new();
74        
75        let server = McpServerImpl {
76            process_manager: Arc::clone(&self.process_manager),
77            window_manager: Arc::clone(&self.window_manager),
78            input_simulator: Arc::clone(&self.input_simulator),
79            debug_tools: Arc::clone(&self.debug_tools),
80            ipc_manager: Arc::clone(&self.ipc_manager),
81        };
82        
83        // Register all methods manually to handle MCP's named parameters
84        let server_clone = server.clone();
85        io.add_method("initialize", move |params: Params| {
86            let server = server_clone.clone();
87            async move {
88                match params {
89                    Params::Map(mut map) => {
90                        let protocol_version = map.remove("protocolVersion")
91                            .and_then(|v| v.as_str().map(String::from))
92                            .unwrap_or_else(|| "1.0".to_string());
93                        
94                        let capabilities = map.remove("capabilities").unwrap_or(Value::Null);
95                        
96                        server.initialize(protocol_version, capabilities)
97                    }
98                    _ => Err(RpcError::invalid_params("Expected object parameters"))
99                }
100            }
101        });
102        
103        let server_clone = server.clone();
104        io.add_method("shutdown", move |_params: Params| {
105            let server = server_clone.clone();
106            async move { server.shutdown() }
107        });
108        
109        let server_clone = server.clone();
110        io.add_method("tools/list", move |_params: Params| {
111            let server = server_clone.clone();
112            async move { server.list_tools() }
113        });
114        
115        let server_clone = server.clone();
116        io.add_method("tools/call", move |params: Params| {
117            let server = server_clone.clone();
118            async move {
119                match params {
120                    Params::Map(map) => server.call_tool(Value::Object(map)),
121                    _ => Err(RpcError::invalid_params("Expected object parameters"))
122                }
123            }
124        });
125        
126        // Register all other tool methods
127        let tool_methods = vec![
128            ("launch_app", "app_path", "args"),
129            ("stop_app", "process_id", ""),
130            ("get_app_logs", "process_id", "lines"),
131            ("take_screenshot", "process_id", "output_path"),
132            ("get_window_info", "process_id", ""),
133            ("send_keyboard_input", "process_id", "keys"),
134            ("send_mouse_click", "process_id", "x,y,button"),
135            ("execute_js", "process_id", "javascript_code"),
136            ("get_devtools_info", "process_id", ""),
137            ("monitor_resources", "process_id", ""),
138            ("list_ipc_handlers", "process_id", ""),
139            ("call_ipc_command", "process_id", "command_name,args"),
140        ];
141        
142        for (method_name, _, _) in tool_methods {
143            let server_clone = server.clone();
144            io.add_method(method_name, move |params: Params| {
145                let server = server_clone.clone();
146                let method_name = method_name.to_string();
147                async move {
148                    match params {
149                        Params::Map(map) => {
150                            server.call_tool(json!({
151                                "name": method_name,
152                                "arguments": Value::Object(map)
153                            }))
154                        }
155                        _ => Err(RpcError::invalid_params("Expected object parameters"))
156                    }
157                }
158            });
159        }
160        
161        let stdin = tokio::io::stdin();
162        let stdout = tokio::io::stdout();
163        let mut reader = BufReader::new(stdin);
164        let mut stdout = stdout;
165        
166        // MCP server ready, waiting for JSON-RPC requests on stdin
167        
168        loop {
169            let mut line = String::new();
170            match reader.read_line(&mut line).await {
171                Ok(0) => {
172                    info!("EOF reached, shutting down");
173                    break;
174                }
175                Ok(_) => {
176                    let line = line.trim();
177                    if line.is_empty() {
178                        continue;
179                    }
180                    
181                    debug!("Received request: {}", line);
182                    
183                    match io.handle_request(&line).await {
184                        Some(response) => {
185                            debug!("Sending response: {}", response);
186                            stdout.write_all(response.as_bytes()).await?;
187                            stdout.write_all(b"\n").await?;
188                            stdout.flush().await?;
189                        }
190                        None => {
191                            error!("No response generated for request: {}", line);
192                        }
193                    }
194                }
195                Err(e) => {
196                    error!("Error reading from stdin: {}", e);
197                    break;
198                }
199            }
200        }
201        
202        Ok(())
203    }
204}
205
206#[derive(Clone)]
207struct McpServerImpl {
208    process_manager: Arc<RwLock<ProcessManager>>,
209    window_manager: Arc<WindowManager>,
210    input_simulator: Arc<InputSimulator>,
211    debug_tools: Arc<DebugTools>,
212    ipc_manager: Arc<IpcManager>,
213}
214
215impl McpServerImpl {
216    fn initialize(&self, protocol_version: String, capabilities: Value) -> jsonrpc_core::Result<Value> {
217        
218        // Support both MCP protocol versions
219        if protocol_version != "1.0" && protocol_version != "2024-11-05" {
220            return Err(RpcError::invalid_params(format!("Unsupported protocol version: {}", protocol_version)));
221        }
222        
223        // Extract client capabilities if provided
224        let _client_capabilities = capabilities;
225        
226        Ok(json!({
227            "protocolVersion": protocol_version,
228            "serverInfo": {
229                "name": "tauri-mcp",
230                "version": env!("CARGO_PKG_VERSION"),
231                "description": "MCP server for testing and interacting with Tauri v2 applications"
232            },
233            "capabilities": {
234                "tools": {
235                    "listTools": true
236                },
237                "resources": null,
238                "prompts": null,
239                "logging": null
240            }
241        }))
242    }
243    
244    fn shutdown(&self) -> jsonrpc_core::Result<Value> {
245        // Cleanup would happen here
246        Ok(json!({
247            "status": "shutdown"
248        }))
249    }
250    
251    fn launch_app(&self, app_path: String, args: Option<Vec<String>>) -> jsonrpc_core::Result<Value> {
252        let process_manager = Arc::clone(&self.process_manager);
253        let args = args.unwrap_or_default();
254        
255        let runtime = tokio::runtime::Handle::current();
256        let result = runtime.block_on(async {
257            let mut manager = process_manager.write().await;
258            manager.launch_app(&app_path, args).await
259        });
260        
261        match result {
262            Ok(process_id) => Ok(json!({
263                "process_id": process_id,
264                "status": "launched"
265            })),
266            Err(e) => Err(RpcError::invalid_params(e.to_string())),
267        }
268    }
269    
270    fn stop_app(&self, process_id: String) -> jsonrpc_core::Result<Value> {
271        let process_manager = Arc::clone(&self.process_manager);
272        
273        let runtime = tokio::runtime::Handle::current();
274        let result = runtime.block_on(async {
275            let mut manager = process_manager.write().await;
276            manager.stop_app(&process_id).await
277        });
278        
279        match result {
280            Ok(()) => Ok(json!({
281                "status": "stopped"
282            })),
283            Err(e) => Err(RpcError::invalid_params(e.to_string())),
284        }
285    }
286    
287    fn get_app_logs(&self, process_id: String, lines: Option<usize>) -> jsonrpc_core::Result<Value> {
288        let process_manager = Arc::clone(&self.process_manager);
289        
290        let runtime = tokio::runtime::Handle::current();
291        let result = runtime.block_on(async {
292            let manager = process_manager.read().await;
293            manager.get_app_logs(&process_id, lines).await
294        });
295        
296        match result {
297            Ok(logs) => Ok(json!({
298                "logs": logs
299            })),
300            Err(e) => Err(RpcError::invalid_params(e.to_string())),
301        }
302    }
303    
304    fn take_screenshot(&self, process_id: String, output_path: Option<String>) -> jsonrpc_core::Result<Value> {
305        let window_manager = Arc::clone(&self.window_manager);
306        let output_path = output_path.map(PathBuf::from);
307        
308        let runtime = tokio::runtime::Handle::current();
309        let result = runtime.block_on(async {
310            window_manager.take_screenshot(&process_id, output_path).await
311        });
312        
313        match result {
314            Ok(screenshot_data) => Ok(json!({
315                "screenshot": screenshot_data
316            })),
317            Err(e) => Err(RpcError::invalid_params(e.to_string())),
318        }
319    }
320    
321    fn get_window_info(&self, process_id: String) -> jsonrpc_core::Result<Value> {
322        let window_manager = Arc::clone(&self.window_manager);
323        
324        let runtime = tokio::runtime::Handle::current();
325        let result = runtime.block_on(async {
326            window_manager.get_window_info(&process_id).await
327        });
328        
329        match result {
330            Ok(info) => Ok(info),
331            Err(e) => Err(RpcError::invalid_params(e.to_string())),
332        }
333    }
334    
335    fn send_keyboard_input(&self, process_id: String, keys: String) -> jsonrpc_core::Result<Value> {
336        let input_simulator = Arc::clone(&self.input_simulator);
337        
338        let runtime = tokio::runtime::Handle::current();
339        let result = runtime.block_on(async {
340            input_simulator.send_keyboard_input(&process_id, &keys).await
341        });
342        
343        match result {
344            Ok(()) => Ok(json!({
345                "status": "sent"
346            })),
347            Err(e) => Err(RpcError::invalid_params(e.to_string())),
348        }
349    }
350    
351    fn send_mouse_click(&self, process_id: String, x: i32, y: i32, button: Option<String>) -> jsonrpc_core::Result<Value> {
352        let input_simulator = Arc::clone(&self.input_simulator);
353        let button = button.unwrap_or_else(|| "left".to_string());
354        
355        let runtime = tokio::runtime::Handle::current();
356        let result = runtime.block_on(async {
357            input_simulator.send_mouse_click(&process_id, x, y, &button).await
358        });
359        
360        match result {
361            Ok(()) => Ok(json!({
362                "status": "clicked"
363            })),
364            Err(e) => Err(RpcError::invalid_params(e.to_string())),
365        }
366    }
367    
368    fn execute_js(&self, process_id: String, javascript_code: String) -> jsonrpc_core::Result<Value> {
369        let debug_tools = Arc::clone(&self.debug_tools);
370        
371        let runtime = tokio::runtime::Handle::current();
372        let result = runtime.block_on(async {
373            debug_tools.execute_js(&process_id, &javascript_code).await
374        });
375        
376        match result {
377            Ok(result) => Ok(json!({
378                "result": result
379            })),
380            Err(e) => Err(RpcError::invalid_params(e.to_string())),
381        }
382    }
383    
384    fn get_devtools_info(&self, process_id: String) -> jsonrpc_core::Result<Value> {
385        let debug_tools = Arc::clone(&self.debug_tools);
386        
387        let runtime = tokio::runtime::Handle::current();
388        let result = runtime.block_on(async {
389            debug_tools.get_devtools_info(&process_id).await
390        });
391        
392        match result {
393            Ok(info) => Ok(info),
394            Err(e) => Err(RpcError::invalid_params(e.to_string())),
395        }
396    }
397    
398    fn monitor_resources(&self, process_id: String) -> jsonrpc_core::Result<Value> {
399        let process_manager = Arc::clone(&self.process_manager);
400        
401        let runtime = tokio::runtime::Handle::current();
402        let result = runtime.block_on(async {
403            let manager = process_manager.read().await;
404            manager.monitor_resources(&process_id).await
405        });
406        
407        match result {
408            Ok(resources) => Ok(resources),
409            Err(e) => Err(RpcError::invalid_params(e.to_string())),
410        }
411    }
412    
413    fn list_ipc_handlers(&self, process_id: String) -> jsonrpc_core::Result<Value> {
414        let ipc_manager = Arc::clone(&self.ipc_manager);
415        
416        let runtime = tokio::runtime::Handle::current();
417        let result = runtime.block_on(async {
418            ipc_manager.list_ipc_handlers(&process_id).await
419        });
420        
421        match result {
422            Ok(handlers) => Ok(json!({
423                "handlers": handlers
424            })),
425            Err(e) => Err(RpcError::invalid_params(e.to_string())),
426        }
427    }
428    
429    fn call_ipc_command(&self, process_id: String, command_name: String, args: Option<Value>) -> jsonrpc_core::Result<Value> {
430        let ipc_manager = Arc::clone(&self.ipc_manager);
431        let args = args.unwrap_or(Value::Null);
432        
433        let runtime = tokio::runtime::Handle::current();
434        let result = runtime.block_on(async {
435            ipc_manager.call_ipc_command(&process_id, &command_name, args).await
436        });
437        
438        match result {
439            Ok(result) => Ok(result),
440            Err(e) => Err(RpcError::invalid_params(e.to_string())),
441        }
442    }
443    
444    fn list_tools(&self) -> jsonrpc_core::Result<Value> {
445        Ok(json!({
446            "tools": [
447                {
448                    "name": "launch_app",
449                    "description": "Launch a Tauri application",
450                    "inputSchema": {
451                        "type": "object",
452                        "properties": {
453                            "app_path": { "type": "string", "description": "Path to the Tauri application" },
454                            "args": { "type": "array", "items": { "type": "string" }, "description": "Optional launch arguments" }
455                        },
456                        "required": ["app_path"]
457                    }
458                },
459                {
460                    "name": "stop_app",
461                    "description": "Stop a running Tauri application",
462                    "inputSchema": {
463                        "type": "object",
464                        "properties": {
465                            "process_id": { "type": "string", "description": "Process ID of the app to stop" }
466                        },
467                        "required": ["process_id"]
468                    }
469                },
470                {
471                    "name": "get_app_logs",
472                    "description": "Get stdout/stderr logs from a running app",
473                    "inputSchema": {
474                        "type": "object",
475                        "properties": {
476                            "process_id": { "type": "string", "description": "Process ID of the app" },
477                            "lines": { "type": "number", "description": "Number of recent lines to return" }
478                        },
479                        "required": ["process_id"]
480                    }
481                },
482                {
483                    "name": "take_screenshot",
484                    "description": "Take a screenshot of the app window",
485                    "inputSchema": {
486                        "type": "object",
487                        "properties": {
488                            "process_id": { "type": "string", "description": "Process ID of the app" },
489                            "output_path": { "type": "string", "description": "Optional path to save the screenshot" }
490                        },
491                        "required": ["process_id"]
492                    }
493                },
494                {
495                    "name": "get_window_info",
496                    "description": "Get window dimensions, position, and state",
497                    "inputSchema": {
498                        "type": "object",
499                        "properties": {
500                            "process_id": { "type": "string", "description": "Process ID of the app" }
501                        },
502                        "required": ["process_id"]
503                    }
504                },
505                {
506                    "name": "send_keyboard_input",
507                    "description": "Send keyboard input to the app",
508                    "inputSchema": {
509                        "type": "object",
510                        "properties": {
511                            "process_id": { "type": "string", "description": "Process ID of the app" },
512                            "keys": { "type": "string", "description": "Keys to send" }
513                        },
514                        "required": ["process_id", "keys"]
515                    }
516                },
517                {
518                    "name": "send_mouse_click",
519                    "description": "Send mouse click to specific coordinates",
520                    "inputSchema": {
521                        "type": "object",
522                        "properties": {
523                            "process_id": { "type": "string", "description": "Process ID of the app" },
524                            "x": { "type": "number", "description": "X coordinate" },
525                            "y": { "type": "number", "description": "Y coordinate" },
526                            "button": { "type": "string", "enum": ["left", "right", "middle"], "description": "Mouse button" }
527                        },
528                        "required": ["process_id", "x", "y"]
529                    }
530                },
531                {
532                    "name": "execute_js",
533                    "description": "Execute JavaScript in the app's webview",
534                    "inputSchema": {
535                        "type": "object",
536                        "properties": {
537                            "process_id": { "type": "string", "description": "Process ID of the app" },
538                            "javascript_code": { "type": "string", "description": "JavaScript code to execute" }
539                        },
540                        "required": ["process_id", "javascript_code"]
541                    }
542                },
543                {
544                    "name": "get_devtools_info",
545                    "description": "Get DevTools connection information",
546                    "inputSchema": {
547                        "type": "object",
548                        "properties": {
549                            "process_id": { "type": "string", "description": "Process ID of the app" }
550                        },
551                        "required": ["process_id"]
552                    }
553                },
554                {
555                    "name": "monitor_resources",
556                    "description": "Monitor CPU, memory, and other resource usage",
557                    "inputSchema": {
558                        "type": "object",
559                        "properties": {
560                            "process_id": { "type": "string", "description": "Process ID of the app" }
561                        },
562                        "required": ["process_id"]
563                    }
564                },
565                {
566                    "name": "list_ipc_handlers",
567                    "description": "List all registered Tauri IPC commands",
568                    "inputSchema": {
569                        "type": "object",
570                        "properties": {
571                            "process_id": { "type": "string", "description": "Process ID of the app" }
572                        },
573                        "required": ["process_id"]
574                    }
575                },
576                {
577                    "name": "call_ipc_command",
578                    "description": "Call a Tauri IPC command",
579                    "inputSchema": {
580                        "type": "object",
581                        "properties": {
582                            "process_id": { "type": "string", "description": "Process ID of the app" },
583                            "command_name": { "type": "string", "description": "Name of the IPC command" },
584                            "args": { "type": "object", "description": "Arguments to pass to the command" }
585                        },
586                        "required": ["process_id", "command_name"]
587                    }
588                }
589            ]
590        }))
591    }
592    
593    fn call_tool(&self, params: Value) -> jsonrpc_core::Result<Value> {
594        let tool_name = params.get("name")
595            .and_then(|v| v.as_str())
596            .ok_or_else(|| RpcError::invalid_params("Missing tool name"))?;
597        
598        let arguments = params.get("arguments")
599            .cloned()
600            .unwrap_or(json!({}));
601        
602        match tool_name {
603            "launch_app" => {
604                let app_path = arguments.get("app_path")
605                    .and_then(|v| v.as_str())
606                    .ok_or_else(|| RpcError::invalid_params("Missing app_path"))?
607                    .to_string();
608                
609                let args = arguments.get("args")
610                    .and_then(|v| v.as_array())
611                    .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect());
612                
613                self.launch_app(app_path, args)
614            },
615            "stop_app" => {
616                let process_id = arguments.get("process_id")
617                    .and_then(|v| v.as_str())
618                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
619                    .to_string();
620                
621                self.stop_app(process_id)
622            },
623            "get_app_logs" => {
624                let process_id = arguments.get("process_id")
625                    .and_then(|v| v.as_str())
626                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
627                    .to_string();
628                
629                let lines = arguments.get("lines")
630                    .and_then(|v| v.as_u64())
631                    .map(|n| n as usize);
632                
633                self.get_app_logs(process_id, lines)
634            },
635            "take_screenshot" => {
636                let process_id = arguments.get("process_id")
637                    .and_then(|v| v.as_str())
638                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
639                    .to_string();
640                
641                let output_path = arguments.get("output_path")
642                    .and_then(|v| v.as_str())
643                    .map(String::from);
644                
645                self.take_screenshot(process_id, output_path)
646            },
647            "get_window_info" => {
648                let process_id = arguments.get("process_id")
649                    .and_then(|v| v.as_str())
650                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
651                    .to_string();
652                
653                self.get_window_info(process_id)
654            },
655            "send_keyboard_input" => {
656                let process_id = arguments.get("process_id")
657                    .and_then(|v| v.as_str())
658                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
659                    .to_string();
660                
661                let keys = arguments.get("keys")
662                    .and_then(|v| v.as_str())
663                    .ok_or_else(|| RpcError::invalid_params("Missing keys"))?
664                    .to_string();
665                
666                self.send_keyboard_input(process_id, keys)
667            },
668            "send_mouse_click" => {
669                let process_id = arguments.get("process_id")
670                    .and_then(|v| v.as_str())
671                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
672                    .to_string();
673                
674                let x = arguments.get("x")
675                    .and_then(|v| v.as_i64())
676                    .ok_or_else(|| RpcError::invalid_params("Missing x coordinate"))? as i32;
677                
678                let y = arguments.get("y")
679                    .and_then(|v| v.as_i64())
680                    .ok_or_else(|| RpcError::invalid_params("Missing y coordinate"))? as i32;
681                
682                let button = arguments.get("button")
683                    .and_then(|v| v.as_str())
684                    .map(String::from);
685                
686                self.send_mouse_click(process_id, x, y, button)
687            },
688            "execute_js" => {
689                let process_id = arguments.get("process_id")
690                    .and_then(|v| v.as_str())
691                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
692                    .to_string();
693                
694                let javascript_code = arguments.get("javascript_code")
695                    .and_then(|v| v.as_str())
696                    .ok_or_else(|| RpcError::invalid_params("Missing javascript_code"))?
697                    .to_string();
698                
699                self.execute_js(process_id, javascript_code)
700            },
701            "get_devtools_info" => {
702                let process_id = arguments.get("process_id")
703                    .and_then(|v| v.as_str())
704                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
705                    .to_string();
706                
707                self.get_devtools_info(process_id)
708            },
709            "monitor_resources" => {
710                let process_id = arguments.get("process_id")
711                    .and_then(|v| v.as_str())
712                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
713                    .to_string();
714                
715                self.monitor_resources(process_id)
716            },
717            "list_ipc_handlers" => {
718                let process_id = arguments.get("process_id")
719                    .and_then(|v| v.as_str())
720                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
721                    .to_string();
722                
723                self.list_ipc_handlers(process_id)
724            },
725            "call_ipc_command" => {
726                let process_id = arguments.get("process_id")
727                    .and_then(|v| v.as_str())
728                    .ok_or_else(|| RpcError::invalid_params("Missing process_id"))?
729                    .to_string();
730                
731                let command_name = arguments.get("command_name")
732                    .and_then(|v| v.as_str())
733                    .ok_or_else(|| RpcError::invalid_params("Missing command_name"))?
734                    .to_string();
735                
736                let args = arguments.get("args").cloned();
737                
738                self.call_ipc_command(process_id, command_name, args)
739            },
740            _ => Err(RpcError::method_not_found())
741        }
742    }
743}