create-discount
Generate discount codes for Shopify stores by specifying title, code, value type, amount, and validity dates to apply promotions to customer orders.
Instructions
Create a basic discount code
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Title of the discount | |
| code | Yes | Discount code that customers will enter | |
| valueType | Yes | Type of discount | |
| value | Yes | Discount value (percentage as decimal or fixed amount) | |
| startsAt | Yes | Start date in ISO format | |
| endsAt | No | Optional end date in ISO format | |
| appliesOncePerCustomer | Yes | Whether discount can be used only once per customer |
Implementation Reference
- src/index.ts:344-401 (registration)Registration of the MCP tool 'create-discount', including input schema with Zod and the handler function that constructs the input and delegates to ShopifyClient.createBasicDiscountCode.server.tool( "create-discount", "Create a basic discount code", { title: z.string().describe("Title of the discount"), code: z.string().describe("Discount code that customers will enter"), valueType: z .enum(["percentage", "fixed_amount"]) .describe("Type of discount"), value: z .number() .describe("Discount value (percentage as decimal or fixed amount)"), startsAt: z.string().describe("Start date in ISO format"), endsAt: z.string().optional().describe("Optional end date in ISO format"), appliesOncePerCustomer: z .boolean() .describe("Whether discount can be used only once per customer"), }, async ({ title, code, valueType, value, startsAt, endsAt, appliesOncePerCustomer, }) => { const client = new ShopifyClient(); try { const discountInput: CreateBasicDiscountCodeInput = { title, code, valueType, value, startsAt, endsAt, includeCollectionIds: [], excludeCollectionIds: [], appliesOncePerCustomer, combinesWith: { productDiscounts: true, orderDiscounts: true, shippingDiscounts: true, }, }; const discount = await client.createBasicDiscountCode( SHOPIFY_ACCESS_TOKEN, MYSHOPIFY_DOMAIN, discountInput ); return { content: [{ type: "text", text: JSON.stringify(discount, null, 2) }], }; } catch (error) { return handleError("Failed to create discount", error); } } );
- Core handler in ShopifyClient that implements the discount creation logic: input validation, GraphQL mutation execution via shopifyGraphqlRequest, response processing with error handling.async createBasicDiscountCode( accessToken: string, shop: string, discountInput: CreateBasicDiscountCodeInput ): Promise<CreateBasicDiscountCodeResponse> { if (discountInput.valueType === "percentage") { if (discountInput.value < 0 || discountInput.value > 1) { throw new CustomError( "Invalid input: percentage value must be between 0 and 1", "InvalidInputError", { contextData: { discountInput, shop, }, } ); } } if (discountInput.valueType === "fixed_amount") { if (discountInput.value <= 0) { throw new CustomError( "Invalid input: fixed_amount value must be greater than 0", "InvalidInputError", { contextData: { discountInput, shop, }, } ); } } const myShopifyDomain = await this.getMyShopifyDomain(accessToken, shop); const isEligibleForSubscription = await this.checkSubscriptionEligibility( accessToken, myShopifyDomain ); const graphqlQuery = this.graphqlQueryPreparationForCreateBasicDiscountCode(); const variables = this.prepareBasicDiscountCodeVariable( discountInput, isEligibleForSubscription ); const res = await this.shopifyGraphqlRequest<BasicDiscountCodeResponse>({ url: `https://${myShopifyDomain}/admin/api/${this.SHOPIFY_API_VERSION}/graphql.json`, accessToken, query: graphqlQuery, variables, }); const id = res.data.data.discountCodeBasicCreate.codeDiscountNode.id; const codeDiscount = res.data.data.discountCodeBasicCreate.codeDiscountNode.codeDiscount.codes .nodes[0]; const userErrors = res.data.data.discountCodeBasicCreate.userErrors; if (userErrors.length > 0) { throw getGraphqlShopifyUserError(userErrors, { shop, discountInput, }); } return { id, code: codeDiscount.code, }; }
- TypeScript type definition for the CreateBasicDiscountCodeInput used by the tool handler.export type CreateBasicDiscountCodeInput = { title: string; code: string; startsAt: ISODate; endsAt?: ISODate; valueType: string; value: number; usageLimit?: number; includeCollectionIds: string[]; excludeCollectionIds: string[]; appliesOncePerCustomer: boolean; combinesWith: { productDiscounts: boolean; orderDiscounts: boolean; shippingDiscounts: boolean; }; };
- Private helper method that returns the GraphQL mutation query string for discountCodeBasicCreate.private graphqlQueryPreparationForCreateBasicDiscountCode(): string { return gql` mutation discountCodeBasicCreate( $basicCodeDiscount: DiscountCodeBasicInput! ) { discountCodeBasicCreate(basicCodeDiscount: $basicCodeDiscount) { codeDiscountNode { id codeDiscount { ... on DiscountCodeBasic { title codes(first: 10) { nodes { code } } startsAt endsAt customerSelection { ... on DiscountCustomerAll { allCustomers } } customerGets { appliesOnOneTimePurchase appliesOnSubscription value { ... on DiscountPercentage { percentage } ... on DiscountAmount { amount { amount currencyCode } appliesOnEachItem } } items { ... on AllDiscountItems { allItems } } } appliesOncePerCustomer } } } userErrors { field code message } } } `;
- Private helper method that prepares the GraphQL variables object for the discount creation mutation.private prepareBasicDiscountCodeVariable( discountInput: CreateBasicDiscountCodeInput, isEligibleForSubscription: boolean ): any { return { basicCodeDiscount: { title: discountInput.title, code: discountInput.code, startsAt: discountInput.startsAt, endsAt: discountInput.endsAt, customerSelection: { all: true, }, customerGets: { appliesOnOneTimePurchase: isEligibleForSubscription ? true : undefined, appliesOnSubscription: isEligibleForSubscription ? true : undefined, value: { percentage: discountInput.valueType === "percentage" ? discountInput.value : undefined, discountAmount: discountInput.valueType === "fixed_amount" ? { amount: discountInput.value, appliesOnEachItem: false, } : undefined, }, items: { all: discountInput.excludeCollectionIds.length === 0 && discountInput.includeCollectionIds.length === 0, collections: discountInput.includeCollectionIds.length || discountInput.excludeCollectionIds.length ? { add: discountInput.includeCollectionIds.map( (id) => `gid://shopify/Collection/${id}` ), remove: discountInput.excludeCollectionIds.map( (id) => `gid://shopify/Collection/${id}` ), } : undefined, }, }, appliesOncePerCustomer: discountInput.appliesOncePerCustomer, recurringCycleLimit: isEligibleForSubscription ? discountInput.valueType === "fixed_amount" ? 1 : null : undefined, usageLimit: discountInput.usageLimit, combinesWith: { productDiscounts: discountInput.combinesWith.productDiscounts, orderDiscounts: discountInput.combinesWith.orderDiscounts, shippingDiscounts: discountInput.combinesWith.shippingDiscounts, }, }, }; }