plugins/aem/edge-delivery-services-content-ops/skills/bulk-metadata/SKILL.md
Audit and update metadata across multiple AEM Edge Delivery Services pages. Scans pages via the query index, identifies missing or inconsistent metadata (titles, descriptions, og tags, robots), and generates a corrected bulk metadata spreadsheet. Use when standardizing metadata across a site, preparing for launch, or fixing SEO issues at scale.
npx skillsauth add adobe/skills bulk-metadataInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Audit metadata across an entire AEM Edge Delivery Services site using the query index, identify gaps and inconsistencies, and produce a corrected bulk metadata spreadsheet ready to paste into Google Sheets or Excel.
This skill fetches external web pages and JSON endpoints for analysis. When fetching:
EDS metadata is managed at three levels, with a clear precedence order:
<meta> tags in the page <head>. Page-level always wins.metadata.xlsx (or metadata Google Sheet) placed in a subdirectory. Applies to all pages in that folder and below.metadata.xlsx (or metadata Google Sheet) in the site root. Uses URL pattern matching to set defaults across the entire site.Precedence: page > folder > bulk. Bulk metadata sets defaults; page-level metadata always overrides.
The bulk metadata spreadsheet uses URL patterns in the first column:
/** — matches all pages site-wide (deepest wildcard)/blog/** — matches all pages under /blog/ at any depth/blog/* — matches only direct children of /blog/ (one level)/about — matches a single specific pageThe spreadsheet is evaluated top-to-bottom. Put broad patterns first, specific overrides later.
noindex robots directives to draft or staging content.Before starting, create a checklist of all steps to track progress:
Fetch the site's query index to get a listing of all indexed pages:
https://<branch>--<repo>--<owner>.aem.live/query-index.json?limit=1000
If the user provides a production URL instead, derive the AEM URL or ask for the owner, repo, and branch values.
The query index returns an object with a data array. Each entry contains:
path — the page path (e.g., /blog/my-post)title — the page title from metadatadescription — the page description from metadataimage — the page's OG image pathlastModified — Unix timestamp of last modificationThere may also be custom properties defined in the site's helix-query.yaml configuration.
If the index returns exactly the limit number of results, warn the user that there may be more pages. Suggest increasing the limit or paginating with the offset parameter.
If the query index returns a 404 (no helix-query.yaml configured), use this fallback chain:
https://<branch>--<repo>--<owner>.aem.live/sitemap.xml. Parse <url><loc> entries to build a page list.For each page returned by the query index, check:
/ or be a full URL.robots meta tag. Most published pages should not have noindex — flag any production page with noindex as a critical issue./drafts/ or test paths should have noindex if they appear in the query index.For a deeper audit, optionally fetch individual pages' HTML to check their full <meta> tags (og:title, og:description, robots, canonical). Only do this if the user requests a deep audit or the site has fewer than 50 pages.
If a bulk metadata spreadsheet already exists, fetch it:
https://<branch>--<repo>--<owner>.aem.live/metadata.json
This returns the spreadsheet as JSON with a data array. Each entry has properties matching the spreadsheet column headers (URL, Title, Description, Image, etc.).
If this returns a 404, there is no bulk metadata spreadsheet yet — note this and proceed.
If it exists, analyze the current rules:
Present a summary table of all pages with their metadata status:
| # | Path | Title | Title Len | Title OK? | Description | Desc Len | Desc OK? | Image | Issues | |---|------|-------|-----------|-----------|-------------|----------|----------|-------|--------| | 1 | /about | About Us | 8 | Short | Our company... | 142 | OK | /media/hero.jpg | Title too short | | 2 | /blog/post-1 | | — | Missing | | — | Missing | | No title, no description, no image |
Produce a metadata spreadsheet table that the user can paste directly into a Google Sheet or Excel file. This is the corrected/improved version of the bulk metadata.
Format:
| URL | Title | Description | Image | Robots | Template | |-----|-------|-------------|-------|--------|----------| | /** | [site default title suffix] | [site default description] | [default og:image path] | | | | /blog/** | | | /media/blog-default.jpg | | article | | /drafts/** | | | | noindex | | | /events/* | | | /media/events-hero.jpg | | event |
/**) go first. These set the baseline defaults."" (empty string) to explicitly clear a value inherited from a broader pattern if needed.Robots value, omit that column.* (single path level) and ** (deep path).Based on the audit findings:
/blog/**, /products/**) and set section-level defaults.noindex rules for draft, staging, or test content paths.Tell the user exactly how to implement the bulk metadata spreadsheet:
nav and footer documents), create a new Google Sheet named metadata.After publishing, verify the metadata is applied:
https://<branch>--<repo>--<owner>.aem.live/metadata.json and confirm your rules appear.<meta> tags in the page source.| Problem | Cause | Solution |
|---------|-------|----------|
| Query index returns empty or 404 | Site may not have a query index configured, or the URL is wrong | Verify the owner, repo, and branch values; check that helix-query.yaml exists in the repo |
| Metadata changes not appearing on pages | Page-level metadata is overriding bulk metadata | This is expected behavior — page-level always wins |
| Metadata.json returns 404 | No bulk metadata spreadsheet exists yet | This is fine — the user will create one using the generated spreadsheet |
| Patterns not matching expected pages | Pattern syntax may be wrong | Use /** for deep paths, /* for single-level; patterns must start with / |
| Spreadsheet changes not taking effect | Spreadsheet may not be published | Open Sidekick on the spreadsheet and click Publish |
| Too many pages in the index | Query index has a default limit | Use ?limit=1000 or paginate with ?offset=1000&limit=1000 |
tools
Use the run-workflow MCP to discover, compose, execute, publish, and save Adobe Firefly workflows. TRIGGER when: user asks what actions are available, what the MCP can do, how to process images/video/3D via workflow, wants to build/run/save/publish a workflow, OR pastes any workflow/batch/execution ID. BARE ID (UUID/workflowId/batchId) = INSPECT ONLY — call inspect_run, NEVER run_workflow_submit. ALWAYS call list_actions first for capability/discovery questions. DO NOT TRIGGER for direct Firefly API calls without MCP (use firefly-api-specs).
tools
Run predefined featured workflows via run-workflow MCP. TRIGGER when user names a featured workflow (retargeting, banners at scale, localization, packaging, banner advertising, etc.) or asks to run a known marketing/production workflow. Requires run-workflow MCP. ALWAYS call get_featured_workflow before compose_workflow. DO NOT TRIGGER for custom one-off workflows with no named template — use run-workflow skill.
tools
Migrate an Adobe Commerce App Builder project from the Integration Starter Kit or Checkout Starter Kit to the new App Management approach. Run from the root of the App Builder project to be migrated. Pass --auto to skip confirmation prompts (suitable for CI or batch use) — auto mode prints a summary of all Q&A questions answered with their defaults. Pass --doc-scan-only to scan README.md and env.dist for outdated content without modifying any files. Use when the user wants to migrate an App Builder project from the Integration Starter Kit or Checkout Starter Kit to the App Management approach, or mentions upgrading their Adobe Commerce extension architecture.
development
Add or modify webhook interceptors in an Adobe Commerce app. Use when the user wants to intercept Commerce operations to validate input, append data, or modify behavior — before or after execution. Requires a base app initialized with commerce-app-init.