@klaudworks/shopify-mcp 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +531 -0
- package/dist/index.js +107 -0
- package/dist/lib/formatters.js +72 -0
- package/dist/lib/shopifyAuth.js +96 -0
- package/dist/lib/toolUtils.js +36 -0
- package/dist/tools/completeDraftOrder.js +76 -0
- package/dist/tools/createCustomer.js +142 -0
- package/dist/tools/createDraftOrder.js +160 -0
- package/dist/tools/createFulfillment.js +100 -0
- package/dist/tools/createProduct.js +123 -0
- package/dist/tools/createRefund.js +115 -0
- package/dist/tools/deleteCustomer.js +47 -0
- package/dist/tools/deleteMetafields.js +54 -0
- package/dist/tools/deleteProduct.js +43 -0
- package/dist/tools/deleteProductVariants.js +82 -0
- package/dist/tools/getCollectionById.js +123 -0
- package/dist/tools/getCollections.js +88 -0
- package/dist/tools/getCustomerById.js +122 -0
- package/dist/tools/getCustomerOrders.js +131 -0
- package/dist/tools/getCustomers.js +125 -0
- package/dist/tools/getFulfillmentOrders.js +106 -0
- package/dist/tools/getInventoryItems.js +86 -0
- package/dist/tools/getInventoryLevels.js +82 -0
- package/dist/tools/getLocations.js +85 -0
- package/dist/tools/getMarkets.js +91 -0
- package/dist/tools/getMetafieldDefinitions.js +112 -0
- package/dist/tools/getMetafields.js +68 -0
- package/dist/tools/getOrderById.js +212 -0
- package/dist/tools/getOrderRefundDetails.js +131 -0
- package/dist/tools/getOrderTransactions.js +85 -0
- package/dist/tools/getOrders.js +148 -0
- package/dist/tools/getPriceLists.js +92 -0
- package/dist/tools/getProductById.js +171 -0
- package/dist/tools/getProductVariantsDetailed.js +139 -0
- package/dist/tools/getProducts.js +155 -0
- package/dist/tools/getShopInfo.js +74 -0
- package/dist/tools/manageCustomerAddress.js +149 -0
- package/dist/tools/manageProductOptions.js +293 -0
- package/dist/tools/manageProductVariants.js +203 -0
- package/dist/tools/manageTags.js +79 -0
- package/dist/tools/mergeCustomers.js +74 -0
- package/dist/tools/orderCancel.js +77 -0
- package/dist/tools/orderCloseOpen.js +74 -0
- package/dist/tools/orderMarkAsPaid.js +51 -0
- package/dist/tools/registry.js +106 -0
- package/dist/tools/setInventoryQuantities.js +74 -0
- package/dist/tools/setMetafields.js +61 -0
- package/dist/tools/updateCustomer.js +119 -0
- package/dist/tools/updateOrder.js +131 -0
- package/dist/tools/updateProduct.js +132 -0
- package/package.json +66 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
// Input schema for creating a product
|
|
5
|
+
const CreateProductInputSchema = z.object({
|
|
6
|
+
title: z.string().min(1),
|
|
7
|
+
descriptionHtml: z.string().optional(),
|
|
8
|
+
handle: z.string().optional().describe("URL slug, e.g. 'black-sunglasses'. Auto-generated from title if omitted."),
|
|
9
|
+
vendor: z.string().optional(),
|
|
10
|
+
productType: z.string().optional(),
|
|
11
|
+
tags: z.array(z.string()).optional(),
|
|
12
|
+
status: z.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).default("DRAFT"),
|
|
13
|
+
seo: z
|
|
14
|
+
.object({
|
|
15
|
+
title: z.string().optional(),
|
|
16
|
+
description: z.string().optional(),
|
|
17
|
+
})
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("SEO title and description for search engines"),
|
|
20
|
+
metafields: z
|
|
21
|
+
.array(z.object({
|
|
22
|
+
namespace: z.string(),
|
|
23
|
+
key: z.string(),
|
|
24
|
+
value: z.string(),
|
|
25
|
+
type: z.string().describe("Metafield type, e.g. 'single_line_text_field', 'json', 'number_integer'"),
|
|
26
|
+
}))
|
|
27
|
+
.optional(),
|
|
28
|
+
productOptions: z
|
|
29
|
+
.array(z.object({
|
|
30
|
+
name: z.string().describe("Option name, e.g. 'Size' or 'Color'"),
|
|
31
|
+
values: z
|
|
32
|
+
.array(z.object({ name: z.string() }))
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Option values"),
|
|
35
|
+
}))
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Product options to create inline (max 3)"),
|
|
38
|
+
collectionsToJoin: z
|
|
39
|
+
.array(z.string())
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Collection GIDs to add the product to"),
|
|
42
|
+
});
|
|
43
|
+
// Will be initialized in index.ts
|
|
44
|
+
let shopifyClient;
|
|
45
|
+
const createProduct = {
|
|
46
|
+
name: "create-product",
|
|
47
|
+
description: "Create a new product. When using productOptions, Shopify registers all option values but only creates one default variant (first value of each option, price $0). Use manage-product-variants with strategy=REMOVE_STANDALONE_VARIANT afterward to create all real variants with prices.",
|
|
48
|
+
schema: CreateProductInputSchema,
|
|
49
|
+
// Add initialize method to set up the GraphQL client
|
|
50
|
+
initialize(client) {
|
|
51
|
+
shopifyClient = client;
|
|
52
|
+
},
|
|
53
|
+
execute: async (input) => {
|
|
54
|
+
try {
|
|
55
|
+
const query = gql `
|
|
56
|
+
#graphql
|
|
57
|
+
|
|
58
|
+
mutation productCreate($product: ProductCreateInput!) {
|
|
59
|
+
productCreate(product: $product) {
|
|
60
|
+
product {
|
|
61
|
+
id
|
|
62
|
+
title
|
|
63
|
+
handle
|
|
64
|
+
descriptionHtml
|
|
65
|
+
vendor
|
|
66
|
+
productType
|
|
67
|
+
status
|
|
68
|
+
tags
|
|
69
|
+
seo {
|
|
70
|
+
title
|
|
71
|
+
description
|
|
72
|
+
}
|
|
73
|
+
options {
|
|
74
|
+
id
|
|
75
|
+
name
|
|
76
|
+
values
|
|
77
|
+
}
|
|
78
|
+
metafields(first: 10) {
|
|
79
|
+
edges {
|
|
80
|
+
node {
|
|
81
|
+
id
|
|
82
|
+
namespace
|
|
83
|
+
key
|
|
84
|
+
value
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
userErrors {
|
|
90
|
+
field
|
|
91
|
+
message
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
const variables = {
|
|
97
|
+
product: input,
|
|
98
|
+
};
|
|
99
|
+
const data = (await shopifyClient.request(query, variables));
|
|
100
|
+
checkUserErrors(data.productCreate.userErrors, "create product");
|
|
101
|
+
const product = data.productCreate.product;
|
|
102
|
+
return {
|
|
103
|
+
product: {
|
|
104
|
+
id: product.id,
|
|
105
|
+
title: product.title,
|
|
106
|
+
handle: product.handle,
|
|
107
|
+
descriptionHtml: product.descriptionHtml,
|
|
108
|
+
vendor: product.vendor,
|
|
109
|
+
productType: product.productType,
|
|
110
|
+
status: product.status,
|
|
111
|
+
tags: product.tags,
|
|
112
|
+
seo: product.seo,
|
|
113
|
+
options: product.options,
|
|
114
|
+
metafields: product.metafields?.edges.map((e) => e.node) || [],
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
handleToolError("create product", error);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
export { createProduct };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const CreateRefundInputSchema = z.object({
|
|
5
|
+
orderId: z.string().describe("The order GID, e.g. gid://shopify/Order/123"),
|
|
6
|
+
refundLineItems: z
|
|
7
|
+
.array(z.object({
|
|
8
|
+
lineItemId: z.string().describe("The line item GID to refund"),
|
|
9
|
+
quantity: z.number().describe("Quantity to refund"),
|
|
10
|
+
restockType: z
|
|
11
|
+
.enum(["CANCEL", "NO_RESTOCK", "RETURN"])
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("How to restock: CANCEL (not yet fulfilled), RETURN (already fulfilled), NO_RESTOCK"),
|
|
14
|
+
locationId: z.string().optional().describe("Location GID for restocking"),
|
|
15
|
+
}))
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Line items to refund"),
|
|
18
|
+
shipping: z
|
|
19
|
+
.object({
|
|
20
|
+
amount: z.string().optional().describe("Shipping refund amount"),
|
|
21
|
+
fullRefund: z.boolean().optional().describe("Whether to fully refund shipping"),
|
|
22
|
+
})
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Shipping cost refund"),
|
|
25
|
+
note: z.string().optional().describe("Note attached to the refund"),
|
|
26
|
+
notify: z.boolean().optional().describe("Whether to send refund notification to customer"),
|
|
27
|
+
currency: z.string().optional().describe("Currency code if different from shop currency (presentment currency)"),
|
|
28
|
+
});
|
|
29
|
+
let shopifyClient;
|
|
30
|
+
const createRefund = {
|
|
31
|
+
name: "refund-create",
|
|
32
|
+
description: "Create a full or partial refund for an order with optional restocking and shipping refund.",
|
|
33
|
+
schema: CreateRefundInputSchema,
|
|
34
|
+
initialize(client) {
|
|
35
|
+
shopifyClient = client;
|
|
36
|
+
},
|
|
37
|
+
execute: async (input) => {
|
|
38
|
+
try {
|
|
39
|
+
const query = gql `
|
|
40
|
+
#graphql
|
|
41
|
+
|
|
42
|
+
mutation refundCreate($input: RefundInput!) {
|
|
43
|
+
refundCreate(input: $input) {
|
|
44
|
+
refund {
|
|
45
|
+
id
|
|
46
|
+
createdAt
|
|
47
|
+
note
|
|
48
|
+
totalRefundedSet {
|
|
49
|
+
shopMoney {
|
|
50
|
+
amount
|
|
51
|
+
currencyCode
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
refundLineItems(first: 20) {
|
|
55
|
+
edges {
|
|
56
|
+
node {
|
|
57
|
+
lineItem {
|
|
58
|
+
id
|
|
59
|
+
title
|
|
60
|
+
}
|
|
61
|
+
quantity
|
|
62
|
+
restockType
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
userErrors {
|
|
68
|
+
field
|
|
69
|
+
message
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
const refundInput = {
|
|
75
|
+
orderId: input.orderId,
|
|
76
|
+
};
|
|
77
|
+
if (input.refundLineItems) {
|
|
78
|
+
refundInput.refundLineItems = input.refundLineItems;
|
|
79
|
+
}
|
|
80
|
+
if (input.shipping) {
|
|
81
|
+
refundInput.shipping = input.shipping;
|
|
82
|
+
}
|
|
83
|
+
if (input.note) {
|
|
84
|
+
refundInput.note = input.note;
|
|
85
|
+
}
|
|
86
|
+
if (input.notify !== undefined) {
|
|
87
|
+
refundInput.notify = input.notify;
|
|
88
|
+
}
|
|
89
|
+
if (input.currency) {
|
|
90
|
+
refundInput.currency = input.currency;
|
|
91
|
+
}
|
|
92
|
+
const data = (await shopifyClient.request(query, { input: refundInput }));
|
|
93
|
+
checkUserErrors(data.refundCreate.userErrors, "create refund");
|
|
94
|
+
const refund = data.refundCreate.refund;
|
|
95
|
+
return {
|
|
96
|
+
refund: {
|
|
97
|
+
id: refund.id,
|
|
98
|
+
createdAt: refund.createdAt,
|
|
99
|
+
note: refund.note,
|
|
100
|
+
totalRefunded: refund.totalRefundedSet?.shopMoney,
|
|
101
|
+
lineItems: refund.refundLineItems.edges.map((e) => ({
|
|
102
|
+
lineItemId: e.node.lineItem.id,
|
|
103
|
+
title: e.node.lineItem.title,
|
|
104
|
+
quantity: e.node.quantity,
|
|
105
|
+
restockType: e.node.restockType,
|
|
106
|
+
})),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
handleToolError("create refund", error);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
export { createRefund };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
// Input schema for deleting a customer
|
|
5
|
+
const DeleteCustomerInputSchema = z.object({
|
|
6
|
+
id: z.string().regex(/^\d+$/, "Customer ID must be numeric")
|
|
7
|
+
});
|
|
8
|
+
// Will be initialized in index.ts
|
|
9
|
+
let shopifyClient;
|
|
10
|
+
const deleteCustomer = {
|
|
11
|
+
name: "delete-customer",
|
|
12
|
+
description: "Delete a customer",
|
|
13
|
+
schema: DeleteCustomerInputSchema,
|
|
14
|
+
// Add initialize method to set up the GraphQL client
|
|
15
|
+
initialize(client) {
|
|
16
|
+
shopifyClient = client;
|
|
17
|
+
},
|
|
18
|
+
execute: async (input) => {
|
|
19
|
+
try {
|
|
20
|
+
const { id } = input;
|
|
21
|
+
// Convert numeric ID to GID format
|
|
22
|
+
const customerGid = `gid://shopify/Customer/${id}`;
|
|
23
|
+
const query = gql `
|
|
24
|
+
#graphql
|
|
25
|
+
|
|
26
|
+
mutation customerDelete($input: CustomerDeleteInput!) {
|
|
27
|
+
customerDelete(input: $input) {
|
|
28
|
+
deletedCustomerId
|
|
29
|
+
userErrors {
|
|
30
|
+
field
|
|
31
|
+
message
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
const data = (await shopifyClient.request(query, {
|
|
37
|
+
input: { id: customerGid }
|
|
38
|
+
}));
|
|
39
|
+
checkUserErrors(data.customerDelete.userErrors, "delete customer");
|
|
40
|
+
return { deletedCustomerId: data.customerDelete.deletedCustomerId };
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
handleToolError("delete customer", error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export { deleteCustomer };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const DeleteMetafieldsInputSchema = z.object({
|
|
5
|
+
metafields: z
|
|
6
|
+
.array(z.object({
|
|
7
|
+
ownerId: z.string().describe("GID of the resource that owns the metafield"),
|
|
8
|
+
namespace: z.string().describe("Metafield namespace"),
|
|
9
|
+
key: z.string().describe("Metafield key"),
|
|
10
|
+
}))
|
|
11
|
+
.min(1)
|
|
12
|
+
.describe("Metafields to delete, identified by owner + namespace + key"),
|
|
13
|
+
});
|
|
14
|
+
let shopifyClient;
|
|
15
|
+
const deleteMetafields = {
|
|
16
|
+
name: "delete-metafields",
|
|
17
|
+
description: "Delete metafields from any Shopify resource by specifying owner ID, namespace, and key.",
|
|
18
|
+
schema: DeleteMetafieldsInputSchema,
|
|
19
|
+
initialize(client) {
|
|
20
|
+
shopifyClient = client;
|
|
21
|
+
},
|
|
22
|
+
execute: async (input) => {
|
|
23
|
+
try {
|
|
24
|
+
const query = gql `
|
|
25
|
+
#graphql
|
|
26
|
+
|
|
27
|
+
mutation metafieldsDelete($metafields: [MetafieldIdentifierInput!]!) {
|
|
28
|
+
metafieldsDelete(metafields: $metafields) {
|
|
29
|
+
deletedMetafields {
|
|
30
|
+
ownerId
|
|
31
|
+
namespace
|
|
32
|
+
key
|
|
33
|
+
}
|
|
34
|
+
userErrors {
|
|
35
|
+
field
|
|
36
|
+
message
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
const data = (await shopifyClient.request(query, {
|
|
42
|
+
metafields: input.metafields,
|
|
43
|
+
}));
|
|
44
|
+
checkUserErrors(data.metafieldsDelete.userErrors, "delete metafields");
|
|
45
|
+
return {
|
|
46
|
+
deletedMetafields: data.metafieldsDelete.deletedMetafields || [],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
handleToolError("delete metafields", error);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export { deleteMetafields };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
// Input schema for deleteProduct
|
|
5
|
+
const DeleteProductInputSchema = z.object({
|
|
6
|
+
id: z.string().min(1).describe("Shopify product GID, e.g. gid://shopify/Product/123"),
|
|
7
|
+
});
|
|
8
|
+
// Will be initialized in index.ts
|
|
9
|
+
let shopifyClient;
|
|
10
|
+
const deleteProduct = {
|
|
11
|
+
name: "delete-product",
|
|
12
|
+
description: "Delete a product",
|
|
13
|
+
schema: DeleteProductInputSchema,
|
|
14
|
+
initialize(client) {
|
|
15
|
+
shopifyClient = client;
|
|
16
|
+
},
|
|
17
|
+
execute: async (input) => {
|
|
18
|
+
try {
|
|
19
|
+
const query = gql `
|
|
20
|
+
#graphql
|
|
21
|
+
|
|
22
|
+
mutation productDelete($input: ProductDeleteInput!) {
|
|
23
|
+
productDelete(input: $input) {
|
|
24
|
+
deletedProductId
|
|
25
|
+
userErrors {
|
|
26
|
+
field
|
|
27
|
+
message
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
const data = (await shopifyClient.request(query, {
|
|
33
|
+
input: { id: input.id },
|
|
34
|
+
}));
|
|
35
|
+
checkUserErrors(data.productDelete.userErrors, "delete product");
|
|
36
|
+
return { deletedProductId: data.productDelete.deletedProductId };
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
handleToolError("delete product", error);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export { deleteProduct };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
// Input schema for deleteProductVariants
|
|
5
|
+
const DeleteProductVariantsInputSchema = z.object({
|
|
6
|
+
productId: z.string().min(1).describe("Shopify product GID"),
|
|
7
|
+
variantIds: z.array(z.string().min(1)).min(1).describe("Array of variant GIDs to delete"),
|
|
8
|
+
});
|
|
9
|
+
// Will be initialized in index.ts
|
|
10
|
+
let shopifyClient;
|
|
11
|
+
const deleteProductVariants = {
|
|
12
|
+
name: "delete-product-variants",
|
|
13
|
+
description: "Delete one or more variants from a product",
|
|
14
|
+
schema: DeleteProductVariantsInputSchema,
|
|
15
|
+
initialize(client) {
|
|
16
|
+
shopifyClient = client;
|
|
17
|
+
},
|
|
18
|
+
execute: async (input) => {
|
|
19
|
+
try {
|
|
20
|
+
const { productId, variantIds } = input;
|
|
21
|
+
const query = gql `
|
|
22
|
+
#graphql
|
|
23
|
+
|
|
24
|
+
mutation productVariantsBulkDelete(
|
|
25
|
+
$productId: ID!
|
|
26
|
+
$variantsIds: [ID!]!
|
|
27
|
+
) {
|
|
28
|
+
productVariantsBulkDelete(
|
|
29
|
+
productId: $productId
|
|
30
|
+
variantsIds: $variantsIds
|
|
31
|
+
) {
|
|
32
|
+
product {
|
|
33
|
+
id
|
|
34
|
+
title
|
|
35
|
+
variants(first: 20) {
|
|
36
|
+
edges {
|
|
37
|
+
node {
|
|
38
|
+
id
|
|
39
|
+
title
|
|
40
|
+
price
|
|
41
|
+
sku
|
|
42
|
+
selectedOptions {
|
|
43
|
+
name
|
|
44
|
+
value
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
userErrors {
|
|
51
|
+
field
|
|
52
|
+
message
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
const data = (await shopifyClient.request(query, {
|
|
58
|
+
productId,
|
|
59
|
+
variantsIds: variantIds,
|
|
60
|
+
}));
|
|
61
|
+
checkUserErrors(data.productVariantsBulkDelete.userErrors, "delete variants");
|
|
62
|
+
const product = data.productVariantsBulkDelete.product;
|
|
63
|
+
return {
|
|
64
|
+
product: {
|
|
65
|
+
id: product.id,
|
|
66
|
+
title: product.title,
|
|
67
|
+
remainingVariants: product.variants.edges.map((e) => ({
|
|
68
|
+
id: e.node.id,
|
|
69
|
+
title: e.node.title,
|
|
70
|
+
price: e.node.price,
|
|
71
|
+
sku: e.node.sku,
|
|
72
|
+
options: e.node.selectedOptions,
|
|
73
|
+
})),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
handleToolError("delete product variants", error);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
export { deleteProductVariants };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const GetCollectionByIdInputSchema = z.object({
|
|
5
|
+
collectionId: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe("The collection ID (e.g. gid://shopify/Collection/123 or just 123)"),
|
|
9
|
+
productsFirst: z
|
|
10
|
+
.number()
|
|
11
|
+
.min(0)
|
|
12
|
+
.max(100)
|
|
13
|
+
.default(25)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Number of products to include (default 25, max 100, 0 to skip products)"),
|
|
16
|
+
});
|
|
17
|
+
let shopifyClient;
|
|
18
|
+
const getCollectionById = {
|
|
19
|
+
name: "get-collection-by-id",
|
|
20
|
+
description: "Get a single collection with full details including products (paginated), rules for smart collections, SEO, and image",
|
|
21
|
+
schema: GetCollectionByIdInputSchema,
|
|
22
|
+
initialize(client) {
|
|
23
|
+
shopifyClient = client;
|
|
24
|
+
},
|
|
25
|
+
execute: async (input) => {
|
|
26
|
+
try {
|
|
27
|
+
const collectionId = input.collectionId.startsWith("gid://")
|
|
28
|
+
? input.collectionId
|
|
29
|
+
: `gid://shopify/Collection/${input.collectionId}`;
|
|
30
|
+
const productsFirst = input.productsFirst ?? 25;
|
|
31
|
+
const query = gql `
|
|
32
|
+
#graphql
|
|
33
|
+
|
|
34
|
+
query GetCollectionById($id: ID!, $productsFirst: Int!) {
|
|
35
|
+
collection(id: $id) {
|
|
36
|
+
id
|
|
37
|
+
title
|
|
38
|
+
handle
|
|
39
|
+
descriptionHtml
|
|
40
|
+
sortOrder
|
|
41
|
+
templateSuffix
|
|
42
|
+
updatedAt
|
|
43
|
+
productsCount {
|
|
44
|
+
count
|
|
45
|
+
}
|
|
46
|
+
ruleSet {
|
|
47
|
+
appliedDisjunctively
|
|
48
|
+
rules {
|
|
49
|
+
column
|
|
50
|
+
relation
|
|
51
|
+
condition
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
image {
|
|
55
|
+
url
|
|
56
|
+
altText
|
|
57
|
+
width
|
|
58
|
+
height
|
|
59
|
+
}
|
|
60
|
+
seo {
|
|
61
|
+
title
|
|
62
|
+
description
|
|
63
|
+
}
|
|
64
|
+
products(first: $productsFirst) {
|
|
65
|
+
edges {
|
|
66
|
+
node {
|
|
67
|
+
id
|
|
68
|
+
title
|
|
69
|
+
handle
|
|
70
|
+
status
|
|
71
|
+
vendor
|
|
72
|
+
productType
|
|
73
|
+
totalInventory
|
|
74
|
+
featuredMedia {
|
|
75
|
+
preview {
|
|
76
|
+
image {
|
|
77
|
+
url
|
|
78
|
+
altText
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
priceRangeV2 {
|
|
83
|
+
minVariantPrice {
|
|
84
|
+
amount
|
|
85
|
+
currencyCode
|
|
86
|
+
}
|
|
87
|
+
maxVariantPrice {
|
|
88
|
+
amount
|
|
89
|
+
currencyCode
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
pageInfo {
|
|
95
|
+
hasNextPage
|
|
96
|
+
endCursor
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
const data = await shopifyClient.request(query, {
|
|
103
|
+
id: collectionId,
|
|
104
|
+
productsFirst,
|
|
105
|
+
});
|
|
106
|
+
if (!data.collection) {
|
|
107
|
+
throw new Error(`Collection not found: ${collectionId}`);
|
|
108
|
+
}
|
|
109
|
+
const collection = {
|
|
110
|
+
...data.collection,
|
|
111
|
+
products: {
|
|
112
|
+
items: edgesToNodes(data.collection.products),
|
|
113
|
+
pageInfo: data.collection.products.pageInfo,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
return { collection };
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
handleToolError("fetch collection", error);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
export { getCollectionById };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const GetCollectionsInputSchema = z.object({
|
|
5
|
+
first: z
|
|
6
|
+
.number()
|
|
7
|
+
.min(1)
|
|
8
|
+
.max(100)
|
|
9
|
+
.default(25)
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Number of collections to return (default 25, max 100)"),
|
|
12
|
+
query: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Search query to filter collections (e.g. 'title:Summer' or 'collection_type:smart')"),
|
|
16
|
+
});
|
|
17
|
+
let shopifyClient;
|
|
18
|
+
const getCollections = {
|
|
19
|
+
name: "get-collections",
|
|
20
|
+
description: "Query collections (manual & smart) with optional filtering. Returns title, handle, products count, sort order, and rules for smart collections.",
|
|
21
|
+
schema: GetCollectionsInputSchema,
|
|
22
|
+
initialize(client) {
|
|
23
|
+
shopifyClient = client;
|
|
24
|
+
},
|
|
25
|
+
execute: async (input) => {
|
|
26
|
+
try {
|
|
27
|
+
const query = gql `
|
|
28
|
+
#graphql
|
|
29
|
+
|
|
30
|
+
query GetCollections($first: Int!, $query: String) {
|
|
31
|
+
collections(first: $first, query: $query) {
|
|
32
|
+
edges {
|
|
33
|
+
node {
|
|
34
|
+
id
|
|
35
|
+
title
|
|
36
|
+
handle
|
|
37
|
+
description
|
|
38
|
+
sortOrder
|
|
39
|
+
productsCount {
|
|
40
|
+
count
|
|
41
|
+
}
|
|
42
|
+
templateSuffix
|
|
43
|
+
updatedAt
|
|
44
|
+
ruleSet {
|
|
45
|
+
appliedDisjunctively
|
|
46
|
+
rules {
|
|
47
|
+
column
|
|
48
|
+
relation
|
|
49
|
+
condition
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
image {
|
|
53
|
+
url
|
|
54
|
+
altText
|
|
55
|
+
}
|
|
56
|
+
seo {
|
|
57
|
+
title
|
|
58
|
+
description
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
pageInfo {
|
|
63
|
+
hasNextPage
|
|
64
|
+
endCursor
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
const variables = {
|
|
70
|
+
first: input.first ?? 25,
|
|
71
|
+
};
|
|
72
|
+
if (input.query) {
|
|
73
|
+
variables.query = input.query;
|
|
74
|
+
}
|
|
75
|
+
const data = await shopifyClient.request(query, variables);
|
|
76
|
+
const collections = edgesToNodes(data.collections);
|
|
77
|
+
return {
|
|
78
|
+
collectionsCount: collections.length,
|
|
79
|
+
collections,
|
|
80
|
+
pageInfo: data.collections.pageInfo,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
handleToolError("fetch collections", error);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
export { getCollections };
|