@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,122 @@
1
+ import { gql } from "graphql-request";
2
+ import { z } from "zod";
3
+ import { handleToolError, edgesToNodes } from "../lib/toolUtils.js";
4
+ // Input schema for getting a customer by ID
5
+ const GetCustomerByIdInputSchema = 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 getCustomerById = {
11
+ name: "get-customer-by-id",
12
+ description: "Get a single customer by ID",
13
+ schema: GetCustomerByIdInputSchema,
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
+ query GetCustomerById($id: ID!) {
27
+ customer(id: $id) {
28
+ id
29
+ firstName
30
+ lastName
31
+ defaultEmailAddress {
32
+ emailAddress
33
+ }
34
+ defaultPhoneNumber {
35
+ phoneNumber
36
+ }
37
+ createdAt
38
+ updatedAt
39
+ tags
40
+ note
41
+ taxExempt
42
+ defaultAddress {
43
+ address1
44
+ address2
45
+ city
46
+ provinceCode
47
+ zip
48
+ country
49
+ phone
50
+ }
51
+ addressesV2(first: 10) {
52
+ edges {
53
+ node {
54
+ address1
55
+ address2
56
+ city
57
+ provinceCode
58
+ zip
59
+ country
60
+ phone
61
+ }
62
+ }
63
+ }
64
+ amountSpent {
65
+ amount
66
+ currencyCode
67
+ }
68
+ numberOfOrders
69
+ metafields(first: 10) {
70
+ edges {
71
+ node {
72
+ id
73
+ namespace
74
+ key
75
+ value
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ `;
82
+ const variables = {
83
+ id: customerGid
84
+ };
85
+ const data = (await shopifyClient.request(query, variables));
86
+ if (!data.customer) {
87
+ throw new Error(`Customer with ID ${id} not found`);
88
+ }
89
+ const customer = data.customer;
90
+ // Format metafields if they exist
91
+ const metafields = customer.metafields
92
+ ? edgesToNodes(customer.metafields)
93
+ : [];
94
+ const addresses = customer.addressesV2
95
+ ? edgesToNodes(customer.addressesV2)
96
+ : [];
97
+ return {
98
+ customer: {
99
+ id: customer.id,
100
+ firstName: customer.firstName,
101
+ lastName: customer.lastName,
102
+ email: customer.defaultEmailAddress?.emailAddress || null,
103
+ phone: customer.defaultPhoneNumber?.phoneNumber || null,
104
+ createdAt: customer.createdAt,
105
+ updatedAt: customer.updatedAt,
106
+ tags: customer.tags,
107
+ note: customer.note,
108
+ taxExempt: customer.taxExempt,
109
+ defaultAddress: customer.defaultAddress,
110
+ addresses,
111
+ amountSpent: customer.amountSpent,
112
+ numberOfOrders: customer.numberOfOrders,
113
+ metafields
114
+ }
115
+ };
116
+ }
117
+ catch (error) {
118
+ handleToolError("fetch customer", error);
119
+ }
120
+ }
121
+ };
122
+ export { getCustomerById };
@@ -0,0 +1,131 @@
1
+ import { gql } from "graphql-request";
2
+ import { z } from "zod";
3
+ import { handleToolError, edgesToNodes } from "../lib/toolUtils.js";
4
+ import { formatOrderSummary } from "../lib/formatters.js";
5
+ // Input schema for getting customer orders
6
+ const GetCustomerOrdersInputSchema = z.object({
7
+ customerId: z.string().regex(/^\d+$/, "Customer ID must be numeric"),
8
+ limit: z.number().default(10),
9
+ after: z.string().optional().describe("Cursor for forward pagination"),
10
+ before: z.string().optional().describe("Cursor for backward pagination"),
11
+ sortKey: z.enum([
12
+ "CREATED_AT", "ORDER_NUMBER", "TOTAL_PRICE", "FINANCIAL_STATUS",
13
+ "FULFILLMENT_STATUS", "UPDATED_AT", "CUSTOMER_NAME", "PROCESSED_AT",
14
+ "ID", "RELEVANCE"
15
+ ]).optional().describe("Sort key for orders"),
16
+ reverse: z.boolean().optional().describe("Reverse the sort order")
17
+ });
18
+ // Will be initialized in index.ts
19
+ let shopifyClient;
20
+ const getCustomerOrders = {
21
+ name: "get-customer-orders",
22
+ description: "Get orders for a specific customer",
23
+ schema: GetCustomerOrdersInputSchema,
24
+ // Add initialize method to set up the GraphQL client
25
+ initialize(client) {
26
+ shopifyClient = client;
27
+ },
28
+ execute: async (input) => {
29
+ try {
30
+ const { customerId, limit, after, before, sortKey, reverse } = input;
31
+ // Query to get orders for a specific customer
32
+ const query = gql `
33
+ #graphql
34
+
35
+ query GetCustomerOrders($query: String!, $first: Int!, $after: String, $before: String, $sortKey: OrderSortKeys, $reverse: Boolean) {
36
+ orders(query: $query, first: $first, after: $after, before: $before, sortKey: $sortKey, reverse: $reverse) {
37
+ edges {
38
+ node {
39
+ id
40
+ name
41
+ createdAt
42
+ displayFinancialStatus
43
+ displayFulfillmentStatus
44
+ totalPriceSet {
45
+ shopMoney {
46
+ amount
47
+ currencyCode
48
+ }
49
+ }
50
+ subtotalPriceSet {
51
+ shopMoney {
52
+ amount
53
+ currencyCode
54
+ }
55
+ }
56
+ totalShippingPriceSet {
57
+ shopMoney {
58
+ amount
59
+ currencyCode
60
+ }
61
+ }
62
+ totalTaxSet {
63
+ shopMoney {
64
+ amount
65
+ currencyCode
66
+ }
67
+ }
68
+ customer {
69
+ id
70
+ firstName
71
+ lastName
72
+ defaultEmailAddress {
73
+ emailAddress
74
+ }
75
+ }
76
+ lineItems(first: 5) {
77
+ edges {
78
+ node {
79
+ id
80
+ title
81
+ quantity
82
+ originalTotalSet {
83
+ shopMoney {
84
+ amount
85
+ currencyCode
86
+ }
87
+ }
88
+ variant {
89
+ id
90
+ title
91
+ sku
92
+ }
93
+ }
94
+ }
95
+ }
96
+ tags
97
+ note
98
+ }
99
+ }
100
+ pageInfo {
101
+ hasNextPage
102
+ hasPreviousPage
103
+ startCursor
104
+ endCursor
105
+ }
106
+ }
107
+ }
108
+ `;
109
+ // We use the query parameter to filter orders by customer ID
110
+ const variables = {
111
+ query: `customer_id:${customerId}`,
112
+ first: limit,
113
+ ...(after && { after }),
114
+ ...(before && { before }),
115
+ ...(sortKey && { sortKey }),
116
+ ...(reverse !== undefined && { reverse })
117
+ };
118
+ const data = (await shopifyClient.request(query, variables));
119
+ // Extract and format order data
120
+ const orders = edgesToNodes(data.orders).map(formatOrderSummary);
121
+ return {
122
+ orders,
123
+ pageInfo: data.orders.pageInfo
124
+ };
125
+ }
126
+ catch (error) {
127
+ handleToolError("fetch customer orders", error);
128
+ }
129
+ }
130
+ };
131
+ export { getCustomerOrders };
@@ -0,0 +1,125 @@
1
+ import { gql } from "graphql-request";
2
+ import { z } from "zod";
3
+ import { handleToolError, edgesToNodes } from "../lib/toolUtils.js";
4
+ // Input schema for getCustomers
5
+ const GetCustomersInputSchema = z.object({
6
+ searchQuery: z.string().optional().describe("Freetext search or Shopify query syntax (e.g. 'country:US tag:vip orders_count:>5')"),
7
+ limit: z.number().default(10),
8
+ after: z.string().optional().describe("Cursor for forward pagination"),
9
+ before: z.string().optional().describe("Cursor for backward pagination"),
10
+ sortKey: z.enum([
11
+ "CREATED_AT", "ID", "LAST_UPDATE", "LOCATION", "NAME",
12
+ "ORDERS_COUNT", "RELEVANCE", "TOTAL_SPENT", "UPDATED_AT"
13
+ ]).optional().describe("Sort key for customers"),
14
+ reverse: z.boolean().optional().describe("Reverse the sort order")
15
+ });
16
+ // Will be initialized in index.ts
17
+ let shopifyClient;
18
+ const getCustomers = {
19
+ name: "get-customers",
20
+ description: "Get customers or search by name/email",
21
+ schema: GetCustomersInputSchema,
22
+ // Add initialize method to set up the GraphQL client
23
+ initialize(client) {
24
+ shopifyClient = client;
25
+ },
26
+ execute: async (input) => {
27
+ try {
28
+ const { searchQuery, limit, after, before, sortKey, reverse } = input;
29
+ const query = gql `
30
+ #graphql
31
+
32
+ query GetCustomers($first: Int!, $query: String, $after: String, $before: String, $sortKey: CustomerSortKeys, $reverse: Boolean) {
33
+ customers(first: $first, query: $query, after: $after, before: $before, sortKey: $sortKey, reverse: $reverse) {
34
+ edges {
35
+ node {
36
+ id
37
+ firstName
38
+ lastName
39
+ defaultEmailAddress {
40
+ emailAddress
41
+ }
42
+ defaultPhoneNumber {
43
+ phoneNumber
44
+ }
45
+ createdAt
46
+ updatedAt
47
+ tags
48
+ defaultAddress {
49
+ address1
50
+ address2
51
+ city
52
+ provinceCode
53
+ zip
54
+ country
55
+ phone
56
+ }
57
+ addressesV2(first: 10) {
58
+ edges {
59
+ node {
60
+ address1
61
+ address2
62
+ city
63
+ provinceCode
64
+ zip
65
+ country
66
+ phone
67
+ }
68
+ }
69
+ }
70
+ amountSpent {
71
+ amount
72
+ currencyCode
73
+ }
74
+ numberOfOrders
75
+ }
76
+ }
77
+ pageInfo {
78
+ hasNextPage
79
+ hasPreviousPage
80
+ startCursor
81
+ endCursor
82
+ }
83
+ }
84
+ }
85
+ `;
86
+ const variables = {
87
+ first: limit,
88
+ query: searchQuery,
89
+ ...(after && { after }),
90
+ ...(before && { before }),
91
+ ...(sortKey && { sortKey }),
92
+ ...(reverse !== undefined && { reverse })
93
+ };
94
+ const data = (await shopifyClient.request(query, variables));
95
+ // Extract and format customer data
96
+ const customers = data.customers.edges.map((edge) => {
97
+ const customer = edge.node;
98
+ return {
99
+ id: customer.id,
100
+ firstName: customer.firstName,
101
+ lastName: customer.lastName,
102
+ email: customer.defaultEmailAddress?.emailAddress || null,
103
+ phone: customer.defaultPhoneNumber?.phoneNumber || null,
104
+ createdAt: customer.createdAt,
105
+ updatedAt: customer.updatedAt,
106
+ tags: customer.tags,
107
+ defaultAddress: customer.defaultAddress,
108
+ addresses: customer.addressesV2
109
+ ? edgesToNodes(customer.addressesV2)
110
+ : [],
111
+ amountSpent: customer.amountSpent,
112
+ numberOfOrders: customer.numberOfOrders
113
+ };
114
+ });
115
+ return {
116
+ customers,
117
+ pageInfo: data.customers.pageInfo
118
+ };
119
+ }
120
+ catch (error) {
121
+ handleToolError("fetch customers", error);
122
+ }
123
+ }
124
+ };
125
+ export { getCustomers };
@@ -0,0 +1,106 @@
1
+ import { gql } from "graphql-request";
2
+ import { z } from "zod";
3
+ import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
4
+ const GetFulfillmentOrdersInputSchema = z.object({
5
+ orderId: z
6
+ .string()
7
+ .min(1)
8
+ .describe("The order ID (e.g. gid://shopify/Order/123 or just 123)"),
9
+ });
10
+ let shopifyClient;
11
+ const getFulfillmentOrders = {
12
+ name: "get-fulfillment-orders",
13
+ description: "Get fulfillment orders for an order including status, assigned location, delivery method, holds, and line items",
14
+ schema: GetFulfillmentOrdersInputSchema,
15
+ initialize(client) {
16
+ shopifyClient = client;
17
+ },
18
+ execute: async (input) => {
19
+ try {
20
+ const orderId = input.orderId.startsWith("gid://")
21
+ ? input.orderId
22
+ : `gid://shopify/Order/${input.orderId}`;
23
+ const query = gql `
24
+ #graphql
25
+
26
+ query GetFulfillmentOrders($id: ID!) {
27
+ order(id: $id) {
28
+ id
29
+ name
30
+ fulfillmentOrders(first: 25) {
31
+ edges {
32
+ node {
33
+ id
34
+ status
35
+ requestStatus
36
+ createdAt
37
+ updatedAt
38
+ fulfillAt
39
+ fulfillBy
40
+ assignedLocation {
41
+ name
42
+ address1
43
+ address2
44
+ city
45
+ province
46
+ countryCode
47
+ zip
48
+ phone
49
+ }
50
+ deliveryMethod {
51
+ methodType
52
+ minDeliveryDateTime
53
+ maxDeliveryDateTime
54
+ presentedName
55
+ serviceCode
56
+ }
57
+ fulfillmentHolds {
58
+ id
59
+ reason
60
+ reasonNotes
61
+ displayReason
62
+ heldByRequestingApp
63
+ }
64
+ lineItems(first: 50) {
65
+ edges {
66
+ node {
67
+ id
68
+ totalQuantity
69
+ remainingQuantity
70
+ productTitle
71
+ variantTitle
72
+ sku
73
+ }
74
+ }
75
+ }
76
+ supportedActions {
77
+ action
78
+ externalUrl
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ `;
86
+ const data = await shopifyClient.request(query, { id: orderId });
87
+ if (!data.order) {
88
+ throw new Error(`Order not found: ${orderId}`);
89
+ }
90
+ const fulfillmentOrders = edgesToNodes(data.order.fulfillmentOrders).map((fo) => ({
91
+ ...fo,
92
+ lineItems: edgesToNodes(fo.lineItems),
93
+ }));
94
+ return {
95
+ orderId: data.order.id,
96
+ orderName: data.order.name,
97
+ fulfillmentOrdersCount: fulfillmentOrders.length,
98
+ fulfillmentOrders,
99
+ };
100
+ }
101
+ catch (error) {
102
+ handleToolError("fetch fulfillment orders", error);
103
+ }
104
+ },
105
+ };
106
+ export { getFulfillmentOrders };
@@ -0,0 +1,86 @@
1
+ import { gql } from "graphql-request";
2
+ import { z } from "zod";
3
+ import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
4
+ const GetInventoryItemsInputSchema = z.object({
5
+ productId: z
6
+ .string()
7
+ .min(1)
8
+ .describe("The product ID (e.g. gid://shopify/Product/123 or just 123)"),
9
+ });
10
+ let shopifyClient;
11
+ const getInventoryItems = {
12
+ name: "get-inventory-items",
13
+ description: "Get inventory item details for all variants of a product including SKU, cost, tracked status, country of origin, and HS codes",
14
+ schema: GetInventoryItemsInputSchema,
15
+ initialize(client) {
16
+ shopifyClient = client;
17
+ },
18
+ execute: async (input) => {
19
+ try {
20
+ const productId = input.productId.startsWith("gid://")
21
+ ? input.productId
22
+ : `gid://shopify/Product/${input.productId}`;
23
+ const query = gql `
24
+ #graphql
25
+
26
+ query GetInventoryItems($id: ID!) {
27
+ product(id: $id) {
28
+ id
29
+ title
30
+ variants(first: 100) {
31
+ edges {
32
+ node {
33
+ id
34
+ title
35
+ sku
36
+ inventoryItem {
37
+ id
38
+ sku
39
+ tracked
40
+ requiresShipping
41
+ unitCost {
42
+ amount
43
+ currencyCode
44
+ }
45
+ countryCodeOfOrigin
46
+ provinceCodeOfOrigin
47
+ harmonizedSystemCode
48
+ measurement {
49
+ weight {
50
+ unit
51
+ value
52
+ }
53
+ }
54
+ locationsCount {
55
+ count
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ `;
64
+ const data = await shopifyClient.request(query, { id: productId });
65
+ if (!data.product) {
66
+ throw new Error(`Product not found: ${productId}`);
67
+ }
68
+ const variants = edgesToNodes(data.product.variants).map((variant) => ({
69
+ variantId: variant.id,
70
+ variantTitle: variant.title,
71
+ variantSku: variant.sku,
72
+ inventoryItem: variant.inventoryItem,
73
+ }));
74
+ return {
75
+ productId: data.product.id,
76
+ productTitle: data.product.title,
77
+ variantsCount: variants.length,
78
+ variants,
79
+ };
80
+ }
81
+ catch (error) {
82
+ handleToolError("fetch inventory items", error);
83
+ }
84
+ },
85
+ };
86
+ export { getInventoryItems };
@@ -0,0 +1,82 @@
1
+ import { gql } from "graphql-request";
2
+ import { z } from "zod";
3
+ import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
4
+ const GetInventoryLevelsInputSchema = z.object({
5
+ inventoryItemId: z
6
+ .string()
7
+ .min(1)
8
+ .describe("The inventory item ID (e.g. gid://shopify/InventoryItem/123 or just 123). Get this from getInventoryItems or product variant data."),
9
+ });
10
+ let shopifyClient;
11
+ const getInventoryLevels = {
12
+ name: "get-inventory-levels",
13
+ description: "Get inventory quantities per location for an inventory item (available, on_hand, committed, reserved, incoming, damaged, etc.)",
14
+ schema: GetInventoryLevelsInputSchema,
15
+ initialize(client) {
16
+ shopifyClient = client;
17
+ },
18
+ execute: async (input) => {
19
+ try {
20
+ const inventoryItemId = input.inventoryItemId.startsWith("gid://")
21
+ ? input.inventoryItemId
22
+ : `gid://shopify/InventoryItem/${input.inventoryItemId}`;
23
+ const query = gql `
24
+ #graphql
25
+
26
+ query GetInventoryLevels($id: ID!) {
27
+ inventoryItem(id: $id) {
28
+ id
29
+ sku
30
+ tracked
31
+ inventoryLevels(first: 50) {
32
+ edges {
33
+ node {
34
+ id
35
+ location {
36
+ id
37
+ name
38
+ isActive
39
+ }
40
+ quantities(
41
+ names: [
42
+ "available"
43
+ "on_hand"
44
+ "committed"
45
+ "reserved"
46
+ "incoming"
47
+ "damaged"
48
+ "quality_control"
49
+ "safety_stock"
50
+ ]
51
+ ) {
52
+ name
53
+ quantity
54
+ }
55
+ updatedAt
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ `;
62
+ const data = await shopifyClient.request(query, {
63
+ id: inventoryItemId,
64
+ });
65
+ if (!data.inventoryItem) {
66
+ throw new Error(`Inventory item not found: ${inventoryItemId}`);
67
+ }
68
+ const levels = edgesToNodes(data.inventoryItem.inventoryLevels);
69
+ return {
70
+ inventoryItemId: data.inventoryItem.id,
71
+ sku: data.inventoryItem.sku,
72
+ tracked: data.inventoryItem.tracked,
73
+ levelsCount: levels.length,
74
+ levels,
75
+ };
76
+ }
77
+ catch (error) {
78
+ handleToolError("fetch inventory levels", error);
79
+ }
80
+ },
81
+ };
82
+ export { getInventoryLevels };