Purpose
llms.txt is the emerging convention for telling AI assistants, in plain text, what a site is and where the important pages live — a curated front door for models. This skill assembles an llms.txt from the store's own data (brand one-liner, top collections, flagship products with URLs, shipping/returns policy links, contact) and publishes it through a theme template so it's served at a stable path. Fixes llms-txt / well-known-llms-txt and the related agents-md discovery signals.
Prerequisites
- Authenticated Shopify CLI session (
shopify auth login --store <domain>) - Required API scopes:
read_products,read_themes,write_themes
Parameters
All skills accept these universal parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| store | string | yes | — | Store domain (e.g., mystore.myshopify.com) |
| format | string | no | human | Output format: human (default) or json |
| dry_run | bool | no | true | Preview the generated llms.txt without writing (defaults ON — edits the live theme) |
Skill-specific parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| theme_id | string | no | — | Theme GID (defaults to published MAIN theme) |
| flagship_count | int | no | 12 | How many top products to list |
| flagship_collection_id | string | no | — | Collection to draw the flagship list from (else best-sellers/recent) |
| brand_summary | string | no | — | Override the brand one-liner (else inferred from shop + homepage) |
Safety
⚠️ Step 4 (
themeFilesUpsert) writes a new template to the LIVE theme. The content is additive (a new file/route) and low-risk, but it is published immediately. Defaultsdry_run: trueso you review the generatedllms.txtfirst. Duplicating the theme before writing is recommended.
Workflow Steps
OPERATION:
shop— query Inputs: none Expected output: Shop name, domain, contact email, policy URLs — the header + contact + policy section of llms.txt.OPERATION:
products— query Inputs:first: flagship_count, optionalquery: "collection_id:'<id>'"(whenflagship_collection_idis set — the curated set is your flagship list),sortKey: UPDATED_AT, reverse: trueotherwise; fieldstitle,onlineStoreUrl,description. Expected output: Flagship product list with URLs for the "Key products" section. (Top-levelproductshas no best-selling sort — use the curated collection for true bestsellers, else most-recently-updated as the proxy.)COMPUTE (no API): render the
llms.txtmarkdown:# <Brand>+ one-liner,## Key products(title — URL — one line),## Policies(shipping/returns/privacy URLs),## Contact. Emit the full text.OPERATION:
themeFilesUpsert— mutation Inputs:themeId, writetemplates/page.llms.liquid(a page template that outputs the text astext/plain) plus, if used, anassets/llms.txtcopy; skipped whendry_run. Expected output: Upserted theme file(s); collectuserErrors. (Note: surface a one-line instruction to create a Page using thellmstemplate, or to add a redirect from/llms.txt.)
GraphQL Operations
# shop:query — validated against api_version 2025-01
query LlmsTxtShop {
shop {
name
primaryDomain { url }
contactEmail
shopPolicies { type url }
}
}
# products:query — validated against api_version 2025-01
# Top-level `products` has no BEST_SELLING sort key. Pass a `collection_id:'…'`
# query filter to use a curated flagship set, otherwise fall back to the most
# recently updated products.
query LlmsTxtFlagship($first: Int!, $query: String) {
products(first: $first, query: $query, sortKey: UPDATED_AT, reverse: true) {
edges {
node { id title onlineStoreUrl description }
}
}
}
# themeFilesUpsert:mutation — validated against api_version 2025-01
mutation LlmsTxtUpsert($themeId: ID!, $files: [OnlineStoreThemeFilesUpsertFileInput!]!) {
themeFilesUpsert(themeId: $themeId, files: $files) {
upsertedThemeFiles { filename }
userErrors { filename code message }
}
}
Session Tracking
Claude MUST emit the following output at each stage. This is mandatory.
On start, emit:
╔══════════════════════════════════════════════╗
║ SKILL: <skill name> ║
║ Store: <store domain> ║
║ Started: <YYYY-MM-DD HH:MM UTC> ║
╚══════════════════════════════════════════════╝
After each step, emit:
[N/TOTAL] <QUERY|MUTATION> <OperationName>
→ Params: <brief summary of key inputs>
→ Result: <count or outcome>
If dry_run: true, prefix every mutation step with [DRY RUN] and do not execute it.
On completion, emit:
For format: human (default):
══════════════════════════════════════════════
OUTCOME SUMMARY
<Metric label>: <value>
Errors: 0
Output: <filename or "none">
══════════════════════════════════════════════
For format: json, emit:
{
"skill": "<skill-slug>",
"store": "<domain>",
"started_at": "<ISO8601>",
"completed_at": "<ISO8601>",
"dry_run": false,
"steps": [
{
"step": 1,
"operation": "<OperationName>",
"type": "query",
"params_summary": "<string>",
"result_summary": "<string>",
"skipped": false
}
],
"outcome": {
"metric_key": 0,
"errors": 0,
"output_file": null
}
}
Output Format
human: the full generated llms.txt + the post-publish step (create a Page on the llms template, or add a /llms.txt → page redirect). json: { theme_id, llms_txt, wrote: true|false, follow_up, errors }.
Error Handling
| Error | Cause | Recovery |
|---|---|---|
THROTTLED |
API rate limit | Wait 2s, retry up to 3 times |
ACCESS_DENIED |
Missing write_themes |
Abort; output the generated llms.txt so it can be added manually |
| No flagship products | Empty/new catalog | Publish header + policies + contact only |
Best Practices
- Keep it short and high-signal —
llms.txtis a map, not a dump; link to canonical pages rather than pasting content. - Regenerate when the flagship lineup or policies change (the
agentic-readiness-monitorroutine can schedule this). - Pair with
shopify-admin-agentic-crawler-access— an llms.txt only helps if crawlers are allowed in. - After publishing, add a storefront redirect
/llms.txt→ the page so it sits at the conventional path.