shopify-operator
Gated writes on products already in the merchant's Shopify store — publish an existing draft, rename, reprice, restock, unlist. New products are not listed here (Product Management → Listing Hub). WRITE capability, gated (explicit merchant confirmation per product).
name: shopify-operator description: >- Gated writes on products already in the merchant's Shopify store — publish an existing draft, rename, reprice, restock, unlist. New products are not listed here (Product Management → Listing Hub). WRITE capability, gated (explicit merchant confirmation per product). tags: [internal, ops, requires-yepai-tools] license: MIT-0
Shopify Operator — changes to products already in the store
The write end of the ops flow, for products that already exist in the merchant's
store. The write tools are PUBLISH_PRODUCT, RENAME_PRODUCT, UNLIST_PRODUCT,
REPRICE_PRODUCT, SET_INVENTORY on the operation MCP server — all tenant-scoped
(never pass a shop domain). A NEW product is not listed here: listing happens in
Product Management → Listing Hub (AGENTS).
Golden rule — gated write
Writes are allowed but never silent and never straight-to-live:
- Show the merchant the product before it goes live — the
STORE_PRODUCTSlookup puts its title, price, status and image on screen; never re-type them. - Publish only on explicit OK. Going live (
PUBLISH_PRODUCT) is a separate call the merchant must confirm per product — the two-call flow below. Never reprice/restock/delete on live products, or bulk-publish, without per-action approval. (The runtime also enforces an approval gate on write tools.)
Read companion: STORE_PRODUCTS lists the store's live catalog (title,
ACTIVE/DRAFT/ARCHIVED status, currency-labelled price, inventory, link) as a
table block — use it to locate/verify a product before any write, and to
answer "list my products / price / stock / status" directly instead of
sending the merchant to the Shopify Admin (in-product page: Product
Management → Store Management). For one known product pass
query="id:<numeric id>" (preferred — immutable) or query="handle:<handle>"
(Shopify search syntax) — ONE call returning just that product; never pull the
full catalog to find something you already have an id or handle for. Do NOT
add a status: filter to a single-product lookup — it can hide the product
(e.g. an ARCHIVED item is invisible behind status:draft).
Target by id, not by name (SKU is never a product locator): the Product
Management hand-offs carry (handle: <handle>, id: <numeric id>) — pass that
id to PUBLISH_PRODUCT / UNLIST_PRODUCT / REPRICE_PRODUCT as
product_id (gid://shopify/Product/<numeric id>, or use query="id:…" to
look it up). The id survives renames; a handle changes when the merchant
edits it; a title is ambiguous. SKUs are variant-level (optional,
duplicable, many per product) — they locate a variant inside SET_INVENTORY,
never a product (and SET_INVENTORY itself takes product_id for
single-variant products — don't demand a SKU you can resolve).
Catalog rows carry hidden targeting keys: every STORE_PRODUCTS row
includes consumer-only id (numeric) and handle — not shown to the
merchant, but IN your tool result. Read the id from the row and act on it;
same-named products are told apart by their row's price/status ("the
USD 70.00 one" = the row whose price says USD 70.00 — use THAT row's id).
NEVER tell the merchant you cannot see product ids, and never route them to
Shopify Admin to fetch one.
Locating by NAME (no id in hand — merchant typed a product name): a fixed two-shot playbook, then stop:
- ONE lookup:
STORE_PRODUCTS(query="title:*<distinctive word>*")— the*wildcards*are REQUIRED (title:wordis prefix/exact and usually returns 0). Pick the most distinctive word from the merchant's phrasing (brand, model), not a generic one ("bag"). - If 0 rows: ONE retry with bare free text (
query="<word>"— matches title/vendor/SKU/tags, expect extra hits). Still nothing, or several plausible matches? STOP and ask — show the near matches and let the merchant pick. HARD limits: max 2 locate queries per task, NEVER construct/guess a handle from a title (handles are copied from tool results only), NEVER sweep the catalog (status:activeetc.) to find one product (real failure 2026-07-17: 12 lookup cards for 2 renames — guessed handles, wildcard-less title:, then full-catalog sweeps). A multi-product ask ("remove YepAI from all titles") is ONEtitle:*yepai*lookup → collect the ids → act on each.
Terse tool-shaped messages (SET_INVENTORY = 10, query="id:123…") are
merchant instructions about the product under discussion — act on THAT
product and echo which one (title + id) in your reply. An id the merchant
just gave you is a Shopify id in this context: never re-read it as a
supplier's or another platform's id, and never drift back to an earlier flow
the merchant didn't ask to resume.
Publishing an EXISTING product (a draft already in the store)
When the merchant asks to publish something ALREADY in their store — e.g. the Product Management hand-off "Publish the EXISTING draft … (handle: x, id: n)" — this is a two-call flow, nothing more:
STORE_PRODUCTS(query="id:<n>")(orhandle:if no id) — ONE lookup, nostatus:filter; show the merchant the current title, price, status and image from the card, ask them to confirm, and END THE TURN there.PUBLISH_PRODUCTis never in the same turn as this lookup — not even when the hand-off itself says "publish": the hand-off is the request, the merchant's reply to what you showed is the confirmation.- On their confirm →
PUBLISH_PRODUCT(product_id="gid://shopify/Product/<n>")(orhandle=if no id). Do NOT create a new product, do NOT run margin analysis unless asked.PUBLISH_PRODUCTrefuses (with the missing fields named) if the draft has no image or no positive price — relay that verbatim: fix in the report flow or admin, then retry. On publication failure it rolls the product back to DRAFT and says so inwarnings.
Confirmation binding — a bare "yes / confirm" answers YOUR last question only
A bare confirmation ("yes", "confirm", "ok", "go ahead") binds EXCLUSIVELY to the question in YOUR immediately preceding message of THIS conversation.
- If your last message asked nothing, do not guess — ask what they are confirming.
- NEVER resolve a confirmation against an older pending flow, another conversation, or anything only in residual memory (real failure 2026-07-18: a bare "yes" confirming a SETTINGS change executed a PUBLISH whose product id existed in no message of the conversation — the wrong product went LIVE).
- Write-target provenance: every write's target id must appear somewhere in THIS conversation (a tool result, a hand-off message, or the merchant's own words). If an id exists only in your recollection, that is contamination — STOP, say you have no valid target in this conversation, and ask.
Verbatim fidelity — ids and URLs are COPIED, never retyped
- Ids: every id you pass to a tool must be copy-pasted from a tool result visible in this conversation (STORE_PRODUCTS / a write's own result / the hand-off message). NEVER reconstruct digits from memory (real 2026-07-16: a just-created draft got "published" under an id that existed nowhere). When a write returns not found, suspect YOUR transcription FIRST: re-anchor on the recent-DRAFTs list inside the error (verbatim ids) or one STORE_PRODUCTS lookup — don't lead the merchant through wrong-tenant/deleted-product theories before ruling out your own slip.
- URLs: quote a URL exactly or not at all — never shorten or decorate it (a trailing "…" became part of a preview_key and broke the link, same day). The result card already carries the canonical View/preview link; in prose prefer "use the View link on the card above".
Reading the result (honesty — SOUL rule 5)
published: true→ the product is LIVE (status ACTIVE + Online Store). Only then say "live/published".status: "DRAFT"+ warnings → NOT live; say exactly why (quoteissues/warnings— e.g. "publish failed — reverted to DRAFT").status: "authorization_required"+authorize_url→ the app lacks a Shopify scope; give the merchant the link to grant access, then retry. A handshake, not an error.preview_urlworks while DRAFT — show it for sign-off;public_urlis the eventual storefront link.
Rename (Product Management pencil hand-off)
The hand-off reads I want to rename the listing "T" (handle: x, id: n). Please update the title to: <new title>. This is ONE call:
RENAME_PRODUCT(product_id="gid://shopify/Product/<n>", title="<new title>")
— no lookup needed (the hand-off already identifies the product), no
in-product "edit the title field" path exists, and do NOT send the merchant
to Shopify Admin. Then quote before_title → title back. The handle and
product URL do NOT change — say so. If the tool warns the new title looks
like a URL slug (lowercase-hyphenated — usually the handle pasted by
mistake), the rename HAS been applied: relay the warning, suggest the
human-readable version, and offer to rename again.
Unlist / reprice / inventory (loop one at a time for bulk)
RENAME_PRODUCT—title(the new customer-facing title) +product_id(orhandle). Title only; handle/URL untouched. Slug-like titles applied but flagged inwarnings; >80 chars warned, not trimmed.PUBLISH_PRODUCT—product_id(orhandle). Publishes an EXISTING product: completeness check (image + positive price) → status ACTIVE + Online Store publication, rollback to DRAFT if publication fails. The gated-write counterpart ofUNLIST_PRODUCT.UNLIST_PRODUCT—product_id(orhandle) +mode:"draft"(off sale, re-listable) ·"archive"(hidden) ·"channel"(remove from the Online Store channel only, status stays ACTIVE).REPRICE_PRODUCT—product_id,price, optionalcompare_at,cost(to report the new margin). All variants → one price; the result returnsbefore_prices→new_priceandvariants_updated— quote the before→after to the merchant. Only on an explicit merchant price change.SET_INVENTORY—quantity+product_id(preferred — the id from the hand-off or your own lookup; a single-variant product resolves directly). A multi-variant product is refused WITH its variant list — relay it and ask the merchant which one, then call again with that variant'ssku.sku/inventory_item_idremain for variant-precise calls; optionallocation_id(defaults to the first active location),track=trueif the item isn't tracked yet. NEVER ask the merchant for a SKU you can resolve from a product id you already have, and NEVER send them to Shopify Admin to look one up. Note: SKU search can lag right after a product is created.- Bulk — there is no bulk tool: loop the write per confirmed product, and report per-item results (a failed item doesn't stop the rest).
Presenting a product to the merchant
- The lookup carries the product: the
STORE_PRODUCTSresult on screen shows its image, title, price and status. Reply in 1–3 sentences; never embed image markdown (chat does not render it) and never re-type the card's numbers. - Say what the margin is when a result carries one (
REPRICE_PRODUCTwithcost): quote gross margin % and say it is gross — the price against the product's cost, shipping and fees excluded. - Define jargon on first use ("landed cost = product cost + shipping + import fees"; "compare-at = the crossed-out 'was' price").
- Match the merchant's language — listing copy in the store's target language; talk to the merchant in the language they read.
- One sentence before each write — say what you're about to do and why.
Honesty & gating
- Attribute sources for a figure the card does not show ("88% gross — your price against your supplier's cost"); the supply channel itself is never named.
- Margin shown is gross only (retail − supplier cost); shipping/fees excluded — say
so, and pair with
MARGIN_ANALYZEfor the fuller picture. - If an image URL was dropped or a field is missing, surface it — don't fabricate.
A sweep is not an instruction
"Delist everything that hasn't sold in 30 days", "reprice all the slow movers", "clear out the dead stock" — these name a RULE, not a product. They are the merchant thinking out loud about a policy, and the one thing you must not do is start applying it. Answer with the list: which products the rule would catch, and what each one would lose. Then let them pick. Per-action approval is not a formality you can batch — it exists because unlisting the wrong product costs a merchant sales they never see, and there is no undo they will think to ask for. This is the one place where being helpful and being safe point in opposite directions, and safe wins.
Download
ZIP package — ready to use
Skill Info
- Creator
- Yepai
- License
- MIT-0
- Downloads
- 0
- Published
- Oct 26, 2026
- Updated
- Oct 29, 2026