create_product
Create a Shopify product with default draft status to avoid premature publishing. Optionally attach public image URLs. Get back the product's GID and handle.
Instructions
Create a new product. The product is created first, then any image_urls (publicly fetchable) are attached as a follow-up call — Shopify pulls each URL and hosts the image on its CDN. The default status is DRAFT to prevent accidentally publishing half-configured products to the storefront; pass status=ACTIVE only when you're ready to go live. New products start with a single hidden 'Default Title' variant; to add real variants with options, call create_variants with strategy='REMOVE_STANDALONE_VARIANT'. Returns the new product's GID and handle.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | ||
| description | No | Description as HTML | |
| vendor | No | ||
| product_type | No | ||
| tags | No | ||
| status | No | DRAFT | |
| image_urls | No | Image URLs to attach after creation |
Implementation Reference
- src/tools/products.ts:213-252 (registration)Tool 'create_product' is registered on the MCP server via server.tool(...) with its name, description, schema and handler.
server.tool( "create_product", "Create a new product. The product is created first, then any image_urls (publicly fetchable) are attached as a follow-up call — Shopify pulls each URL and hosts the image on its CDN. The default `status` is DRAFT to prevent accidentally publishing half-configured products to the storefront; pass status=ACTIVE only when you're ready to go live. New products start with a single hidden 'Default Title' variant; to add real variants with options, call create_variants with strategy='REMOVE_STANDALONE_VARIANT'. Returns the new product's GID and handle.", createProductSchema, async (args) => { const data = await client.graphql<{ productCreate: { product: Product | null; userErrors: ShopifyUserError[]; }; }>(CREATE_PRODUCT_MUTATION, { input: { title: args.title, descriptionHtml: args.description, vendor: args.vendor, productType: args.product_type, tags: args.tags, status: args.status, }, }); throwIfUserErrors(data.productCreate.userErrors, "productCreate"); const product = data.productCreate.product; if (!product) throw new Error("productCreate returned no product"); const attached: string[] = []; if (args.image_urls?.length) { await attachImages(client, product.id, args.image_urls); attached.push(...args.image_urls); } const lines = [ `Created ${args.status} product: ${product.title} (${product.id})`, ` handle: ${product.handle}`, ]; if (attached.length) { lines.push(` attached ${attached.length} image(s)`); } return { content: [{ type: "text" as const, text: lines.join("\n") }] }; }, ); - src/tools/products.ts:129-140 (schema)Input schema for create_product using Zod: title (required string), description, vendor, product_type, tags, status (default DRAFT), and image_urls.
const createProductSchema = { title: z.string().min(1), description: z.string().optional().describe("Description as HTML"), vendor: z.string().optional(), product_type: z.string().optional(), tags: z.array(z.string()).optional(), status: z.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).default("DRAFT"), image_urls: z .array(z.string().url()) .optional() .describe("Image URLs to attach after creation"), }; - src/tools/products.ts:217-252 (handler)Handler function that executes the product creation via GraphQL mutation, then optionally attaches images via attachImages helper.
async (args) => { const data = await client.graphql<{ productCreate: { product: Product | null; userErrors: ShopifyUserError[]; }; }>(CREATE_PRODUCT_MUTATION, { input: { title: args.title, descriptionHtml: args.description, vendor: args.vendor, productType: args.product_type, tags: args.tags, status: args.status, }, }); throwIfUserErrors(data.productCreate.userErrors, "productCreate"); const product = data.productCreate.product; if (!product) throw new Error("productCreate returned no product"); const attached: string[] = []; if (args.image_urls?.length) { await attachImages(client, product.id, args.image_urls); attached.push(...args.image_urls); } const lines = [ `Created ${args.status} product: ${product.title} (${product.id})`, ` handle: ${product.handle}`, ]; if (attached.length) { lines.push(` attached ${attached.length} image(s)`); } return { content: [{ type: "text" as const, text: lines.join("\n") }] }; }, ); - src/tools/products.ts:308-331 (helper)attachImages helper used by create_product handler to attach image URLs to a product after creation.
export async function attachImages( client: ShopifyClient, productId: string, imageUrls: string[], altText?: string, ): Promise<Array<{ id: string }>> { const data = await client.graphql<{ productCreateMedia: { media: Array<{ id: string } | null>; mediaUserErrors: ShopifyUserError[]; }; }>(CREATE_MEDIA_MUTATION, { productId, media: imageUrls.map((url) => ({ originalSource: url, mediaContentType: "IMAGE", alt: altText, })), }); throwIfUserErrors(data.productCreateMedia.mediaUserErrors, "productCreateMedia"); return data.productCreateMedia.media.filter( (m): m is { id: string } => m !== null, ); } - src/server.ts:57-57 (registration)Import and call of registerProductTools which registers create_product along with other product tools.
registerProductTools(s, shopify);