$npx -y skills add webflow/webflow-skills --skill webflow-compress-cms-imageCompress and convert CMS item image fields to webp or avif in a Webflow collection. Prompts for collection ID, item ID, image fields, quality, and target format, then downloads, converts, re-uploads via presigned S3, and publishes the updated item.
| 1 | # Webflow CMS Image Compression |
| 2 | |
| 3 | Compress and reformat image fields on a Webflow CMS item to `.webp` or `.avif`. |
| 4 | |
| 5 | This skill does not support images embedded inside Rich Text fields at this time. Tell the user that limitation before starting and only process CMS fields whose schema type is `Image` or `MultiImage`. |
| 6 | |
| 7 | ## Important Note |
| 8 | |
| 9 | **ALWAYS use Webflow MCP tools for Webflow operations:** |
| 10 | - Use Webflow MCP's `data_sites_tool` with action `list_sites` to discover the site ID when needed |
| 11 | - Use Webflow MCP's `data_cms_tool` with action `get_collection_details` to fetch collection schemas |
| 12 | - Use Webflow MCP's `data_cms_tool` with action `list_collection_items` to fetch the target CMS item |
| 13 | - Use Webflow MCP's `data_assets_tool` with action `create_asset` to create presigned asset uploads |
| 14 | - Use Webflow MCP's `data_cms_tool` with action `update_collection_items` to update CMS image fields |
| 15 | - Use Webflow MCP's `data_cms_tool` with action `publish_collection_items` to publish the updated item |
| 16 | - All Webflow MCP calls must include the required `context` parameter (15-25 words, third-person perspective) |
| 17 | - Mutating operations require explicit user confirmation. Ask the user to type `confirm` before uploading assets, updating CMS fields, or publishing. |
| 18 | - Rich Text embedded images are not supported. Do not parse or rewrite Rich Text HTML for image compression. |
| 19 | |
| 20 | ## Instructions |
| 21 | |
| 22 | ### Phase 1: Gather Parameters |
| 23 | |
| 24 | Collect all required inputs in one shot when possible: |
| 25 | |
| 26 | 1. **Collection ID**: Ask "What is the Collection ID?" |
| 27 | 2. **Item ID**: Ask "What is the Item ID?" |
| 28 | 3. **Image fields**: Ask "Which image fields should be compressed?" |
| 29 | - "All image fields" - compress every Image or MultiImage field found on the item |
| 30 | - "Specify field names" - user will name specific field slugs |
| 31 | 4. **Target format**: Ask "Target format?" |
| 32 | - "webp (Recommended)" - best browser support, good compression |
| 33 | - "avif" - better compression, slightly less support |
| 34 | 5. **Quality (1-100)**: Ask "Quality (1-100)?" |
| 35 | - "85 - Recommended (webp)" - visually lossless for most photos |
| 36 | - "75 - Recommended (avif)" - avif is efficient at lower quality |
| 37 | - "Custom" - user types their own value |
| 38 | |
| 39 | If the user chose "Specify field names", follow up for comma-separated field slugs. If the user chose "Custom" quality, follow up for the numeric value. Validate custom quality is an integer from 1 to 100. |
| 40 | |
| 41 | ### Phase 2: Discover Image Fields |
| 42 | |
| 43 | 1. Call `data_cms_tool` with action `get_collection_details` and the collection ID. |
| 44 | 2. Filter fields where `type === "Image"` or `type === "MultiImage"`. |
| 45 | 3. Select target fields: |
| 46 | - If the user chose "All image fields", use every Image and MultiImage field found. |
| 47 | - If the user named specific fields, validate each slug exists and is an Image or MultiImage field. |
| 48 | - Warn and skip requested fields that do not exist or are not image fields. |
| 49 | 4. Stop and report if no valid image fields remain. |
| 50 | |
| 51 | ### Phase 3: Fetch the CMS Item |
| 52 | |
| 53 | Call `data_cms_tool` with action `list_collection_items` to fetch the target item. Use the item ID to identify the item directly when the tool supports it; otherwise filter or search the returned items. |
| 54 | |
| 55 | Extract current `fieldData` for each target field: |
| 56 | - For Image fields, capture `url` and `fileId`. |
| 57 | - For MultiImage fields, capture each array entry's `url` and `fileId`. |
| 58 | - Skip null, empty, or malformed image values. |
| 59 | - Skip images already in the target format unless the user explicitly asks to recompress them. |
| 60 | |
| 61 | ### Phase 4: Preview and Confirm |
| 62 | |
| 63 | Before downloading or uploading anything, show a preview: |
| 64 | |
| 65 | ```markdown |
| 66 | Compression Preview |
| 67 | |
| 68 | Collection: [collection ID] |
| 69 | Item: [item ID] |
| 70 | Target format: webp |
| 71 | Quality: 85 |
| 72 | |
| 73 | Fields to process: |
| 74 | - main-image: hero.jpg -> hero.webp |
| 75 | - gallery: 3 images -> webp |
| 76 | |
| 77 | Skipped: |
| 78 | - thumbnail: already webp |
| 79 | |
| 80 | Type `confirm` to download, convert, upload, update the CMS item, and publish. |
| 81 | ``` |
| 82 | |
| 83 | Proceed only after the user types `confirm`. |
| 84 | |
| 85 | ### Phase 5: Convert Each Image |
| 86 | |
| 87 | For each image: |
| 88 | |
| 89 | 1. Download to `/tmp/`: |
| 90 | |
| 91 | ```bash |
| 92 | curl -sL "{url}" -o "/tmp/cms_img_{fieldSlug}_{index}.orig" |
| 93 | ``` |
| 94 | |
| 95 | 2. Ensure Pillow is available: |
| 96 | |
| 97 | ```bash |
| 98 | python3 -c "from PIL import Image" 2>/dev/null || pip3 install Pillow -q |
| 99 | ``` |
| 100 | |
| 101 | For avif, also try: |
| 102 | |
| 103 | ```bash |
| 104 | pip3 install pillow-avif-plugin -q |
| 105 | ``` |
| 106 | |
| 107 | 3. Convert with the user's quality setting: |
| 108 | |
| 109 | ```bash |
| 110 | python3 - <<'EOF' |
| 111 | from PIL import Image |
| 112 | import os |
| 113 | |
| 114 | try: |
| 115 | import pillow_avif |
| 116 | except ImportError: |
| 117 | pass |
| 118 | |
| 119 | source = "/tmp/cms_img_{fieldSlug}_{index}.orig" |
| 120 | target = "/tmp/cms_img_{fieldSlug}_{index}.{ext}" |
| 121 | img = Image.open(source) |
| 122 | img.save(target, "{FORMAT}", quality={quality}) |
| 123 | orig = os.path.getsize(source) |
| 124 | new = os.path.getsize(target) |
| 125 | print(f"Original: {orig:,} bytes |