@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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +531 -0
  3. package/dist/index.js +107 -0
  4. package/dist/lib/formatters.js +72 -0
  5. package/dist/lib/shopifyAuth.js +96 -0
  6. package/dist/lib/toolUtils.js +36 -0
  7. package/dist/tools/completeDraftOrder.js +76 -0
  8. package/dist/tools/createCustomer.js +142 -0
  9. package/dist/tools/createDraftOrder.js +160 -0
  10. package/dist/tools/createFulfillment.js +100 -0
  11. package/dist/tools/createProduct.js +123 -0
  12. package/dist/tools/createRefund.js +115 -0
  13. package/dist/tools/deleteCustomer.js +47 -0
  14. package/dist/tools/deleteMetafields.js +54 -0
  15. package/dist/tools/deleteProduct.js +43 -0
  16. package/dist/tools/deleteProductVariants.js +82 -0
  17. package/dist/tools/getCollectionById.js +123 -0
  18. package/dist/tools/getCollections.js +88 -0
  19. package/dist/tools/getCustomerById.js +122 -0
  20. package/dist/tools/getCustomerOrders.js +131 -0
  21. package/dist/tools/getCustomers.js +125 -0
  22. package/dist/tools/getFulfillmentOrders.js +106 -0
  23. package/dist/tools/getInventoryItems.js +86 -0
  24. package/dist/tools/getInventoryLevels.js +82 -0
  25. package/dist/tools/getLocations.js +85 -0
  26. package/dist/tools/getMarkets.js +91 -0
  27. package/dist/tools/getMetafieldDefinitions.js +112 -0
  28. package/dist/tools/getMetafields.js +68 -0
  29. package/dist/tools/getOrderById.js +212 -0
  30. package/dist/tools/getOrderRefundDetails.js +131 -0
  31. package/dist/tools/getOrderTransactions.js +85 -0
  32. package/dist/tools/getOrders.js +148 -0
  33. package/dist/tools/getPriceLists.js +92 -0
  34. package/dist/tools/getProductById.js +171 -0
  35. package/dist/tools/getProductVariantsDetailed.js +139 -0
  36. package/dist/tools/getProducts.js +155 -0
  37. package/dist/tools/getShopInfo.js +74 -0
  38. package/dist/tools/manageCustomerAddress.js +149 -0
  39. package/dist/tools/manageProductOptions.js +293 -0
  40. package/dist/tools/manageProductVariants.js +203 -0
  41. package/dist/tools/manageTags.js +79 -0
  42. package/dist/tools/mergeCustomers.js +74 -0
  43. package/dist/tools/orderCancel.js +77 -0
  44. package/dist/tools/orderCloseOpen.js +74 -0
  45. package/dist/tools/orderMarkAsPaid.js +51 -0
  46. package/dist/tools/registry.js +106 -0
  47. package/dist/tools/setInventoryQuantities.js +74 -0
  48. package/dist/tools/setMetafields.js +61 -0
  49. package/dist/tools/updateCustomer.js +119 -0
  50. package/dist/tools/updateOrder.js +131 -0
  51. package/dist/tools/updateProduct.js +132 -0
  52. 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 };