list_resources
List and filter resources in Android projects using Apktool MCP Server. Specify resource types to locate specific files efficiently.
Instructions
List resources in a project, optionally filtered by resource type.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_dir | Yes | ||
| resource_type | No |
Implementation Reference
- apktool_mcp_server.py:797-931 (handler)The handler function implementing the list_resources tool. Lists resources from an APKTool decoded project directory, either all resource types with counts or files in a specific resource type directory. Supports pagination via offset and count parameters. Includes input validation and detailed error handling.async def list_resources( project_dir: str, resource_type: Optional[str] = None, offset: int = 0, count: int = 0 ) -> Dict: """ List resources with pagination support and enhanced metadata. Args: project_dir: Path to the APKTool project directory resource_type: Optional resource type to filter by (e.g., "layout", "drawable") offset: Starting offset for pagination count: Number of items to return (0 means use default) Returns: Paginated dictionary with list of resources and metadata """ # Input validation path_validation = ValidationUtils.validate_path(project_dir, must_exist=True) if not path_validation["valid"]: return {"success": False, "error": path_validation["error"]} res_path = os.path.join(project_dir, "res") if not os.path.exists(res_path): return { "success": False, "error": f"Resources directory not found: {res_path}" } try: if resource_type: # List resources of specific type type_path = os.path.join(res_path, resource_type) if not os.path.exists(type_path): # Get available resource types resource_types = [ d for d in os.listdir(res_path) if os.path.isdir(os.path.join(res_path, d)) ] return { "success": False, "error": f"Resource type directory not found: {resource_type}", "available_types": resource_types } resources = [] for item in os.listdir(type_path): item_path = os.path.join(type_path, item) if os.path.isfile(item_path): resources.append({ "name": item, "path": item_path, "size": os.path.getsize(item_path), "type": resource_type, "extension": os.path.splitext(item)[1], "modified_time": os.path.getmtime(item_path) }) # Sort by name resources.sort(key=lambda x: x["name"]) # Apply pagination paginated_result = PaginationUtils.handle_pagination( items=resources, offset=offset, count=count, data_type="resources", items_key="resources" ) paginated_result["success"] = True paginated_result["resource_type"] = resource_type paginated_result["resource_path"] = type_path return paginated_result else: # List all resource types with counts resource_types = [] for item in os.listdir(res_path): type_path = os.path.join(res_path, item) if os.path.isdir(type_path): try: files = [f for f in os.listdir(type_path) if os.path.isfile(os.path.join(type_path, f))] resource_count = len(files) # Calculate total size total_size = 0 for f in files: try: total_size += os.path.getsize(os.path.join(type_path, f)) except: pass resource_types.append({ "type": item, "path": type_path, "count": resource_count, "total_size": total_size }) except Exception as e: logger.warning(f"Error processing resource type {item}: {e}") resource_types.append({ "type": item, "path": type_path, "count": 0, "total_size": 0, "error": str(e) }) # Sort by type name resource_types.sort(key=lambda x: x["type"]) # Apply pagination paginated_result = PaginationUtils.handle_pagination( items=resource_types, offset=offset, count=count, data_type="resource-types", items_key="resource_types" ) paginated_result["success"] = True return paginated_result except Exception as e: logger.error(f"Error listing resources: {str(e)}") return { "success": False, "error": f"Failed to list resources: {str(e)}" }
- apktool_mcp_server.py:797-797 (registration)The @mcp.tool() decorator registers the list_resources function as an MCP tool.async def list_resources(
- apktool_mcp_server.py:50-142 (helper)PaginationUtils class providing handle_pagination method used by list_resources for paginated responses.class PaginationUtils: """Utility class for handling pagination across different MCP tools""" # Configuration constants DEFAULT_PAGE_SIZE = 100 MAX_PAGE_SIZE = 10000 MAX_OFFSET = 1000000 @staticmethod def validate_pagination_params(offset: int, count: int) -> tuple[int, int]: """Validate and normalize pagination parameters""" offset = max(0, min(offset, PaginationUtils.MAX_OFFSET)) count = max(0, min(count, PaginationUtils.MAX_PAGE_SIZE)) return offset, count @staticmethod def handle_pagination( items: List[Any], offset: int = 0, count: int = 0, data_type: str = "paginated-list", items_key: str = "items", item_transformer: Optional[Callable[[Any], Any]] = None ) -> Dict[str, Any]: """ Generic pagination handler for list data Args: items: List of items to paginate offset: Starting offset count: Number of items to return (0 means use default) data_type: Type identifier for the response items_key: Key name for items in response item_transformer: Optional function to transform items Returns: Paginated response dictionary """ if items is None: items = [] total_items = len(items) # Validate parameters offset, count = PaginationUtils.validate_pagination_params(offset, count) # Determine effective limit if count == 0: effective_limit = min(PaginationUtils.DEFAULT_PAGE_SIZE, max(0, total_items - offset)) else: effective_limit = min(count, max(0, total_items - offset)) # Calculate bounds start_index = min(offset, total_items) end_index = min(start_index + effective_limit, total_items) has_more = end_index < total_items # Extract and transform paginated subset paginated_items = items[start_index:end_index] if item_transformer: paginated_items = [item_transformer(item) for item in paginated_items] # Build response result = { "type": data_type, items_key: paginated_items, "pagination": { "total": total_items, "offset": offset, "limit": effective_limit, "count": len(paginated_items), "has_more": has_more } } # Add navigation helpers if has_more: result["pagination"]["next_offset"] = end_index if offset > 0: prev_offset = max(0, offset - effective_limit) result["pagination"]["prev_offset"] = prev_offset # Page calculations if effective_limit > 0: current_page = (offset // effective_limit) + 1 total_pages = (total_items + effective_limit - 1) // effective_limit result["pagination"]["current_page"] = current_page result["pagination"]["total_pages"] = total_pages result["pagination"]["page_size"] = effective_limit return result
- apktool_mcp_server.py:143-178 (helper)ValidationUtils class providing validate_path used at the start of list_resources for input validation.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}