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}

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