ida_execute_script
Execute Python scripts within IDA Pro to automate analysis tasks using IDA's API modules directly from the MCP server interface.
Instructions
Execute a Python script in IDA Pro and return its output. The script runs in IDA's context with access to all IDA API modules.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| script | Yes |
Implementation Reference
- src/mcp_server_ida/server.py:1003-1007 (registration)Registration of the 'ida_execute_script' tool in the MCP server's list_tools() function, specifying name, description, and input schema.Tool( name=IDATools.EXECUTE_SCRIPT, description="Execute a Python script in IDA Pro and return its output. The script runs in IDA's context with access to all IDA API modules.", inputSchema=ExecuteScript.schema(), ),
- src/mcp_server_ida/server.py:81-82 (schema)Pydantic model defining the input schema for the 'ida_execute_script' tool, with a single 'script' string parameter.class ExecuteScript(BaseModel): script: str
- src/mcp_server_ida/server.py:732-778 (handler)Handler in IDAProFunctions class that sends the 'execute_script' socket request to the IDA Pro plugin and processes the response into tool output.def execute_script(self, script: str) -> str: """Execute a Python script in IDA Pro and return its output. The script runs in IDA's context with access to all IDA API modules.""" try: response: Dict[str, Any] = self.communicator.send_request( "execute_script", {"script": script} ) # Handle case where response is None if response is None: self.logger.error("Received None response from IDA when executing script") return "Error executing script: Received empty response from IDA" # Handle case where response contains error if "error" in response: return f"Error executing script: {response['error']}" # Handle successful execution success: bool = response.get("success", False) if not success: error_msg: str = response.get("error", "Unknown error") traceback: str = response.get("traceback", "") return f"Script execution failed: {error_msg}\n\nTraceback:\n{traceback}" # Get output - ensure all values are strings to avoid None errors stdout: str = str(response.get("stdout", "")) stderr: str = str(response.get("stderr", "")) return_value: str = str(response.get("return_value", "")) result_text: List[str] = [] result_text.append("Script executed successfully") if return_value and return_value != "None": result_text.append(f"\nReturn value:\n{return_value}") if stdout: result_text.append(f"\nStandard output:\n{stdout}") if stderr: result_text.append(f"\nStandard error:\n{stderr}") return "\n".join(result_text) except Exception as e: self.logger.error(f"Error executing script: {str(e)}", exc_info=True) return f"Error executing script: {str(e)}"
- src/mcp_server_ida/server.py:1176-1195 (handler)Main MCP tool call handler in call_tool() that validates arguments and invokes the IDAProFunctions.execute_script method.case IDATools.EXECUTE_SCRIPT: try: if "script" not in arguments or not arguments["script"]: return [TextContent( type="text", text="Error: No script content provided" )] result: str = ida_functions.execute_script(arguments["script"]) return [TextContent( type="text", text=result )] except Exception as e: logger.error(f"Error executing script: {str(e)}", exc_info=True) return [TextContent( type="text", text=f"Error executing script: {str(e)}" )]
- Core implementation in IDA Pro plugin that executes the provided Python script in IDA's context using exec(), capturing stdout/stderr/return value, with UI hooks and auto-handlers to avoid interactive dialogs.def _execute_script_internal(self, script: str) -> Dict[str, Any]: """Internal implementation for execute_script without sync wrapper""" try: print(f"Executing script, length: {len(script) if script else 0}") # Check for empty script if not script or not script.strip(): print("Error: Empty script provided") return { "success": False, "error": "Empty script provided", "stdout": "", "stderr": "", "traceback": "" } # Create a local namespace for script execution script_globals = { '__builtins__': __builtins__, 'idaapi': idaapi, 'idautils': idautils, 'idc': idc, 'ida_funcs': ida_funcs, 'ida_bytes': ida_bytes, 'ida_name': ida_name, 'ida_segment': ida_segment, 'ida_lines': ida_lines, 'ida_hexrays': ida_hexrays } script_locals = {} # Save original stdin/stdout/stderr import sys import io original_stdout = sys.stdout original_stderr = sys.stderr original_stdin = sys.stdin # Create string IO objects to capture output stdout_capture = io.StringIO() stderr_capture = io.StringIO() # Redirect stdout/stderr to capture output sys.stdout = stdout_capture sys.stderr = stderr_capture # Prevent script from trying to read from stdin sys.stdin = io.StringIO() try: # Create UI hooks print("Setting up UI hooks") hooks = self._create_ui_hooks() hooks.hook() # Install auto-continue handlers for common dialogs - but first, redirect stderr temp_stderr = sys.stderr auto_handler_stderr = io.StringIO() sys.stderr = auto_handler_stderr print("Installing auto handlers") self._install_auto_handlers() # Restore stderr and save auto-handler errors separately sys.stderr = stderr_capture auto_handler_errors = auto_handler_stderr.getvalue() # Only log auto-handler errors, don't include in script output if auto_handler_errors: print(f"Auto-handler setup errors (not shown to user): {auto_handler_errors}") # Execute the script print("Executing script...") exec(script, script_globals, script_locals) print("Script execution completed") # Get captured output stdout = stdout_capture.getvalue() stderr = stderr_capture.getvalue() # Filter out auto-handler messages from stdout stdout_lines = stdout.splitlines() filtered_stdout_lines = [] for line in stdout_lines: skip_line = False auto_handler_messages = [ "Setting up UI hooks", "Installing auto handlers", "Error installing auto handlers", "Found and saved", "Could not access user_cancelled", "Installed auto_", "Auto handlers installed", "Note: Could not", "Restoring IO streams", "Unhooking UI hooks", "Restoring original handlers", "Refreshing view", "Original handlers restored", "No original handlers" ] for msg in auto_handler_messages: if msg in line: skip_line = True break if not skip_line: filtered_stdout_lines.append(line) filtered_stdout = "\n".join(filtered_stdout_lines) # Compile script results - ensure all fields are present result = { "stdout": filtered_stdout.strip() if filtered_stdout else "", "stderr": stderr.strip() if stderr else "", "success": True, "traceback": "" } # Check for return value if "result" in script_locals: try: print(f"Script returned value of type: {type(script_locals['result']).__name__}") result["return_value"] = str(script_locals["result"]) except Exception as rv_err: print(f"Error converting return value: {str(rv_err)}") result["stderr"] += f"\nError converting return value: {str(rv_err)}" result["return_value"] = "Error: Could not convert return value to string" print(f"Returning script result with keys: {', '.join(result.keys())}") return result except Exception as e: import traceback error_msg = str(e) tb = traceback.format_exc() print(f"Script execution error: {error_msg}") print(tb) return { "success": False, "stdout": stdout_capture.getvalue().strip() if stdout_capture else "", "stderr": stderr_capture.getvalue().strip() if stderr_capture else "", "error": error_msg, "traceback": tb } finally: # Restore original stdin/stdout/stderr print("Restoring IO streams") sys.stdout = original_stdout sys.stderr = original_stderr sys.stdin = original_stdin # Unhook UI hooks print("Unhooking UI hooks") hooks.unhook() # Restore original handlers print("Restoring original handlers") self._restore_original_handlers() # Refresh view to show any changes made by script print("Refreshing view") self._refresh_view_internal() except Exception as e: print(f"Error in execute_script outer scope: {str(e)}") traceback.print_exc() return { "success": False, "stdout": "", "stderr": "", "error": str(e), "traceback": traceback.format_exc() }