@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,74 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const SetInventoryQuantitiesInputSchema = z.object({
|
|
5
|
+
reason: z.string().describe("Reason for the quantity change (e.g. 'correction', 'cycle_count_available', 'received')"),
|
|
6
|
+
name: z.enum(["available", "on_hand"]).describe("Which quantity to set: 'available' or 'on_hand'"),
|
|
7
|
+
quantities: z
|
|
8
|
+
.array(z.object({
|
|
9
|
+
inventoryItemId: z.string().describe("Inventory item GID"),
|
|
10
|
+
locationId: z.string().describe("Location GID"),
|
|
11
|
+
quantity: z.number().describe("Absolute quantity to set"),
|
|
12
|
+
}))
|
|
13
|
+
.min(1)
|
|
14
|
+
.describe("Quantities to set for each inventory item at each location"),
|
|
15
|
+
ignoreCompareQuantity: z.boolean().default(true).describe("Skip compare-and-set check (default true for simplicity)"),
|
|
16
|
+
});
|
|
17
|
+
let shopifyClient;
|
|
18
|
+
const setInventoryQuantities = {
|
|
19
|
+
name: "inventory-set-quantities",
|
|
20
|
+
description: "Set absolute inventory quantities for items at specific locations. Use for inventory corrections, cycle counts, etc.",
|
|
21
|
+
schema: SetInventoryQuantitiesInputSchema,
|
|
22
|
+
initialize(client) {
|
|
23
|
+
shopifyClient = client;
|
|
24
|
+
},
|
|
25
|
+
execute: async (input) => {
|
|
26
|
+
try {
|
|
27
|
+
const query = gql `
|
|
28
|
+
#graphql
|
|
29
|
+
|
|
30
|
+
mutation inventorySetQuantities($input: InventorySetQuantitiesInput!) {
|
|
31
|
+
inventorySetQuantities(input: $input) {
|
|
32
|
+
inventoryAdjustmentGroup {
|
|
33
|
+
reason
|
|
34
|
+
changes {
|
|
35
|
+
name
|
|
36
|
+
delta
|
|
37
|
+
quantityAfterChange
|
|
38
|
+
item {
|
|
39
|
+
id
|
|
40
|
+
sku
|
|
41
|
+
}
|
|
42
|
+
location {
|
|
43
|
+
id
|
|
44
|
+
name
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
userErrors {
|
|
49
|
+
field
|
|
50
|
+
message
|
|
51
|
+
code
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
const data = (await shopifyClient.request(query, {
|
|
57
|
+
input: {
|
|
58
|
+
reason: input.reason,
|
|
59
|
+
name: input.name,
|
|
60
|
+
ignoreCompareQuantity: input.ignoreCompareQuantity,
|
|
61
|
+
quantities: input.quantities,
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
64
|
+
checkUserErrors(data.inventorySetQuantities.userErrors, "set inventory quantities");
|
|
65
|
+
return {
|
|
66
|
+
adjustmentGroup: data.inventorySetQuantities.inventoryAdjustmentGroup,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
handleToolError("set inventory quantities", error);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
export { setInventoryQuantities };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const SetMetafieldsInputSchema = z.object({
|
|
5
|
+
metafields: z
|
|
6
|
+
.array(z.object({
|
|
7
|
+
ownerId: z.string().describe("GID of the resource (product, order, customer, variant, etc.)"),
|
|
8
|
+
namespace: z.string().optional().describe("Metafield namespace. If omitted, app-reserved namespace is used."),
|
|
9
|
+
key: z.string().describe("Unique identifier within its namespace (2-64 chars)"),
|
|
10
|
+
value: z.string().describe("The value to set (always stored as string)"),
|
|
11
|
+
type: z.string().optional().describe("Metafield type, e.g. 'single_line_text_field', 'json', 'number_integer'. Required if no definition exists."),
|
|
12
|
+
}))
|
|
13
|
+
.min(1)
|
|
14
|
+
.max(25)
|
|
15
|
+
.describe("Metafields to set (max 25). Works on any resource: products, orders, customers, variants, collections, etc."),
|
|
16
|
+
});
|
|
17
|
+
let shopifyClient;
|
|
18
|
+
const setMetafields = {
|
|
19
|
+
name: "set-metafields",
|
|
20
|
+
description: "Set metafields on any Shopify resource (products, orders, customers, variants, collections, etc.). Creates or updates up to 25 metafields atomically.",
|
|
21
|
+
schema: SetMetafieldsInputSchema,
|
|
22
|
+
initialize(client) {
|
|
23
|
+
shopifyClient = client;
|
|
24
|
+
},
|
|
25
|
+
execute: async (input) => {
|
|
26
|
+
try {
|
|
27
|
+
const query = gql `
|
|
28
|
+
#graphql
|
|
29
|
+
|
|
30
|
+
mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
|
|
31
|
+
metafieldsSet(metafields: $metafields) {
|
|
32
|
+
metafields {
|
|
33
|
+
id
|
|
34
|
+
namespace
|
|
35
|
+
key
|
|
36
|
+
value
|
|
37
|
+
type
|
|
38
|
+
ownerType
|
|
39
|
+
}
|
|
40
|
+
userErrors {
|
|
41
|
+
field
|
|
42
|
+
message
|
|
43
|
+
code
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
const data = (await shopifyClient.request(query, {
|
|
49
|
+
metafields: input.metafields,
|
|
50
|
+
}));
|
|
51
|
+
checkUserErrors(data.metafieldsSet.userErrors, "set metafields");
|
|
52
|
+
return {
|
|
53
|
+
metafields: data.metafieldsSet.metafields || [],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
handleToolError("set metafields", error);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
export { setMetafields };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
// Input schema for updating a customer
|
|
5
|
+
const UpdateCustomerInputSchema = z.object({
|
|
6
|
+
id: z.string().regex(/^\d+$/, "Customer ID must be numeric"),
|
|
7
|
+
firstName: z.string().optional(),
|
|
8
|
+
lastName: z.string().optional(),
|
|
9
|
+
email: z.string().email().optional(),
|
|
10
|
+
phone: z.string().optional(),
|
|
11
|
+
tags: z.array(z.string()).optional(),
|
|
12
|
+
note: z.string().optional(),
|
|
13
|
+
emailMarketingConsent: z
|
|
14
|
+
.object({
|
|
15
|
+
marketingState: z.enum(["NOT_SUBSCRIBED", "SUBSCRIBED", "UNSUBSCRIBED", "PENDING"]),
|
|
16
|
+
consentUpdatedAt: z.string().optional(),
|
|
17
|
+
marketingOptInLevel: z.enum(["SINGLE_OPT_IN", "CONFIRMED_OPT_IN", "UNKNOWN"]).optional()
|
|
18
|
+
})
|
|
19
|
+
.optional(),
|
|
20
|
+
taxExempt: z.boolean().optional(),
|
|
21
|
+
metafields: z
|
|
22
|
+
.array(z.object({
|
|
23
|
+
id: z.string().optional(),
|
|
24
|
+
namespace: z.string().optional(),
|
|
25
|
+
key: z.string().optional(),
|
|
26
|
+
value: z.string(),
|
|
27
|
+
type: z.string().optional()
|
|
28
|
+
}))
|
|
29
|
+
.optional()
|
|
30
|
+
});
|
|
31
|
+
// Will be initialized in index.ts
|
|
32
|
+
let shopifyClient;
|
|
33
|
+
const updateCustomer = {
|
|
34
|
+
name: "update-customer",
|
|
35
|
+
description: "Update a customer's information",
|
|
36
|
+
schema: UpdateCustomerInputSchema,
|
|
37
|
+
// Add initialize method to set up the GraphQL client
|
|
38
|
+
initialize(client) {
|
|
39
|
+
shopifyClient = client;
|
|
40
|
+
},
|
|
41
|
+
execute: async (input) => {
|
|
42
|
+
try {
|
|
43
|
+
const { id, ...customerFields } = input;
|
|
44
|
+
// Convert numeric ID to GID format
|
|
45
|
+
const customerGid = `gid://shopify/Customer/${id}`;
|
|
46
|
+
const query = gql `
|
|
47
|
+
#graphql
|
|
48
|
+
|
|
49
|
+
mutation customerUpdate($input: CustomerInput!) {
|
|
50
|
+
customerUpdate(input: $input) {
|
|
51
|
+
customer {
|
|
52
|
+
id
|
|
53
|
+
firstName
|
|
54
|
+
lastName
|
|
55
|
+
defaultEmailAddress {
|
|
56
|
+
emailAddress
|
|
57
|
+
}
|
|
58
|
+
defaultPhoneNumber {
|
|
59
|
+
phoneNumber
|
|
60
|
+
}
|
|
61
|
+
tags
|
|
62
|
+
note
|
|
63
|
+
taxExempt
|
|
64
|
+
emailMarketingConsent {
|
|
65
|
+
marketingState
|
|
66
|
+
consentUpdatedAt
|
|
67
|
+
marketingOptInLevel
|
|
68
|
+
}
|
|
69
|
+
metafields(first: 10) {
|
|
70
|
+
edges {
|
|
71
|
+
node {
|
|
72
|
+
id
|
|
73
|
+
namespace
|
|
74
|
+
key
|
|
75
|
+
value
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
userErrors {
|
|
81
|
+
field
|
|
82
|
+
message
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
const variables = {
|
|
88
|
+
input: {
|
|
89
|
+
id: customerGid,
|
|
90
|
+
...customerFields
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const data = (await shopifyClient.request(query, variables));
|
|
94
|
+
checkUserErrors(data.customerUpdate.userErrors, "update customer");
|
|
95
|
+
// Format and return the updated customer
|
|
96
|
+
const customer = data.customerUpdate.customer;
|
|
97
|
+
// Format metafields if they exist
|
|
98
|
+
const metafields = customer.metafields?.edges.map((edge) => edge.node) || [];
|
|
99
|
+
return {
|
|
100
|
+
customer: {
|
|
101
|
+
id: customer.id,
|
|
102
|
+
firstName: customer.firstName,
|
|
103
|
+
lastName: customer.lastName,
|
|
104
|
+
email: customer.defaultEmailAddress?.emailAddress || null,
|
|
105
|
+
phone: customer.defaultPhoneNumber?.phoneNumber || null,
|
|
106
|
+
tags: customer.tags,
|
|
107
|
+
note: customer.note,
|
|
108
|
+
taxExempt: customer.taxExempt,
|
|
109
|
+
emailMarketingConsent: customer.emailMarketingConsent,
|
|
110
|
+
metafields
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
handleToolError("update customer", error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
export { updateCustomer };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
// Will be initialized in index.ts
|
|
5
|
+
let shopifyClient;
|
|
6
|
+
// Input schema for updateOrder
|
|
7
|
+
// Based on https://shopify.dev/docs/api/admin-graphql/latest/mutations/orderupdate
|
|
8
|
+
const UpdateOrderInputSchema = z.object({
|
|
9
|
+
id: z.string().min(1),
|
|
10
|
+
tags: z.array(z.string()).optional(),
|
|
11
|
+
email: z.string().email().optional(),
|
|
12
|
+
note: z.string().optional(),
|
|
13
|
+
customAttributes: z
|
|
14
|
+
.array(z.object({
|
|
15
|
+
key: z.string(),
|
|
16
|
+
value: z.string()
|
|
17
|
+
}))
|
|
18
|
+
.optional(),
|
|
19
|
+
metafields: z
|
|
20
|
+
.array(z.object({
|
|
21
|
+
id: z.string().optional(),
|
|
22
|
+
namespace: z.string().optional(),
|
|
23
|
+
key: z.string().optional(),
|
|
24
|
+
value: z.string(),
|
|
25
|
+
type: z.string().optional()
|
|
26
|
+
}))
|
|
27
|
+
.optional(),
|
|
28
|
+
phone: z.string().optional(),
|
|
29
|
+
poNumber: z.string().optional(),
|
|
30
|
+
shippingAddress: z
|
|
31
|
+
.object({
|
|
32
|
+
address1: z.string().optional(),
|
|
33
|
+
address2: z.string().optional(),
|
|
34
|
+
city: z.string().optional(),
|
|
35
|
+
company: z.string().optional(),
|
|
36
|
+
countryCode: z.string().optional(),
|
|
37
|
+
firstName: z.string().optional(),
|
|
38
|
+
lastName: z.string().optional(),
|
|
39
|
+
phone: z.string().optional(),
|
|
40
|
+
provinceCode: z.string().optional(),
|
|
41
|
+
zip: z.string().optional()
|
|
42
|
+
})
|
|
43
|
+
.optional()
|
|
44
|
+
});
|
|
45
|
+
const updateOrder = {
|
|
46
|
+
name: "update-order",
|
|
47
|
+
description: "Update an existing order with new information",
|
|
48
|
+
schema: UpdateOrderInputSchema,
|
|
49
|
+
// Add initialize method to set up the GraphQL client
|
|
50
|
+
initialize(client) {
|
|
51
|
+
shopifyClient = client;
|
|
52
|
+
},
|
|
53
|
+
execute: async (input) => {
|
|
54
|
+
try {
|
|
55
|
+
// Prepare input for GraphQL mutation
|
|
56
|
+
const { id, ...orderFields } = input;
|
|
57
|
+
const query = gql `
|
|
58
|
+
#graphql
|
|
59
|
+
|
|
60
|
+
mutation orderUpdate($input: OrderInput!) {
|
|
61
|
+
orderUpdate(input: $input) {
|
|
62
|
+
order {
|
|
63
|
+
id
|
|
64
|
+
name
|
|
65
|
+
email
|
|
66
|
+
note
|
|
67
|
+
tags
|
|
68
|
+
customAttributes {
|
|
69
|
+
key
|
|
70
|
+
value
|
|
71
|
+
}
|
|
72
|
+
metafields(first: 10) {
|
|
73
|
+
edges {
|
|
74
|
+
node {
|
|
75
|
+
id
|
|
76
|
+
namespace
|
|
77
|
+
key
|
|
78
|
+
value
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
shippingAddress {
|
|
83
|
+
address1
|
|
84
|
+
address2
|
|
85
|
+
city
|
|
86
|
+
company
|
|
87
|
+
country
|
|
88
|
+
firstName
|
|
89
|
+
lastName
|
|
90
|
+
phone
|
|
91
|
+
province
|
|
92
|
+
zip
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
userErrors {
|
|
96
|
+
field
|
|
97
|
+
message
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
const variables = {
|
|
103
|
+
input: {
|
|
104
|
+
id,
|
|
105
|
+
...orderFields
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const data = (await shopifyClient.request(query, variables));
|
|
109
|
+
checkUserErrors(data.orderUpdate.userErrors, "update order");
|
|
110
|
+
// Format and return the updated order
|
|
111
|
+
const order = data.orderUpdate.order;
|
|
112
|
+
// Return the updated order data
|
|
113
|
+
return {
|
|
114
|
+
order: {
|
|
115
|
+
id: order.id,
|
|
116
|
+
name: order.name,
|
|
117
|
+
email: order.email,
|
|
118
|
+
note: order.note,
|
|
119
|
+
tags: order.tags,
|
|
120
|
+
customAttributes: order.customAttributes,
|
|
121
|
+
metafields: order.metafields?.edges.map((edge) => edge.node) || [],
|
|
122
|
+
shippingAddress: order.shippingAddress
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
handleToolError("update order", error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
export { updateOrder };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { checkUserErrors, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
// Input schema for updateProduct
|
|
5
|
+
const UpdateProductInputSchema = z.object({
|
|
6
|
+
id: z.string().min(1).describe("Shopify product GID, e.g. gid://shopify/Product/123"),
|
|
7
|
+
title: z.string().optional(),
|
|
8
|
+
descriptionHtml: z.string().optional(),
|
|
9
|
+
handle: z.string().optional().describe("URL slug for the product"),
|
|
10
|
+
vendor: z.string().optional(),
|
|
11
|
+
productType: z.string().optional(),
|
|
12
|
+
tags: z.array(z.string()).optional(),
|
|
13
|
+
status: z.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional(),
|
|
14
|
+
seo: z
|
|
15
|
+
.object({
|
|
16
|
+
title: z.string().optional(),
|
|
17
|
+
description: z.string().optional(),
|
|
18
|
+
})
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("SEO title and description for search engines"),
|
|
21
|
+
metafields: z
|
|
22
|
+
.array(z.object({
|
|
23
|
+
id: z.string().optional(),
|
|
24
|
+
namespace: z.string().optional(),
|
|
25
|
+
key: z.string().optional(),
|
|
26
|
+
value: z.string(),
|
|
27
|
+
type: z.string().optional(),
|
|
28
|
+
}))
|
|
29
|
+
.optional(),
|
|
30
|
+
collectionsToJoin: z.array(z.string()).optional().describe("Collection GIDs to add the product to"),
|
|
31
|
+
collectionsToLeave: z.array(z.string()).optional().describe("Collection GIDs to remove the product from"),
|
|
32
|
+
redirectNewHandle: z.boolean().optional().describe("If true, old handle redirects to new handle"),
|
|
33
|
+
});
|
|
34
|
+
// Will be initialized in index.ts
|
|
35
|
+
let shopifyClient;
|
|
36
|
+
const updateProduct = {
|
|
37
|
+
name: "update-product",
|
|
38
|
+
description: "Update an existing product's fields (title, description, status, tags, etc.)",
|
|
39
|
+
schema: UpdateProductInputSchema,
|
|
40
|
+
initialize(client) {
|
|
41
|
+
shopifyClient = client;
|
|
42
|
+
},
|
|
43
|
+
execute: async (input) => {
|
|
44
|
+
try {
|
|
45
|
+
const { id, ...productFields } = input;
|
|
46
|
+
const query = gql `
|
|
47
|
+
#graphql
|
|
48
|
+
|
|
49
|
+
mutation productUpdate($product: ProductUpdateInput!) {
|
|
50
|
+
productUpdate(product: $product) {
|
|
51
|
+
product {
|
|
52
|
+
id
|
|
53
|
+
title
|
|
54
|
+
handle
|
|
55
|
+
descriptionHtml
|
|
56
|
+
vendor
|
|
57
|
+
productType
|
|
58
|
+
status
|
|
59
|
+
tags
|
|
60
|
+
seo {
|
|
61
|
+
title
|
|
62
|
+
description
|
|
63
|
+
}
|
|
64
|
+
metafields(first: 10) {
|
|
65
|
+
edges {
|
|
66
|
+
node {
|
|
67
|
+
id
|
|
68
|
+
namespace
|
|
69
|
+
key
|
|
70
|
+
value
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
variants(first: 20) {
|
|
75
|
+
edges {
|
|
76
|
+
node {
|
|
77
|
+
id
|
|
78
|
+
title
|
|
79
|
+
price
|
|
80
|
+
sku
|
|
81
|
+
selectedOptions {
|
|
82
|
+
name
|
|
83
|
+
value
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
userErrors {
|
|
90
|
+
field
|
|
91
|
+
message
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
const variables = {
|
|
97
|
+
product: {
|
|
98
|
+
id,
|
|
99
|
+
...productFields,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
const data = (await shopifyClient.request(query, variables));
|
|
103
|
+
checkUserErrors(data.productUpdate.userErrors, "update product");
|
|
104
|
+
const product = data.productUpdate.product;
|
|
105
|
+
return {
|
|
106
|
+
product: {
|
|
107
|
+
id: product.id,
|
|
108
|
+
title: product.title,
|
|
109
|
+
handle: product.handle,
|
|
110
|
+
descriptionHtml: product.descriptionHtml,
|
|
111
|
+
vendor: product.vendor,
|
|
112
|
+
productType: product.productType,
|
|
113
|
+
status: product.status,
|
|
114
|
+
tags: product.tags,
|
|
115
|
+
seo: product.seo,
|
|
116
|
+
metafields: product.metafields?.edges.map((e) => e.node) || [],
|
|
117
|
+
variants: product.variants?.edges.map((e) => ({
|
|
118
|
+
id: e.node.id,
|
|
119
|
+
title: e.node.title,
|
|
120
|
+
price: e.node.price,
|
|
121
|
+
sku: e.node.sku,
|
|
122
|
+
options: e.node.selectedOptions,
|
|
123
|
+
})) || [],
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
handleToolError("update product", error);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
export { updateProduct };
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@klaudworks/shopify-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP Server for Shopify API, enabling interaction with store data through GraphQL API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": "dist/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public",
|
|
10
|
+
"provenance": true
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "rimraf dist && tsc",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"dev": "node --loader ts-node/esm src/index.ts",
|
|
16
|
+
"test": "echo '(no tests yet — placeholder so CI stays green; swap in node --test when tests are added)' && exit 0",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"clean": "rimraf dist"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"shopify",
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"graphql",
|
|
25
|
+
"ai",
|
|
26
|
+
"llm",
|
|
27
|
+
"claude"
|
|
28
|
+
],
|
|
29
|
+
"author": {
|
|
30
|
+
"name": "Your Name",
|
|
31
|
+
"email": "your.email@example.com",
|
|
32
|
+
"url": "https://your-website.com"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
|
+
"dotenv": "^16.0.3",
|
|
38
|
+
"graphql": "^16.6.0",
|
|
39
|
+
"graphql-request": "^7.4.0",
|
|
40
|
+
"minimist": "^1.2.8",
|
|
41
|
+
"zod": "^3.21.4"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/minimist": "^1.2.2",
|
|
45
|
+
"@types/node": "^22.10.0",
|
|
46
|
+
"rimraf": "^5.0.10",
|
|
47
|
+
"ts-node": "^10.9.1",
|
|
48
|
+
"typescript": "^5.0.4"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE"
|
|
54
|
+
],
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/klaudworks/shopify-mcp.git"
|
|
58
|
+
},
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/klaudworks/shopify-mcp/issues"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/klaudworks/shopify-mcp#readme",
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|