Skip to main content
Glama

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
NameRequiredDescriptionDefault
scriptYes

Implementation Reference

  • 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(),
    ),
  • Pydantic model defining the input schema for the 'ida_execute_script' tool, with a single 'script' string parameter.
    class ExecuteScript(BaseModel):
        script: str
  • 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)}"
  • 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()
            }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MxIris-Reverse-Engineering/ida-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server