update_variants
Update one or more product variants in a single call: modify price, compareAtPrice, SKU, barcode, taxable status, inventory policy (DENY or CONTINUE), and option values without changing inventory quantities.
Instructions
Update one or more existing variants in a single call. Editable fields: price, compareAtPrice (set to null to clear), SKU, barcode, taxable, inventoryPolicy (DENY blocks oversells, CONTINUE allows backorders), and optionValues (e.g. rename a size). Per-variant only; only the fields you provide are written. For inventory quantity changes use set_inventory_quantity — this tool deliberately doesn't accept quantities to keep that audit trail in one place.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| productId | Yes | Product GID. | |
| variants | Yes |
Implementation Reference
- src/tools/variants.ts:353-387 (handler)The main handler function for the 'update_variants' MCP tool. Calls the Shopify GraphQL mutation productVariantsBulkUpdate with the provided productId and variants, throws on user errors, and returns a formatted result listing updated variants.
server.tool( "update_variants", "Update one or more existing variants in a single call. Editable fields: price, compareAtPrice (set to null to clear), SKU, barcode, taxable, inventoryPolicy (DENY blocks oversells, CONTINUE allows backorders), and optionValues (e.g. rename a size). Per-variant only; only the fields you provide are written. For inventory quantity changes use set_inventory_quantity — this tool deliberately doesn't accept quantities to keep that audit trail in one place.", updateVariantsSchema, async (args) => { const data = await client.graphql<{ productVariantsBulkUpdate: { productVariants: VariantNode[]; userErrors: ShopifyUserError[]; }; }>(VARIANTS_BULK_UPDATE_MUTATION, { productId: args.productId, variants: args.variants, }); throwIfUserErrors( data.productVariantsBulkUpdate.userErrors, "productVariantsBulkUpdate", ); const updated = data.productVariantsBulkUpdate.productVariants; return { content: [ { type: "text" as const, text: [ `Updated ${updated.length} variant(s):`, ...updated.map( (v) => ` ${v.title} ${v.price}${v.compareAtPrice ? ` (cmp ${v.compareAtPrice})` : ""}${v.sku ? ` SKU:${v.sku}` : ""} — ${v.id}`, ), ].join("\n"), }, ], }; }, ); - src/tools/variants.ts:209-212 (schema)The Zod schema for update_variants input validation. Requires productId (string) and variants (array of variantUpdateSchema, 1-100 items).
const updateVariantsSchema = { productId: z.string().describe("Product GID."), variants: z.array(variantUpdateSchema).min(1).max(100), }; - src/tools/variants.ts:182-191 (schema)The per-variant update schema (variantUpdateSchema). Defines editable fields: id (required), price, compareAtPrice (nullable), sku, barcode, taxable, inventoryPolicy, optionValues.
const variantUpdateSchema = z.object({ id: z.string().describe("Variant GID to update."), price: z.string().optional(), compareAtPrice: z.string().optional().nullable(), sku: z.string().optional(), barcode: z.string().optional(), taxable: z.boolean().optional(), inventoryPolicy: z.enum(["DENY", "CONTINUE"]).optional(), optionValues: z.array(optionValueInputSchema).optional(), }); - src/server.ts:20-64 (registration)Import and registration of registerVariantTools in the main server setup, which registers update_variants (along with other variant tools) on the McpServer.
import { registerVariantTools } from "./tools/variants.js"; import { registerFulfillmentTools } from "./tools/fulfillment.js"; import { registerWebhookTools } from "./tools/webhooks.js"; import { registerMetaobjectTools } from "./tools/metaobjects.js"; import { registerAnalyticsTools } from "./tools/analytics.js"; import { registerBridgeTools } from "./tools/bridge.js"; export interface ServerConfig { host: string; port: number; shopifyStore: string; shopifyAccessToken: string; shopifyApiVersion?: string; comfyUIUrl?: string; comfyUIPublicUrl?: string; comfyUIDefaultCkpt: string; } interface Session { server: McpServer; transport: StreamableHTTPServerTransport; } function buildContext(config: ServerConfig) { const shopify = new ShopifyClient({ store: config.shopifyStore, accessToken: config.shopifyAccessToken, apiVersion: config.shopifyApiVersion, }); const comfyui = config.comfyUIUrl ? new ComfyUIClient({ baseUrl: config.comfyUIUrl, publicUrl: config.comfyUIPublicUrl ?? config.comfyUIUrl, }) : null; const buildServer = () => { const s = new McpServer({ name: "shopify-mcp", version: "0.1.0" }); registerProductTools(s, shopify); registerOrderTools(s, shopify); registerInventoryTools(s, shopify); registerCustomerTools(s, shopify); registerMetafieldTools(s, shopify); registerDraftOrderTools(s, shopify); registerCollectionTools(s, shopify); registerVariantTools(s, shopify); - src/tools/variants.ts:84-101 (helper)The GraphQL mutation string (VARIANTS_BULK_UPDATE_MUTATION) used by the update_variants handler to call productVariantsBulkUpdate.
const VARIANTS_BULK_UPDATE_MUTATION = /* GraphQL */ ` mutation VariantsBulkUpdate( $productId: ID! $variants: [ProductVariantsBulkInput!]! ) { productVariantsBulkUpdate(productId: $productId, variants: $variants) { productVariants { id title price compareAtPrice sku barcode } userErrors { field message } } } `;