update-product
Modify product details in Shopify stores by updating titles, descriptions, SEO metadata, pricing, inventory, and variants through the Shopify MCP Server.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| productId | Yes | The GID of the product to update (e.g., "gid://shopify/Product/1234567890") | |
| title | No | The new title for the product | |
| descriptionHtml | No | The new HTML description for the product | |
| seo | No | SEO information for the product | |
| status | No | Product status (ACTIVE, ARCHIVED, or DRAFT) | |
| vendor | No | The vendor or manufacturer of the product | |
| productType | No | The type or category of the product | |
| tags | No | Array of tags to categorize the product | |
| variants | No | Product variants to update |
Implementation Reference
- src/tools/updateProduct.ts:168-261 (handler)The `execute` method of the `updateProduct` tool object, which contains the core logic for updating Shopify product details and variants using GraphQL mutations.execute: async (input: UpdateProductInput) => { if (!updateProduct.client) { throw new Error('GraphQL client not initialized. Call initialize() first.'); } try { const { productId, variants, ...productInput } = input; let response: ProductUpdateResponse; // If we're only updating variants, fetch the initial product data if (Object.keys(productInput).length === 0) { response = await updateProduct.client.request<ProductUpdateResponse>(updateProductMutation, { input: { id: productId } }); } else { // Update product details response = await updateProduct.client.request<ProductUpdateResponse>(updateProductMutation, { input: { id: productId, ...productInput } }); if (response.productUpdate.userErrors.length > 0) { throw new Error( `Failed to update product: ${response.productUpdate.userErrors .map((error) => error.message) .join(", ")}` ); } } // Update variants if provided if (variants && variants.length > 0) { const variantResponse = await updateProduct.client.request<VariantsBulkUpdateResponse>(updateVariantsMutation, { productId, variants: variants.map(variant => ({ id: variant.id, price: variant.price, compareAtPrice: variant.compareAtPrice, sku: variant.sku, barcode: variant.barcode, inventoryPolicy: variant.inventoryPolicy })) }); if (variantResponse.productVariantsBulkUpdate.userErrors.length > 0) { throw new Error( `Failed to update variants: ${variantResponse.productVariantsBulkUpdate.userErrors .map((error) => error.message) .join(", ")}` ); } // Fetch the latest product data after variant update response = await updateProduct.client.request<ProductUpdateResponse>(updateProductMutation, { input: { id: productId } }); } // Format variants for response const formattedVariants = response.productUpdate.product.variants.edges.map((edge) => ({ id: edge.node.id, title: edge.node.title, price: edge.node.price, compareAtPrice: edge.node.compareAtPrice, sku: edge.node.sku, barcode: edge.node.barcode, inventoryQuantity: edge.node.inventoryQuantity, inventoryPolicy: edge.node.inventoryPolicy })); return { product: { id: response.productUpdate.product.id, title: response.productUpdate.product.title, descriptionHtml: response.productUpdate.product.descriptionHtml, handle: response.productUpdate.product.handle, status: response.productUpdate.product.status, vendor: response.productUpdate.product.vendor, productType: response.productUpdate.product.productType, tags: response.productUpdate.product.tags, seo: response.productUpdate.product.seo, variants: formattedVariants, updatedAt: response.productUpdate.product.updatedAt } }; } catch (error: unknown) { if (error instanceof Error) { throw new Error(`Failed to update product: ${error.message}`); } throw new Error('Failed to update product: Unknown error'); } },
- src/tools/updateProduct.ts:5-29 (schema)Zod input schema defining all parameters for the update-product tool, used in the tool definition.const UpdateProductInputSchema = z.object({ productId: z.string().min(1).describe("The GID of the product to update (e.g., \"gid://shopify/Product/1234567890\")"), title: z.string().optional().describe("The new title for the product"), descriptionHtml: z.string().optional().describe("The new HTML description for the product"), seo: z.object({ title: z.string().optional().describe("SEO-optimized title for the product"), description: z.string().optional().describe("SEO meta description for the product") }).optional().describe("SEO information for the product"), status: z.enum(["ACTIVE", "ARCHIVED", "DRAFT"]).optional().describe("Product status (ACTIVE, ARCHIVED, or DRAFT)"), vendor: z.string().optional().describe("The vendor or manufacturer of the product"), productType: z.string().optional().describe("The type or category of the product"), tags: z.array(z.string()).optional().describe("Array of tags to categorize the product"), variants: z.array(z.object({ id: z.string().optional().describe("The GID of the variant to update"), price: z.string().optional().describe("The price of the variant"), compareAtPrice: z.string().optional().describe("Compare at price for showing a markdown"), sku: z.string().optional().describe("Stock keeping unit (SKU)"), barcode: z.string().optional().describe("Barcode (ISBN, UPC, GTIN, etc.)"), inventoryQuantity: z.number().optional().describe("Available inventory quantity"), inventoryPolicy: z.enum(["DENY", "CONTINUE"]).optional().describe("What happens when a variant is out of stock"), fulfillmentService: z.string().optional().describe("Service responsible for fulfilling the variant"), weight: z.number().optional().describe("Weight of the variant"), weightUnit: z.enum(["KILOGRAMS", "GRAMS", "POUNDS", "OUNCES"]).optional().describe("Unit of weight measurement") })).optional().describe("Product variants to update") });
- src/index.ts:309-342 (registration)MCP server registration of the 'update-product' tool, including duplicated inline schema and wrapper that calls the tool's execute method.server.tool( "update-product", { productId: z.string().min(1).describe("The GID of the product to update (e.g., \"gid://shopify/Product/1234567890\")"), title: z.string().optional().describe("The new title for the product"), descriptionHtml: z.string().optional().describe("The new HTML description for the product"), seo: z.object({ title: z.string().optional().describe("SEO-optimized title for the product"), description: z.string().optional().describe("SEO meta description for the product") }).optional().describe("SEO information for the product"), status: z.enum(["ACTIVE", "ARCHIVED", "DRAFT"]).optional().describe("Product status (ACTIVE, ARCHIVED, or DRAFT)"), vendor: z.string().optional().describe("The vendor or manufacturer of the product"), productType: z.string().optional().describe("The type or category of the product"), tags: z.array(z.string()).optional().describe("Array of tags to categorize the product"), variants: z.array(z.object({ id: z.string().optional().describe("The GID of the variant to update"), price: z.string().optional().describe("The price of the variant"), compareAtPrice: z.string().optional().describe("Compare at price for showing a markdown"), sku: z.string().optional().describe("Stock keeping unit (SKU)"), barcode: z.string().optional().describe("Barcode (ISBN, UPC, GTIN, etc.)"), inventoryQuantity: z.number().optional().describe("Available inventory quantity"), inventoryPolicy: z.enum(["DENY", "CONTINUE"]).optional().describe("What happens when a variant is out of stock"), fulfillmentService: z.string().optional().describe("Service responsible for fulfilling the variant"), weight: z.number().optional().describe("Weight of the variant"), weightUnit: z.enum(["KILOGRAMS", "GRAMS", "POUNDS", "OUNCES"]).optional().describe("Unit of weight measurement") })).optional().describe("Product variants to update") }, async (args) => { const result = await updateProduct.execute(args); return { content: [{ type: "text", text: JSON.stringify(result) }] }; } );
- src/tools/updateProduct.ts:93-131 (helper)GraphQL mutation for updating product details, used in the handler.const updateProductMutation = gql` mutation productUpdate($input: ProductInput!) { productUpdate(input: $input) { product { id title descriptionHtml handle status vendor productType tags seo { title description } variants(first: 50) { edges { node { id title price compareAtPrice sku barcode inventoryQuantity inventoryPolicy } } } updatedAt } userErrors { field message } } } `;
- src/index.ts:71-71 (helper)Initialization of the updateProduct tool with the Shopify GraphQL client.updateProduct.initialize(shopifyClient);