@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,85 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const GetLocationsInputSchema = z.object({
|
|
5
|
+
includeInactive: z
|
|
6
|
+
.boolean()
|
|
7
|
+
.default(false)
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("Whether to include deactivated locations (default false)"),
|
|
10
|
+
first: z
|
|
11
|
+
.number()
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(100)
|
|
14
|
+
.default(50)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Number of locations to return (default 50, max 100)"),
|
|
17
|
+
});
|
|
18
|
+
let shopifyClient;
|
|
19
|
+
const getLocations = {
|
|
20
|
+
name: "get-locations",
|
|
21
|
+
description: "Get all inventory/fulfillment locations with addresses, capabilities, and active status",
|
|
22
|
+
schema: GetLocationsInputSchema,
|
|
23
|
+
initialize(client) {
|
|
24
|
+
shopifyClient = client;
|
|
25
|
+
},
|
|
26
|
+
execute: async (input) => {
|
|
27
|
+
try {
|
|
28
|
+
const query = gql `
|
|
29
|
+
#graphql
|
|
30
|
+
|
|
31
|
+
query GetLocations($first: Int!, $includeInactive: Boolean!) {
|
|
32
|
+
locations(first: $first, includeInactive: $includeInactive) {
|
|
33
|
+
edges {
|
|
34
|
+
node {
|
|
35
|
+
id
|
|
36
|
+
name
|
|
37
|
+
isActive
|
|
38
|
+
isFulfillmentService
|
|
39
|
+
fulfillsOnlineOrders
|
|
40
|
+
shipsInventory
|
|
41
|
+
hasActiveInventory
|
|
42
|
+
hasUnfulfilledOrders
|
|
43
|
+
address {
|
|
44
|
+
address1
|
|
45
|
+
address2
|
|
46
|
+
city
|
|
47
|
+
province
|
|
48
|
+
provinceCode
|
|
49
|
+
country
|
|
50
|
+
countryCode
|
|
51
|
+
zip
|
|
52
|
+
phone
|
|
53
|
+
latitude
|
|
54
|
+
longitude
|
|
55
|
+
}
|
|
56
|
+
fulfillmentService {
|
|
57
|
+
serviceName
|
|
58
|
+
handle
|
|
59
|
+
}
|
|
60
|
+
localPickupSettingsV2 {
|
|
61
|
+
instructions
|
|
62
|
+
pickupTime
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
const variables = {
|
|
70
|
+
first: input.first ?? 50,
|
|
71
|
+
includeInactive: input.includeInactive ?? false,
|
|
72
|
+
};
|
|
73
|
+
const data = await shopifyClient.request(query, variables);
|
|
74
|
+
const locations = edgesToNodes(data.locations);
|
|
75
|
+
return {
|
|
76
|
+
locationsCount: locations.length,
|
|
77
|
+
locations,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
handleToolError("fetch locations", error);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
export { getLocations };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const GetMarketsInputSchema = z.object({
|
|
5
|
+
first: z
|
|
6
|
+
.number()
|
|
7
|
+
.min(1)
|
|
8
|
+
.max(50)
|
|
9
|
+
.default(25)
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Number of markets to return (default 25, max 50)"),
|
|
12
|
+
});
|
|
13
|
+
let shopifyClient;
|
|
14
|
+
const getMarkets = {
|
|
15
|
+
name: "get-markets",
|
|
16
|
+
description: "Get all markets with their regions, currencies, status, and web presence configuration",
|
|
17
|
+
schema: GetMarketsInputSchema,
|
|
18
|
+
initialize(client) {
|
|
19
|
+
shopifyClient = client;
|
|
20
|
+
},
|
|
21
|
+
execute: async (input) => {
|
|
22
|
+
try {
|
|
23
|
+
const query = gql `
|
|
24
|
+
#graphql
|
|
25
|
+
|
|
26
|
+
query GetMarkets($first: Int!) {
|
|
27
|
+
markets(first: $first) {
|
|
28
|
+
edges {
|
|
29
|
+
node {
|
|
30
|
+
id
|
|
31
|
+
name
|
|
32
|
+
handle
|
|
33
|
+
status
|
|
34
|
+
type
|
|
35
|
+
currencySettings {
|
|
36
|
+
baseCurrency {
|
|
37
|
+
currencyCode
|
|
38
|
+
currencyName
|
|
39
|
+
}
|
|
40
|
+
localCurrencies
|
|
41
|
+
}
|
|
42
|
+
webPresences(first: 10) {
|
|
43
|
+
edges {
|
|
44
|
+
node {
|
|
45
|
+
id
|
|
46
|
+
subfolderSuffix
|
|
47
|
+
defaultLocale {
|
|
48
|
+
locale
|
|
49
|
+
name
|
|
50
|
+
primary
|
|
51
|
+
published
|
|
52
|
+
}
|
|
53
|
+
alternateLocales {
|
|
54
|
+
locale
|
|
55
|
+
name
|
|
56
|
+
published
|
|
57
|
+
}
|
|
58
|
+
domain {
|
|
59
|
+
id
|
|
60
|
+
host
|
|
61
|
+
url
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
const variables = {
|
|
72
|
+
first: input.first ?? 25,
|
|
73
|
+
};
|
|
74
|
+
const data = await shopifyClient.request(query, variables);
|
|
75
|
+
const markets = edgesToNodes(data.markets).map((market) => ({
|
|
76
|
+
...market,
|
|
77
|
+
webPresences: market.webPresences
|
|
78
|
+
? edgesToNodes(market.webPresences)
|
|
79
|
+
: [],
|
|
80
|
+
}));
|
|
81
|
+
return {
|
|
82
|
+
marketsCount: markets.length,
|
|
83
|
+
markets,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
handleToolError("fetch markets", error);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
export { getMarkets };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
/** Map common underscore aliases to their correct Shopify API enum values */
|
|
5
|
+
const OWNER_TYPE_NORMALIZE = {
|
|
6
|
+
PRODUCT_VARIANT: "PRODUCTVARIANT",
|
|
7
|
+
DRAFT_ORDER: "DRAFTORDER",
|
|
8
|
+
CART_TRANSFORM: "CARTTRANSFORM",
|
|
9
|
+
};
|
|
10
|
+
const GetMetafieldDefinitionsInputSchema = z.object({
|
|
11
|
+
ownerType: z
|
|
12
|
+
.enum([
|
|
13
|
+
"API_PERMISSION",
|
|
14
|
+
"ARTICLE",
|
|
15
|
+
"BLOG",
|
|
16
|
+
"CART_TRANSFORM",
|
|
17
|
+
"CARTTRANSFORM",
|
|
18
|
+
"COLLECTION",
|
|
19
|
+
"COMPANY",
|
|
20
|
+
"COMPANY_LOCATION",
|
|
21
|
+
"CUSTOMER",
|
|
22
|
+
"DELIVERY_CUSTOMIZATION",
|
|
23
|
+
"DISCOUNT",
|
|
24
|
+
"DRAFT_ORDER",
|
|
25
|
+
"DRAFTORDER",
|
|
26
|
+
"FULFILLMENT_CONSTRAINT_RULE",
|
|
27
|
+
"GIFT_CARD_TRANSACTION",
|
|
28
|
+
"LOCATION",
|
|
29
|
+
"MARKET",
|
|
30
|
+
"MEDIA_IMAGE",
|
|
31
|
+
"ORDER",
|
|
32
|
+
"ORDER_ROUTING_LOCATION_RULE",
|
|
33
|
+
"PAGE",
|
|
34
|
+
"PAYMENT_CUSTOMIZATION",
|
|
35
|
+
"PRODUCT",
|
|
36
|
+
"PRODUCT_VARIANT",
|
|
37
|
+
"PRODUCTVARIANT",
|
|
38
|
+
"SELLING_PLAN",
|
|
39
|
+
"SHOP",
|
|
40
|
+
"VALIDATION",
|
|
41
|
+
])
|
|
42
|
+
.describe("The resource type to get metafield definitions for (e.g. PRODUCT, ORDER, CUSTOMER). " +
|
|
43
|
+
"Note: Some Shopify types use concatenated names without underscores " +
|
|
44
|
+
"(PRODUCTVARIANT not PRODUCT_VARIANT, DRAFTORDER not DRAFT_ORDER, CARTTRANSFORM not CART_TRANSFORM). " +
|
|
45
|
+
"Underscore aliases are accepted and normalized automatically."),
|
|
46
|
+
first: z
|
|
47
|
+
.number()
|
|
48
|
+
.min(1)
|
|
49
|
+
.max(100)
|
|
50
|
+
.default(50)
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Number of definitions to return (default 50, max 100)"),
|
|
53
|
+
});
|
|
54
|
+
let shopifyClient;
|
|
55
|
+
const getMetafieldDefinitions = {
|
|
56
|
+
name: "get-metafield-definitions",
|
|
57
|
+
description: "Discover custom metafield definitions for any resource type (PRODUCT, ORDER, CUSTOMER, etc.). Returns namespace, key, name, type, and validations.",
|
|
58
|
+
schema: GetMetafieldDefinitionsInputSchema,
|
|
59
|
+
initialize(client) {
|
|
60
|
+
shopifyClient = client;
|
|
61
|
+
},
|
|
62
|
+
execute: async (input) => {
|
|
63
|
+
try {
|
|
64
|
+
const query = gql `
|
|
65
|
+
#graphql
|
|
66
|
+
|
|
67
|
+
query GetMetafieldDefinitions(
|
|
68
|
+
$ownerType: MetafieldOwnerType!
|
|
69
|
+
$first: Int!
|
|
70
|
+
) {
|
|
71
|
+
metafieldDefinitions(ownerType: $ownerType, first: $first) {
|
|
72
|
+
edges {
|
|
73
|
+
node {
|
|
74
|
+
id
|
|
75
|
+
namespace
|
|
76
|
+
key
|
|
77
|
+
name
|
|
78
|
+
description
|
|
79
|
+
ownerType
|
|
80
|
+
pinnedPosition
|
|
81
|
+
type {
|
|
82
|
+
name
|
|
83
|
+
category
|
|
84
|
+
}
|
|
85
|
+
validations {
|
|
86
|
+
name
|
|
87
|
+
type
|
|
88
|
+
value
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
const variables = {
|
|
96
|
+
ownerType: OWNER_TYPE_NORMALIZE[input.ownerType] ?? input.ownerType,
|
|
97
|
+
first: input.first ?? 50,
|
|
98
|
+
};
|
|
99
|
+
const data = await shopifyClient.request(query, variables);
|
|
100
|
+
const definitions = edgesToNodes(data.metafieldDefinitions);
|
|
101
|
+
return {
|
|
102
|
+
ownerType: input.ownerType,
|
|
103
|
+
definitionsCount: definitions.length,
|
|
104
|
+
definitions,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
handleToolError("fetch metafield definitions", error);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
export { getMetafieldDefinitions };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const GetMetafieldsInputSchema = z.object({
|
|
5
|
+
ownerId: z.string().describe("GID of the resource (product, order, customer, variant, collection, etc.)"),
|
|
6
|
+
namespace: z.string().optional().describe("Filter metafields by namespace"),
|
|
7
|
+
first: z.number().default(25).describe("Number of metafields to return (max 50)"),
|
|
8
|
+
after: z.string().optional().describe("Cursor for pagination"),
|
|
9
|
+
});
|
|
10
|
+
let shopifyClient;
|
|
11
|
+
const getMetafields = {
|
|
12
|
+
name: "get-metafields",
|
|
13
|
+
description: "Get metafields for any Shopify resource (products, orders, customers, variants, collections, etc.). Uses the node query with HasMetafields interface.",
|
|
14
|
+
schema: GetMetafieldsInputSchema,
|
|
15
|
+
initialize(client) {
|
|
16
|
+
shopifyClient = client;
|
|
17
|
+
},
|
|
18
|
+
execute: async (input) => {
|
|
19
|
+
try {
|
|
20
|
+
const query = gql `
|
|
21
|
+
#graphql
|
|
22
|
+
|
|
23
|
+
query GetMetafields($ownerId: ID!, $first: Int!, $namespace: String, $after: String) {
|
|
24
|
+
node(id: $ownerId) {
|
|
25
|
+
... on HasMetafields {
|
|
26
|
+
metafields(first: $first, namespace: $namespace, after: $after) {
|
|
27
|
+
edges {
|
|
28
|
+
node {
|
|
29
|
+
id
|
|
30
|
+
namespace
|
|
31
|
+
key
|
|
32
|
+
value
|
|
33
|
+
type
|
|
34
|
+
updatedAt
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
pageInfo {
|
|
38
|
+
hasNextPage
|
|
39
|
+
endCursor
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
const data = (await shopifyClient.request(query, {
|
|
47
|
+
ownerId: input.ownerId,
|
|
48
|
+
first: input.first,
|
|
49
|
+
...(input.namespace && { namespace: input.namespace }),
|
|
50
|
+
...(input.after && { after: input.after }),
|
|
51
|
+
}));
|
|
52
|
+
if (!data.node) {
|
|
53
|
+
throw new Error(`Resource with ID ${input.ownerId} not found`);
|
|
54
|
+
}
|
|
55
|
+
if (!data.node.metafields) {
|
|
56
|
+
throw new Error(`Resource ${input.ownerId} does not support metafields`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
metafields: data.node.metafields.edges.map((e) => e.node),
|
|
60
|
+
pageInfo: data.node.metafields.pageInfo,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
handleToolError("fetch metafields", error);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
export { getMetafields };
|
|
@@ -0,0 +1,212 @@
|
|
|
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 getOrderById
|
|
6
|
+
const GetOrderByIdInputSchema = z.object({
|
|
7
|
+
orderId: z.string().min(1)
|
|
8
|
+
});
|
|
9
|
+
// Will be initialized in index.ts
|
|
10
|
+
let shopifyClient;
|
|
11
|
+
const getOrderById = {
|
|
12
|
+
name: "get-order-by-id",
|
|
13
|
+
description: "Get a specific order by ID",
|
|
14
|
+
schema: GetOrderByIdInputSchema,
|
|
15
|
+
// Add initialize method to set up the GraphQL client
|
|
16
|
+
initialize(client) {
|
|
17
|
+
shopifyClient = client;
|
|
18
|
+
},
|
|
19
|
+
execute: async (input) => {
|
|
20
|
+
try {
|
|
21
|
+
const { orderId } = input;
|
|
22
|
+
// Smart lookup: detect format and resolve to GID
|
|
23
|
+
let resolvedId;
|
|
24
|
+
const trimmed = orderId.trim();
|
|
25
|
+
if (trimmed.startsWith("gid://")) {
|
|
26
|
+
// Already a full GID
|
|
27
|
+
resolvedId = trimmed;
|
|
28
|
+
}
|
|
29
|
+
else if (/^#?\d{1,9}$/.test(trimmed)) {
|
|
30
|
+
// Short number or #number — treat as order name, query by name
|
|
31
|
+
const orderName = trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
32
|
+
const nameQuery = gql `
|
|
33
|
+
#graphql
|
|
34
|
+
|
|
35
|
+
query FindOrderByName($query: String!) {
|
|
36
|
+
orders(first: 1, query: $query) {
|
|
37
|
+
edges {
|
|
38
|
+
node {
|
|
39
|
+
id
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
const nameData = (await shopifyClient.request(nameQuery, {
|
|
46
|
+
query: `name:${orderName}`,
|
|
47
|
+
}));
|
|
48
|
+
if (nameData.orders.edges.length === 0) {
|
|
49
|
+
throw new Error(`Order with name ${orderName} not found`);
|
|
50
|
+
}
|
|
51
|
+
resolvedId = nameData.orders.edges[0].node.id;
|
|
52
|
+
}
|
|
53
|
+
else if (/^\d+$/.test(trimmed)) {
|
|
54
|
+
// Long numeric ID — convert to GID
|
|
55
|
+
resolvedId = `gid://shopify/Order/${trimmed}`;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Unknown format — try as-is
|
|
59
|
+
resolvedId = trimmed;
|
|
60
|
+
}
|
|
61
|
+
const query = gql `
|
|
62
|
+
#graphql
|
|
63
|
+
|
|
64
|
+
query GetOrderById($id: ID!) {
|
|
65
|
+
order(id: $id) {
|
|
66
|
+
id
|
|
67
|
+
name
|
|
68
|
+
createdAt
|
|
69
|
+
displayFinancialStatus
|
|
70
|
+
displayFulfillmentStatus
|
|
71
|
+
totalPriceSet {
|
|
72
|
+
shopMoney {
|
|
73
|
+
amount
|
|
74
|
+
currencyCode
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
subtotalPriceSet {
|
|
78
|
+
shopMoney {
|
|
79
|
+
amount
|
|
80
|
+
currencyCode
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
totalShippingPriceSet {
|
|
84
|
+
shopMoney {
|
|
85
|
+
amount
|
|
86
|
+
currencyCode
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
totalTaxSet {
|
|
90
|
+
shopMoney {
|
|
91
|
+
amount
|
|
92
|
+
currencyCode
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
customer {
|
|
96
|
+
id
|
|
97
|
+
firstName
|
|
98
|
+
lastName
|
|
99
|
+
defaultEmailAddress {
|
|
100
|
+
emailAddress
|
|
101
|
+
}
|
|
102
|
+
defaultPhoneNumber {
|
|
103
|
+
phoneNumber
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
shippingAddress {
|
|
107
|
+
address1
|
|
108
|
+
address2
|
|
109
|
+
city
|
|
110
|
+
provinceCode
|
|
111
|
+
zip
|
|
112
|
+
country
|
|
113
|
+
phone
|
|
114
|
+
}
|
|
115
|
+
lineItems(first: 20) {
|
|
116
|
+
edges {
|
|
117
|
+
node {
|
|
118
|
+
id
|
|
119
|
+
title
|
|
120
|
+
quantity
|
|
121
|
+
originalTotalSet {
|
|
122
|
+
shopMoney {
|
|
123
|
+
amount
|
|
124
|
+
currencyCode
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
variant {
|
|
128
|
+
id
|
|
129
|
+
title
|
|
130
|
+
sku
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
tags
|
|
136
|
+
note
|
|
137
|
+
billingAddress {
|
|
138
|
+
address1
|
|
139
|
+
address2
|
|
140
|
+
city
|
|
141
|
+
provinceCode
|
|
142
|
+
zip
|
|
143
|
+
country
|
|
144
|
+
company
|
|
145
|
+
phone
|
|
146
|
+
firstName
|
|
147
|
+
lastName
|
|
148
|
+
}
|
|
149
|
+
cancelReason
|
|
150
|
+
cancelledAt
|
|
151
|
+
updatedAt
|
|
152
|
+
returnStatus
|
|
153
|
+
processedAt
|
|
154
|
+
poNumber
|
|
155
|
+
discountCodes
|
|
156
|
+
currentTotalPriceSet {
|
|
157
|
+
shopMoney {
|
|
158
|
+
amount
|
|
159
|
+
currencyCode
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
metafields(first: 20) {
|
|
163
|
+
edges {
|
|
164
|
+
node {
|
|
165
|
+
id
|
|
166
|
+
namespace
|
|
167
|
+
key
|
|
168
|
+
value
|
|
169
|
+
type
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
`;
|
|
176
|
+
const variables = {
|
|
177
|
+
id: resolvedId
|
|
178
|
+
};
|
|
179
|
+
const data = (await shopifyClient.request(query, variables));
|
|
180
|
+
if (!data.order) {
|
|
181
|
+
throw new Error(`Order with ID ${orderId} not found`);
|
|
182
|
+
}
|
|
183
|
+
// Extract and format order data
|
|
184
|
+
const order = data.order;
|
|
185
|
+
const base = formatOrderSummary(order);
|
|
186
|
+
const formattedOrder = {
|
|
187
|
+
...base,
|
|
188
|
+
customer: order.customer
|
|
189
|
+
? {
|
|
190
|
+
...base.customer,
|
|
191
|
+
phone: order.customer.defaultPhoneNumber?.phoneNumber || null,
|
|
192
|
+
}
|
|
193
|
+
: null,
|
|
194
|
+
billingAddress: order.billingAddress,
|
|
195
|
+
cancelReason: order.cancelReason,
|
|
196
|
+
cancelledAt: order.cancelledAt,
|
|
197
|
+
updatedAt: order.updatedAt,
|
|
198
|
+
returnStatus: order.returnStatus,
|
|
199
|
+
processedAt: order.processedAt,
|
|
200
|
+
poNumber: order.poNumber,
|
|
201
|
+
discountCodes: order.discountCodes,
|
|
202
|
+
currentTotalPrice: order.currentTotalPriceSet?.shopMoney,
|
|
203
|
+
metafields: edgesToNodes(order.metafields),
|
|
204
|
+
};
|
|
205
|
+
return { order: formattedOrder };
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
handleToolError("fetch order", error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
export { getOrderById };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { gql } from "graphql-request";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { edgesToNodes, handleToolError } from "../lib/toolUtils.js";
|
|
4
|
+
const GetOrderRefundDetailsInputSchema = 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 getOrderRefundDetails = {
|
|
12
|
+
name: "get-order-refund-details",
|
|
13
|
+
description: "Get detailed refund info for an order including refunded items, amounts, restock status, and associated transactions",
|
|
14
|
+
schema: GetOrderRefundDetailsInputSchema,
|
|
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 GetOrderRefundDetails($id: ID!) {
|
|
27
|
+
order(id: $id) {
|
|
28
|
+
id
|
|
29
|
+
name
|
|
30
|
+
refunds(first: 25) {
|
|
31
|
+
id
|
|
32
|
+
note
|
|
33
|
+
createdAt
|
|
34
|
+
updatedAt
|
|
35
|
+
totalRefundedSet {
|
|
36
|
+
shopMoney {
|
|
37
|
+
amount
|
|
38
|
+
currencyCode
|
|
39
|
+
}
|
|
40
|
+
presentmentMoney {
|
|
41
|
+
amount
|
|
42
|
+
currencyCode
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
refundLineItems(first: 50) {
|
|
46
|
+
edges {
|
|
47
|
+
node {
|
|
48
|
+
quantity
|
|
49
|
+
restockType
|
|
50
|
+
restocked
|
|
51
|
+
priceSet {
|
|
52
|
+
shopMoney {
|
|
53
|
+
amount
|
|
54
|
+
currencyCode
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
subtotalSet {
|
|
58
|
+
shopMoney {
|
|
59
|
+
amount
|
|
60
|
+
currencyCode
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
totalTaxSet {
|
|
64
|
+
shopMoney {
|
|
65
|
+
amount
|
|
66
|
+
currencyCode
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
lineItem {
|
|
70
|
+
id
|
|
71
|
+
title
|
|
72
|
+
sku
|
|
73
|
+
}
|
|
74
|
+
location {
|
|
75
|
+
id
|
|
76
|
+
name
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
transactions(first: 10) {
|
|
82
|
+
edges {
|
|
83
|
+
node {
|
|
84
|
+
id
|
|
85
|
+
kind
|
|
86
|
+
status
|
|
87
|
+
gateway
|
|
88
|
+
amountSet {
|
|
89
|
+
shopMoney {
|
|
90
|
+
amount
|
|
91
|
+
currencyCode
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
processedAt
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
duties {
|
|
99
|
+
amountSet {
|
|
100
|
+
shopMoney {
|
|
101
|
+
amount
|
|
102
|
+
currencyCode
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
const data = await shopifyClient.request(query, { id: orderId });
|
|
111
|
+
if (!data.order) {
|
|
112
|
+
throw new Error(`Order not found: ${orderId}`);
|
|
113
|
+
}
|
|
114
|
+
const refunds = data.order.refunds.map((refund) => ({
|
|
115
|
+
...refund,
|
|
116
|
+
refundLineItems: edgesToNodes(refund.refundLineItems),
|
|
117
|
+
transactions: edgesToNodes(refund.transactions),
|
|
118
|
+
}));
|
|
119
|
+
return {
|
|
120
|
+
orderId: data.order.id,
|
|
121
|
+
orderName: data.order.name,
|
|
122
|
+
refundsCount: refunds.length,
|
|
123
|
+
refunds,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
handleToolError("fetch order refund details", error);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
export { getOrderRefundDetails };
|