Skip to main content
Glama

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
NameRequiredDescriptionDefault
project_dirYes
resource_typeNo

Implementation Reference

  • 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)}"
            }
  • The @mcp.tool() decorator registers the list_resources function as an MCP tool.
    async def list_resources(
  • 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
  • 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}
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