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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

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}

Tool Definition Quality

Score is being calculated. Check back soon.

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