Skip to main content
Glama

decode_apk

Decode APK files to access and analyze their contents using Apktool. Extract resources, source code, or customize decoding options for Android app reverse engineering.

Instructions

Decode an APK file using APKTool

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
apk_pathYes
forceNo
no_resNo
no_srcNo

Implementation Reference

  • The primary handler function for the 'decode_apk' MCP tool. It validates input APK path, constructs the apktool decode command with options, executes it using run_command, and enriches the result with project metadata checks.
    @mcp.tool()
    async def decode_apk(
        apk_path: str,
        force: bool = True,
        no_res: bool = False,
        no_src: bool = False,
        output_dir: Optional[str] = None,
        timeout: int = DEFAULT_TIMEOUT
    ) -> Dict:
        """
        Decode an APK file using APKTool with comprehensive validation and error handling.
        
        Args:
            apk_path: Path to the APK file to decode
            force: Force delete destination directory if it exists
            no_res: Do not decode resources
            no_src: Do not decode sources
            output_dir: Custom output directory (optional)
            timeout: Command timeout in seconds
            
        Returns:
            Dictionary with operation results including validation details
        """
        # Input validation
        path_validation = ValidationUtils.validate_path(apk_path, must_exist=True)
        if not path_validation["valid"]:
            return {"success": False, "error": path_validation["error"]}
        
        if not apk_path.lower().endswith(('.apk', '.xapk')):
            return {"success": False, "error": "File must have .apk or .xapk extension"}
        
        # Determine output directory
        if output_dir is None:
            apk_name = os.path.basename(apk_path).rsplit('.', 1)[0]
            output_dir = os.path.join(WORKSPACE_DIR, apk_name)
        
        # Build command
        command = ["apktool", "d", apk_path, "-o", output_dir]
        if force:
            command.append("-f")
        if no_res:
            command.append("-r")
        if no_src:
            command.append("-s")
        
        result = run_command(command, timeout=timeout)
        
        if result["success"]:
            # Additional validation - check if output directory was created
            if os.path.exists(output_dir):
                result["output_dir"] = output_dir
                result["workspace"] = WORKSPACE_DIR
                
                # Get basic project info
                manifest_path = os.path.join(output_dir, "AndroidManifest.xml")
                apktool_yml_path = os.path.join(output_dir, "apktool.yml")
                
                result["has_manifest"] = os.path.exists(manifest_path)
                result["has_apktool_yml"] = os.path.exists(apktool_yml_path)
            else:
                result["warning"] = "Decode reported success but output directory not found"
        
        return result
  • The @mcp.tool() decorator registers the decode_apk function as an MCP tool in FastMCP framework.
    @mcp.tool()
  • Helper function used by decode_apk to execute the apktool decode command with timeout, error handling, and logging.
    def run_command(command: List[str], timeout: int = DEFAULT_TIMEOUT, cwd: Optional[str] = None) -> Dict[str, Union[str, int, bool]]:
        """Enhanced command runner with comprehensive error handling"""
        try:
            logger.info(f"Running command: {' '.join(command)}")
            
            # Input validation
            if not command or not all(isinstance(arg, str) for arg in command):
                return {
                    "success": False,
                    "error": "Invalid command format"
                }
            
            result = subprocess.run(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                check=True,
                timeout=timeout,
                cwd=cwd
            )
            
            logger.info(f"Command completed successfully with return code {result.returncode}")
            return {
                "success": True,
                "stdout": result.stdout,
                "stderr": result.stderr,
                "returncode": result.returncode,
                "command": " ".join(command)
            }
            
        except subprocess.CalledProcessError as e:
            logger.error(f"Command failed with return code {e.returncode}: {e.stderr}")
            return {
                "success": False,
                "stdout": e.stdout or "",
                "stderr": e.stderr or "",
                "returncode": e.returncode,
                "error": f"Command failed with return code {e.returncode}",
                "command": " ".join(command)
            }
            
        except subprocess.TimeoutExpired as e:
            logger.error(f"Command timed out after {timeout} seconds")
            return {
                "success": False,
                "error": f"Command timed out after {timeout} seconds",
                "command": " ".join(command)
            }
            
        except FileNotFoundError:
            return {
                "success": False,
                "error": "APKTool not found. Please ensure APKTool is installed and in PATH"
            }
            
        except Exception as e:
            logger.error(f"Unexpected error running command: {str(e)}")
            return {
                "success": False,
                "error": f"Unexpected error: {str(e)}",
                "command": " ".join(command)
            }
  • Utility class providing validate_path used for input validation in decode_apk.
    class ValidationUtils:
        """Utility class for input validation"""
        
        @staticmethod
        def validate_path(path: str, must_exist: bool = True) -> Dict[str, Union[bool, str]]:
            """Validate file/directory path"""
            if not path or not isinstance(path, str):
                return {"valid": False, "error": "Path cannot be empty"}
                
            if must_exist and not os.path.exists(path):
                return {"valid": False, "error": f"Path does not exist: {path}"}
                
            return {"valid": True}
        
        @staticmethod
        def validate_class_name(class_name: str) -> Dict[str, Union[bool, str]]:
            """Validate Java class name format"""
            if not class_name or not isinstance(class_name, str):
                return {"valid": False, "error": "Class name cannot be empty"}
                
            if not class_name.replace('.', '').replace('_', '').replace('$', '').replace('/', '').isalnum():
                return {"valid": False, "error": "Invalid class name format"}
                
            return {"valid": True}
         
        @staticmethod
        def validate_search_pattern(pattern: str) -> Dict[str, Union[bool, str]]:
            """Validate search pattern"""
            if not pattern or not isinstance(pattern, str):
                return {"valid": False, "error": "Search pattern cannot be empty"}
                
            if len(pattern) > 1000:
                return {"valid": False, "error": "Search pattern too long (max 1000 characters)"}
                
            return {"valid": True}
Install Server

Other Tools

Related Tools

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/zinja-coder/apktool-mcp-server'

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