@anton.andrusenko/shopify-mcp-admin 0.2.0 → 0.3.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/README.md +31 -0
- package/dist/index.js +1081 -681
- package/package.json +12 -2
package/dist/index.js
CHANGED
|
@@ -22,7 +22,10 @@ var configSchema = z.object({
|
|
|
22
22
|
PORT: z.string().default("3000").transform(Number),
|
|
23
23
|
// Transport selection (AC-2.2.6, AC-2.2.7)
|
|
24
24
|
// Default: stdio for Claude Desktop compatibility
|
|
25
|
-
TRANSPORT: z.enum(["stdio", "http"]).default("stdio")
|
|
25
|
+
TRANSPORT: z.enum(["stdio", "http"]).default("stdio"),
|
|
26
|
+
// Store info cache TTL (milliseconds)
|
|
27
|
+
// Default: 5 minutes (300000ms) - configurable for performance tuning
|
|
28
|
+
STORE_INFO_CACHE_TTL_MS: z.string().optional().default("300000").transform(Number).describe("Cache TTL for store info in milliseconds (default: 5 minutes)")
|
|
26
29
|
}).refine(
|
|
27
30
|
(data) => {
|
|
28
31
|
const hasLegacyAuth = !!data.SHOPIFY_ACCESS_TOKEN;
|
|
@@ -936,7 +939,7 @@ function transformShopResponse(shop) {
|
|
|
936
939
|
async function getStoreInfo() {
|
|
937
940
|
const now = Date.now();
|
|
938
941
|
const config = getConfig();
|
|
939
|
-
const ttl =
|
|
942
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
940
943
|
if (_cachedStoreInfo && now - _cacheTimestamp < ttl) {
|
|
941
944
|
log.debug("Returning cached store info");
|
|
942
945
|
return _cachedStoreInfo;
|
|
@@ -963,6 +966,42 @@ async function getStoreInfo() {
|
|
|
963
966
|
var require2 = createRequire(import.meta.url);
|
|
964
967
|
var packageJson = require2("../package.json");
|
|
965
968
|
var SERVER_NAME = "shopify-mcp-admin";
|
|
969
|
+
var SERVER_INSTRUCTIONS = `This MCP server provides access to a Shopify store's Admin API.
|
|
970
|
+
|
|
971
|
+
KEY CONCEPTS:
|
|
972
|
+
- Products contain variants; each variant has its own price, SKU, and inventory
|
|
973
|
+
- Inventory is tracked per variant AND per location (multi-location stores)
|
|
974
|
+
- Metafields store custom data using namespace/key pairs
|
|
975
|
+
- Collections organize products for navigation and SEO
|
|
976
|
+
- All IDs use Shopify GID format: "gid://shopify/Product/123"
|
|
977
|
+
|
|
978
|
+
RESOURCE TYPES & GID FORMATS:
|
|
979
|
+
- Products: gid://shopify/Product/{id}
|
|
980
|
+
- Variants: gid://shopify/ProductVariant/{id}
|
|
981
|
+
- Collections: gid://shopify/Collection/{id}
|
|
982
|
+
- Images: gid://shopify/MediaImage/{id}
|
|
983
|
+
- Pages: gid://shopify/Page/{id}
|
|
984
|
+
- Blogs: gid://shopify/Blog/{id}
|
|
985
|
+
- Articles: gid://shopify/Article/{id}
|
|
986
|
+
- Redirects: gid://shopify/UrlRedirect/{id}
|
|
987
|
+
- InventoryItems: gid://shopify/InventoryItem/{id}
|
|
988
|
+
- Locations: gid://shopify/Location/{id}
|
|
989
|
+
|
|
990
|
+
BEST PRACTICES:
|
|
991
|
+
- Always verify product/variant IDs using get-product before updates
|
|
992
|
+
- Use list-low-inventory to proactively identify stock issues
|
|
993
|
+
- When updating SEO, consider updating URL redirects
|
|
994
|
+
- For bulk operations, prefer bulk tools when available
|
|
995
|
+
- Use metafields to store custom SEO data (JSON-LD, Open Graph)
|
|
996
|
+
|
|
997
|
+
RATE LIMITS:
|
|
998
|
+
- Shopify GraphQL has cost-based rate limiting (~50 points/sec)
|
|
999
|
+
- Large queries may retry automatically with exponential backoff
|
|
1000
|
+
- Rate limit errors include helpful retry suggestions
|
|
1001
|
+
|
|
1002
|
+
WORKFLOW HINTS:
|
|
1003
|
+
- Tool descriptions include **Prerequisites:** and **Follow-ups:** for multi-step operations
|
|
1004
|
+
- Check tool category and relationships for semantic grouping`;
|
|
966
1005
|
function getServerVersion() {
|
|
967
1006
|
return packageJson.version;
|
|
968
1007
|
}
|
|
@@ -978,9 +1017,13 @@ function createServer() {
|
|
|
978
1017
|
capabilities: {
|
|
979
1018
|
tools: {},
|
|
980
1019
|
// Enable tools capability (AC-2.1.4)
|
|
981
|
-
resources: {}
|
|
1020
|
+
resources: {},
|
|
982
1021
|
// Enable resources capability (AC-2.1.4)
|
|
983
|
-
|
|
1022
|
+
logging: {}
|
|
1023
|
+
// Enable logging capability (AC-9.5.1.2)
|
|
1024
|
+
},
|
|
1025
|
+
instructions: SERVER_INSTRUCTIONS
|
|
1026
|
+
// Provide Shopify context to AI agents (AC-9.5.1.1)
|
|
984
1027
|
}
|
|
985
1028
|
);
|
|
986
1029
|
return server;
|
|
@@ -2334,6 +2377,21 @@ function createSingleTenantContext(client, shopDomain) {
|
|
|
2334
2377
|
|
|
2335
2378
|
// src/tools/registration.ts
|
|
2336
2379
|
var KEBAB_CASE_REGEX = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
2380
|
+
function deriveDefaultAnnotations(name) {
|
|
2381
|
+
const isReadOnly = name.startsWith("get-") || name.startsWith("list-");
|
|
2382
|
+
const isDestructive = name.startsWith("delete-");
|
|
2383
|
+
const isCreate = name.startsWith("create-");
|
|
2384
|
+
return {
|
|
2385
|
+
readOnlyHint: isReadOnly,
|
|
2386
|
+
destructiveHint: isDestructive,
|
|
2387
|
+
// create-* is NOT idempotent (each call creates new resource)
|
|
2388
|
+
// delete-* is NOT idempotent (second call fails - resource gone)
|
|
2389
|
+
// update-*, set-*, add-*, remove-*, reorder-* ARE idempotent
|
|
2390
|
+
idempotentHint: !isCreate && !isDestructive,
|
|
2391
|
+
// All our tools interact with Shopify API
|
|
2392
|
+
openWorldHint: true
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2337
2395
|
var registeredTools = /* @__PURE__ */ new Map();
|
|
2338
2396
|
function validateToolName(name) {
|
|
2339
2397
|
if (!name || name.trim() === "") {
|
|
@@ -2393,7 +2451,7 @@ function wrapToolHandler(toolName, schema, handler) {
|
|
|
2393
2451
|
};
|
|
2394
2452
|
}
|
|
2395
2453
|
function registerTool(definition, handler, options = {}) {
|
|
2396
|
-
const { name, title, description, inputSchema: inputSchema41 } = definition;
|
|
2454
|
+
const { name, title, description, inputSchema: inputSchema41, annotations } = definition;
|
|
2397
2455
|
try {
|
|
2398
2456
|
if (!options.skipNameValidation) {
|
|
2399
2457
|
validateToolName(name);
|
|
@@ -2403,13 +2461,20 @@ function registerTool(definition, handler, options = {}) {
|
|
|
2403
2461
|
}
|
|
2404
2462
|
const jsonSchema = convertZodToJsonSchema(inputSchema41);
|
|
2405
2463
|
const wrappedHandler = wrapToolHandler(name, inputSchema41, handler);
|
|
2464
|
+
const finalAnnotations = {
|
|
2465
|
+
...deriveDefaultAnnotations(name),
|
|
2466
|
+
...annotations,
|
|
2467
|
+
// Use definition title as fallback for annotation title
|
|
2468
|
+
title: annotations?.title ?? title
|
|
2469
|
+
};
|
|
2406
2470
|
const registeredTool = {
|
|
2407
2471
|
name,
|
|
2408
2472
|
title,
|
|
2409
2473
|
description,
|
|
2410
2474
|
inputSchema: jsonSchema,
|
|
2411
2475
|
zodSchema: inputSchema41,
|
|
2412
|
-
handler: wrappedHandler
|
|
2476
|
+
handler: wrappedHandler,
|
|
2477
|
+
annotations: finalAnnotations
|
|
2413
2478
|
};
|
|
2414
2479
|
registeredTools.set(name, registeredTool);
|
|
2415
2480
|
log.debug(`Registered tool: ${name}`);
|
|
@@ -2428,7 +2493,8 @@ function getRegisteredTools() {
|
|
|
2428
2493
|
return Array.from(registeredTools.values()).map((tool) => ({
|
|
2429
2494
|
name: tool.name,
|
|
2430
2495
|
description: `${tool.title}: ${tool.description}`,
|
|
2431
|
-
inputSchema: tool.inputSchema
|
|
2496
|
+
inputSchema: tool.inputSchema,
|
|
2497
|
+
annotations: tool.annotations
|
|
2432
2498
|
}));
|
|
2433
2499
|
}
|
|
2434
2500
|
function getToolByName(name) {
|
|
@@ -2448,7 +2514,7 @@ function registerContextAwareTool(definition, handler, options = {}) {
|
|
|
2448
2514
|
}
|
|
2449
2515
|
|
|
2450
2516
|
// src/tools/add-product-image.ts
|
|
2451
|
-
import { z as
|
|
2517
|
+
import { z as z3 } from "zod";
|
|
2452
2518
|
|
|
2453
2519
|
// src/shopify/mutations.ts
|
|
2454
2520
|
var PRODUCT_CREATE_MUTATION = `
|
|
@@ -3088,22 +3154,41 @@ async function addProductImage(productId, input) {
|
|
|
3088
3154
|
};
|
|
3089
3155
|
}
|
|
3090
3156
|
|
|
3157
|
+
// src/tools/validators.ts
|
|
3158
|
+
import { z as z2 } from "zod";
|
|
3159
|
+
var GID_PATTERN = /^gid:\/\/shopify\/[A-Za-z]+\/\d+$/;
|
|
3160
|
+
function gidSchema(resourceType) {
|
|
3161
|
+
const pattern = resourceType ? new RegExp(`^gid://shopify/${resourceType}/\\d+$`) : GID_PATTERN;
|
|
3162
|
+
const example = resourceType ? `gid://shopify/${resourceType}/123456789` : "gid://shopify/Product/123456789";
|
|
3163
|
+
return z2.string().regex(pattern, `Must be a valid Shopify GID (e.g., ${example})`).describe(`Shopify Global ID (format: ${example})`);
|
|
3164
|
+
}
|
|
3165
|
+
var productIdSchema = gidSchema("Product");
|
|
3166
|
+
var variantIdSchema = gidSchema("ProductVariant");
|
|
3167
|
+
var collectionIdSchema = gidSchema("Collection");
|
|
3168
|
+
var imageIdSchema = gidSchema("MediaImage");
|
|
3169
|
+
var pageIdSchema = gidSchema("Page");
|
|
3170
|
+
var blogIdSchema = gidSchema("Blog");
|
|
3171
|
+
var articleIdSchema = gidSchema("Article");
|
|
3172
|
+
var redirectIdSchema = gidSchema("UrlRedirect");
|
|
3173
|
+
var inventoryItemIdSchema = gidSchema("InventoryItem");
|
|
3174
|
+
var locationIdSchema = gidSchema("Location");
|
|
3175
|
+
|
|
3091
3176
|
// src/tools/add-product-image.ts
|
|
3092
|
-
var inputSchema =
|
|
3093
|
-
productId:
|
|
3177
|
+
var inputSchema = z3.object({
|
|
3178
|
+
productId: productIdSchema.describe(
|
|
3094
3179
|
'Shopify product GID (e.g., "gid://shopify/Product/123"). Required. Use list-products to find product IDs.'
|
|
3095
3180
|
),
|
|
3096
|
-
url:
|
|
3181
|
+
url: z3.string().url().describe(
|
|
3097
3182
|
"Publicly accessible image URL. Shopify will fetch and host the image. Required. Supported formats: JPEG, PNG, GIF, WEBP."
|
|
3098
3183
|
),
|
|
3099
|
-
altText:
|
|
3184
|
+
altText: z3.string().optional().describe(
|
|
3100
3185
|
"Alt text for accessibility and SEO. Describes the image for screen readers. Recommended for better accessibility and search rankings."
|
|
3101
3186
|
)
|
|
3102
3187
|
});
|
|
3103
|
-
var outputSchema =
|
|
3104
|
-
id:
|
|
3105
|
-
url:
|
|
3106
|
-
altText:
|
|
3188
|
+
var outputSchema = z3.object({
|
|
3189
|
+
id: z3.string().describe('Image GID (e.g., "gid://shopify/MediaImage/123")'),
|
|
3190
|
+
url: z3.string().describe("Shopify CDN URL for the image"),
|
|
3191
|
+
altText: z3.string().nullable().describe("Alt text for the image")
|
|
3107
3192
|
});
|
|
3108
3193
|
var handleAddProductImage = async (context, params) => {
|
|
3109
3194
|
log.debug(`Adding image to product on shop: ${context.shopDomain}`);
|
|
@@ -3161,6 +3246,14 @@ function registerAddProductImageTool() {
|
|
|
3161
3246
|
relatedTools: ["get-product", "update-product-image"],
|
|
3162
3247
|
prerequisites: ["create-product"],
|
|
3163
3248
|
followUps: ["update-product-image", "reorder-product-images"]
|
|
3249
|
+
},
|
|
3250
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
3251
|
+
annotations: {
|
|
3252
|
+
readOnlyHint: false,
|
|
3253
|
+
destructiveHint: false,
|
|
3254
|
+
idempotentHint: false,
|
|
3255
|
+
// Adding same URL creates duplicate images!
|
|
3256
|
+
openWorldHint: true
|
|
3164
3257
|
}
|
|
3165
3258
|
},
|
|
3166
3259
|
handleAddProductImage
|
|
@@ -3168,7 +3261,7 @@ function registerAddProductImageTool() {
|
|
|
3168
3261
|
}
|
|
3169
3262
|
|
|
3170
3263
|
// src/tools/add-products-to-collection.ts
|
|
3171
|
-
import { z as
|
|
3264
|
+
import { z as z4 } from "zod";
|
|
3172
3265
|
|
|
3173
3266
|
// src/shopify/collections.ts
|
|
3174
3267
|
var GET_COLLECTION_QUERY = `
|
|
@@ -3569,18 +3662,18 @@ async function removeProductsFromCollection(collectionId, productIds) {
|
|
|
3569
3662
|
}
|
|
3570
3663
|
|
|
3571
3664
|
// src/tools/add-products-to-collection.ts
|
|
3572
|
-
var inputSchema2 =
|
|
3573
|
-
collectionId:
|
|
3665
|
+
var inputSchema2 = z4.object({
|
|
3666
|
+
collectionId: collectionIdSchema.describe(
|
|
3574
3667
|
'Collection GID (e.g., "gid://shopify/Collection/123"). Use list-collections to find valid collection IDs.'
|
|
3575
3668
|
),
|
|
3576
|
-
productIds:
|
|
3669
|
+
productIds: z4.array(productIdSchema).min(1).max(250).describe(
|
|
3577
3670
|
'Array of product GIDs to add (e.g., ["gid://shopify/Product/123", "gid://shopify/Product/456"]). Maximum 250 products per call. Use list-products to find valid product IDs.'
|
|
3578
3671
|
)
|
|
3579
3672
|
});
|
|
3580
|
-
var outputSchema2 =
|
|
3581
|
-
collectionId:
|
|
3582
|
-
productsCount:
|
|
3583
|
-
addedCount:
|
|
3673
|
+
var outputSchema2 = z4.object({
|
|
3674
|
+
collectionId: z4.string().describe("Collection GID"),
|
|
3675
|
+
productsCount: z4.number().describe("Total number of products now in the collection"),
|
|
3676
|
+
addedCount: z4.number().describe("Number of products added in this operation")
|
|
3584
3677
|
});
|
|
3585
3678
|
var handleAddProductsToCollection = async (context, params) => {
|
|
3586
3679
|
log.debug(
|
|
@@ -3624,6 +3717,14 @@ function registerAddProductsToCollectionTool() {
|
|
|
3624
3717
|
relatedTools: ["remove-products-from-collection", "list-products"],
|
|
3625
3718
|
prerequisites: ["create-collection", "list-products"],
|
|
3626
3719
|
followUps: ["get-collection"]
|
|
3720
|
+
},
|
|
3721
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
3722
|
+
annotations: {
|
|
3723
|
+
readOnlyHint: false,
|
|
3724
|
+
destructiveHint: false,
|
|
3725
|
+
idempotentHint: true,
|
|
3726
|
+
// Adding same products is idempotent (no-op)
|
|
3727
|
+
openWorldHint: true
|
|
3627
3728
|
}
|
|
3628
3729
|
},
|
|
3629
3730
|
handleAddProductsToCollection
|
|
@@ -3631,7 +3732,7 @@ function registerAddProductsToCollectionTool() {
|
|
|
3631
3732
|
}
|
|
3632
3733
|
|
|
3633
3734
|
// src/tools/create-article.ts
|
|
3634
|
-
import { z as
|
|
3735
|
+
import { z as z5 } from "zod";
|
|
3635
3736
|
|
|
3636
3737
|
// src/shopify/blogs.ts
|
|
3637
3738
|
var LIST_BLOGS_QUERY = `
|
|
@@ -4137,51 +4238,51 @@ async function deleteArticle(articleId) {
|
|
|
4137
4238
|
}
|
|
4138
4239
|
|
|
4139
4240
|
// src/tools/create-article.ts
|
|
4140
|
-
var inputSchema3 =
|
|
4141
|
-
blogId:
|
|
4241
|
+
var inputSchema3 = z5.object({
|
|
4242
|
+
blogId: blogIdSchema.describe(
|
|
4142
4243
|
'The blog ID to create the article in (required). Must be a valid Shopify GID format. Example: "gid://shopify/Blog/12345". Use list-blogs to find blog IDs.'
|
|
4143
4244
|
),
|
|
4144
|
-
title:
|
|
4245
|
+
title: z5.string().min(1).describe(
|
|
4145
4246
|
"The title of the article (required). This will be displayed as the article heading. Example: 'How to Use Our Products', '10 Tips for Beginners'."
|
|
4146
4247
|
),
|
|
4147
|
-
authorName:
|
|
4248
|
+
authorName: z5.string().min(1).describe(
|
|
4148
4249
|
"The name of the article author (required). This is displayed on the article. Example: 'John Doe', 'Marketing Team'."
|
|
4149
4250
|
),
|
|
4150
|
-
body:
|
|
4251
|
+
body: z5.string().optional().describe(
|
|
4151
4252
|
"The HTML body content of the article. Supports HTML markup for formatting. Example: '<h2>Introduction</h2><p>Welcome to our guide...</p>'"
|
|
4152
4253
|
),
|
|
4153
|
-
summary:
|
|
4254
|
+
summary: z5.string().optional().describe(
|
|
4154
4255
|
"A summary or excerpt of the article. Used for previews on blog listing pages. Can include HTML markup."
|
|
4155
4256
|
),
|
|
4156
|
-
tags:
|
|
4257
|
+
tags: z5.array(z5.string()).optional().describe(
|
|
4157
4258
|
"Tags for categorization. Helps organize articles and improve discoverability. Example: ['guide', 'tutorial', 'beginner']."
|
|
4158
4259
|
),
|
|
4159
|
-
image:
|
|
4160
|
-
url:
|
|
4161
|
-
altText:
|
|
4260
|
+
image: z5.object({
|
|
4261
|
+
url: z5.string().url().describe("The URL of the featured image"),
|
|
4262
|
+
altText: z5.string().optional().describe("Alt text for accessibility")
|
|
4162
4263
|
}).optional().describe("Featured image for the article."),
|
|
4163
|
-
isPublished:
|
|
4264
|
+
isPublished: z5.boolean().optional().describe(
|
|
4164
4265
|
"Whether the article should be visible on the storefront. Defaults to false (unpublished). Set to true to make the article immediately visible."
|
|
4165
4266
|
),
|
|
4166
|
-
publishDate:
|
|
4267
|
+
publishDate: z5.string().optional().describe(
|
|
4167
4268
|
"The date and time when the article should become visible (ISO 8601 format). Example: '2024-01-15T10:00:00Z'. If not set and isPublished is true, publishes immediately."
|
|
4168
4269
|
),
|
|
4169
|
-
handle:
|
|
4270
|
+
handle: z5.string().optional().describe(
|
|
4170
4271
|
"The URL handle/slug for the article. If not provided, it will be auto-generated from the title. Example: 'how-to-use-our-products' would create URL /blogs/blog-handle/how-to-use-our-products."
|
|
4171
4272
|
),
|
|
4172
|
-
templateSuffix:
|
|
4273
|
+
templateSuffix: z5.string().optional().describe(
|
|
4173
4274
|
"The suffix of the Liquid template used to render the article. For example, 'featured' would use the template 'article.featured.liquid'."
|
|
4174
4275
|
)
|
|
4175
4276
|
});
|
|
4176
|
-
var outputSchema3 =
|
|
4177
|
-
id:
|
|
4178
|
-
title:
|
|
4179
|
-
handle:
|
|
4180
|
-
blog:
|
|
4181
|
-
id:
|
|
4182
|
-
title:
|
|
4277
|
+
var outputSchema3 = z5.object({
|
|
4278
|
+
id: z5.string().describe('Created article GID (e.g., "gid://shopify/Article/123")'),
|
|
4279
|
+
title: z5.string().describe("Article title"),
|
|
4280
|
+
handle: z5.string().describe("URL handle/slug"),
|
|
4281
|
+
blog: z5.object({
|
|
4282
|
+
id: z5.string().describe("Parent blog GID"),
|
|
4283
|
+
title: z5.string().describe("Parent blog title")
|
|
4183
4284
|
}),
|
|
4184
|
-
isPublished:
|
|
4285
|
+
isPublished: z5.boolean().describe("Whether article is visible on storefront")
|
|
4185
4286
|
});
|
|
4186
4287
|
var handleCreateArticle = async (context, params) => {
|
|
4187
4288
|
log.debug(`Creating article on shop: ${context.shopDomain}`);
|
|
@@ -4237,6 +4338,14 @@ function registerCreateArticleTool() {
|
|
|
4237
4338
|
relatedTools: ["list-blogs", "list-articles", "update-article"],
|
|
4238
4339
|
prerequisites: ["list-blogs"],
|
|
4239
4340
|
followUps: ["list-articles", "update-article"]
|
|
4341
|
+
},
|
|
4342
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
4343
|
+
annotations: {
|
|
4344
|
+
readOnlyHint: false,
|
|
4345
|
+
destructiveHint: false,
|
|
4346
|
+
idempotentHint: false,
|
|
4347
|
+
// create-* tools are NOT idempotent
|
|
4348
|
+
openWorldHint: true
|
|
4240
4349
|
}
|
|
4241
4350
|
},
|
|
4242
4351
|
handleCreateArticle
|
|
@@ -4244,26 +4353,26 @@ function registerCreateArticleTool() {
|
|
|
4244
4353
|
}
|
|
4245
4354
|
|
|
4246
4355
|
// src/tools/create-blog.ts
|
|
4247
|
-
import { z as
|
|
4248
|
-
var inputSchema4 =
|
|
4249
|
-
title:
|
|
4356
|
+
import { z as z6 } from "zod";
|
|
4357
|
+
var inputSchema4 = z6.object({
|
|
4358
|
+
title: z6.string().min(1).describe(
|
|
4250
4359
|
"The title of the blog (required). This will be displayed as the blog heading. Example: 'Company News', 'Product Guides', 'Industry Insights'."
|
|
4251
4360
|
),
|
|
4252
|
-
handle:
|
|
4361
|
+
handle: z6.string().optional().describe(
|
|
4253
4362
|
"The URL handle/slug for the blog. If not provided, it will be auto-generated from the title. Example: 'news' would create URL /blogs/news."
|
|
4254
4363
|
),
|
|
4255
|
-
commentPolicy:
|
|
4364
|
+
commentPolicy: z6.enum(["CLOSED", "MODERATE", "OPEN"]).optional().describe(
|
|
4256
4365
|
"Comment moderation policy for the blog. CLOSED: Comments disabled. MODERATE: Comments require approval. OPEN: Comments appear immediately. Defaults to MODERATE if not specified."
|
|
4257
4366
|
),
|
|
4258
|
-
templateSuffix:
|
|
4367
|
+
templateSuffix: z6.string().optional().describe(
|
|
4259
4368
|
"The suffix of the Liquid template used to render the blog. For example, 'news' would use the template 'blog.news.liquid'."
|
|
4260
4369
|
)
|
|
4261
4370
|
});
|
|
4262
|
-
var outputSchema4 =
|
|
4263
|
-
id:
|
|
4264
|
-
title:
|
|
4265
|
-
handle:
|
|
4266
|
-
commentPolicy:
|
|
4371
|
+
var outputSchema4 = z6.object({
|
|
4372
|
+
id: z6.string().describe('Created blog GID (e.g., "gid://shopify/Blog/123")'),
|
|
4373
|
+
title: z6.string().describe("Blog title"),
|
|
4374
|
+
handle: z6.string().describe("URL handle/slug"),
|
|
4375
|
+
commentPolicy: z6.enum(["CLOSED", "MODERATE", "OPEN"]).describe("Comment moderation policy")
|
|
4267
4376
|
});
|
|
4268
4377
|
var handleCreateBlog = async (context, params) => {
|
|
4269
4378
|
log.debug(`Creating blog on shop: ${context.shopDomain}`);
|
|
@@ -4305,6 +4414,14 @@ function registerCreateBlogTool() {
|
|
|
4305
4414
|
relationships: {
|
|
4306
4415
|
relatedTools: ["list-blogs", "update-blog", "create-article"],
|
|
4307
4416
|
followUps: ["create-article", "list-blogs"]
|
|
4417
|
+
},
|
|
4418
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
4419
|
+
annotations: {
|
|
4420
|
+
readOnlyHint: false,
|
|
4421
|
+
destructiveHint: false,
|
|
4422
|
+
idempotentHint: false,
|
|
4423
|
+
// create-* tools are NOT idempotent
|
|
4424
|
+
openWorldHint: true
|
|
4308
4425
|
}
|
|
4309
4426
|
},
|
|
4310
4427
|
handleCreateBlog
|
|
@@ -4312,20 +4429,20 @@ function registerCreateBlogTool() {
|
|
|
4312
4429
|
}
|
|
4313
4430
|
|
|
4314
4431
|
// src/tools/create-collection.ts
|
|
4315
|
-
import { z as
|
|
4316
|
-
var inputSchema5 =
|
|
4317
|
-
title:
|
|
4318
|
-
handle:
|
|
4432
|
+
import { z as z7 } from "zod";
|
|
4433
|
+
var inputSchema5 = z7.object({
|
|
4434
|
+
title: z7.string().min(1).describe("Collection title (required). This will be displayed to customers."),
|
|
4435
|
+
handle: z7.string().optional().describe(
|
|
4319
4436
|
'URL handle/slug for the collection (e.g., "summer-sale"). Auto-generated from title if not provided.'
|
|
4320
4437
|
),
|
|
4321
|
-
descriptionHtml:
|
|
4438
|
+
descriptionHtml: z7.string().optional().describe(
|
|
4322
4439
|
"Collection description with HTML formatting. Displayed on collection pages in the store."
|
|
4323
4440
|
),
|
|
4324
|
-
seo:
|
|
4325
|
-
title:
|
|
4326
|
-
description:
|
|
4441
|
+
seo: z7.object({
|
|
4442
|
+
title: z7.string().optional().describe("SEO title for search engines"),
|
|
4443
|
+
description: z7.string().optional().describe("SEO meta description for search engines")
|
|
4327
4444
|
}).optional().describe("SEO metadata to optimize collection visibility in search results"),
|
|
4328
|
-
sortOrder:
|
|
4445
|
+
sortOrder: z7.enum([
|
|
4329
4446
|
"ALPHA_ASC",
|
|
4330
4447
|
"ALPHA_DESC",
|
|
4331
4448
|
"BEST_SELLING",
|
|
@@ -4337,18 +4454,18 @@ var inputSchema5 = z6.object({
|
|
|
4337
4454
|
]).optional().describe(
|
|
4338
4455
|
"How products are sorted within the collection. Options: ALPHA_ASC (A-Z), ALPHA_DESC (Z-A), BEST_SELLING, CREATED (oldest first), CREATED_DESC (newest first), MANUAL, PRICE_ASC (low to high), PRICE_DESC (high to low)"
|
|
4339
4456
|
),
|
|
4340
|
-
image:
|
|
4341
|
-
src:
|
|
4342
|
-
altText:
|
|
4457
|
+
image: z7.object({
|
|
4458
|
+
src: z7.string().url().describe("Publicly accessible image URL"),
|
|
4459
|
+
altText: z7.string().optional().describe("Alt text for accessibility and SEO")
|
|
4343
4460
|
}).optional().describe("Collection image displayed on collection pages"),
|
|
4344
|
-
templateSuffix:
|
|
4461
|
+
templateSuffix: z7.string().optional().describe(
|
|
4345
4462
|
'Liquid template suffix for custom collection templates. For example, "featured" uses collection.featured.liquid'
|
|
4346
4463
|
)
|
|
4347
4464
|
});
|
|
4348
|
-
var outputSchema5 =
|
|
4349
|
-
id:
|
|
4350
|
-
title:
|
|
4351
|
-
handle:
|
|
4465
|
+
var outputSchema5 = z7.object({
|
|
4466
|
+
id: z7.string().describe('Created collection GID (e.g., "gid://shopify/Collection/123")'),
|
|
4467
|
+
title: z7.string().describe("Collection title"),
|
|
4468
|
+
handle: z7.string().describe("Collection URL handle")
|
|
4352
4469
|
});
|
|
4353
4470
|
var handleCreateCollection = async (context, params) => {
|
|
4354
4471
|
log.debug(`Creating collection on shop: ${context.shopDomain}`);
|
|
@@ -4387,6 +4504,14 @@ function registerCreateCollectionTool() {
|
|
|
4387
4504
|
relationships: {
|
|
4388
4505
|
relatedTools: ["list-collections", "get-collection"],
|
|
4389
4506
|
followUps: ["add-products-to-collection", "update-collection"]
|
|
4507
|
+
},
|
|
4508
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
4509
|
+
annotations: {
|
|
4510
|
+
readOnlyHint: false,
|
|
4511
|
+
destructiveHint: false,
|
|
4512
|
+
idempotentHint: false,
|
|
4513
|
+
// Each call creates a new collection
|
|
4514
|
+
openWorldHint: true
|
|
4390
4515
|
}
|
|
4391
4516
|
},
|
|
4392
4517
|
handleCreateCollection
|
|
@@ -4394,7 +4519,7 @@ function registerCreateCollectionTool() {
|
|
|
4394
4519
|
}
|
|
4395
4520
|
|
|
4396
4521
|
// src/tools/create-page.ts
|
|
4397
|
-
import { z as
|
|
4522
|
+
import { z as z8 } from "zod";
|
|
4398
4523
|
|
|
4399
4524
|
// src/shopify/pages.ts
|
|
4400
4525
|
var PAGE_QUERY = `
|
|
@@ -4676,28 +4801,28 @@ async function deletePage(pageId) {
|
|
|
4676
4801
|
}
|
|
4677
4802
|
|
|
4678
4803
|
// src/tools/create-page.ts
|
|
4679
|
-
var inputSchema6 =
|
|
4680
|
-
title:
|
|
4804
|
+
var inputSchema6 = z8.object({
|
|
4805
|
+
title: z8.string().min(1).describe(
|
|
4681
4806
|
"The title of the page (required). This will be displayed as the page heading. Example: 'About Us', 'Contact', 'Privacy Policy'."
|
|
4682
4807
|
),
|
|
4683
|
-
body:
|
|
4808
|
+
body: z8.string().optional().describe(
|
|
4684
4809
|
"The HTML body content of the page. Supports HTML markup for formatting. Example: '<h1>About Our Company</h1><p>We are a great company.</p>'"
|
|
4685
4810
|
),
|
|
4686
|
-
handle:
|
|
4811
|
+
handle: z8.string().optional().describe(
|
|
4687
4812
|
"The URL handle/slug for the page. If not provided, it will be auto-generated from the title. Example: 'about-us' would create URL /pages/about-us."
|
|
4688
4813
|
),
|
|
4689
|
-
isPublished:
|
|
4814
|
+
isPublished: z8.boolean().optional().describe(
|
|
4690
4815
|
"Whether the page should be visible on the storefront. Defaults to false (unpublished). Set to true to make the page immediately visible."
|
|
4691
4816
|
),
|
|
4692
|
-
templateSuffix:
|
|
4817
|
+
templateSuffix: z8.string().optional().describe(
|
|
4693
4818
|
"The suffix of the Liquid template used to render the page. For example, 'contact' would use the template 'page.contact.liquid'."
|
|
4694
4819
|
)
|
|
4695
4820
|
});
|
|
4696
|
-
var outputSchema6 =
|
|
4697
|
-
id:
|
|
4698
|
-
title:
|
|
4699
|
-
handle:
|
|
4700
|
-
isPublished:
|
|
4821
|
+
var outputSchema6 = z8.object({
|
|
4822
|
+
id: z8.string().describe('Created page GID (e.g., "gid://shopify/Page/123")'),
|
|
4823
|
+
title: z8.string().describe("Page title"),
|
|
4824
|
+
handle: z8.string().describe("URL handle/slug"),
|
|
4825
|
+
isPublished: z8.boolean().describe("Whether page is visible on storefront")
|
|
4701
4826
|
});
|
|
4702
4827
|
var handleCreatePage = async (context, params) => {
|
|
4703
4828
|
log.debug(`Creating page on shop: ${context.shopDomain}`);
|
|
@@ -4740,6 +4865,14 @@ function registerCreatePageTool() {
|
|
|
4740
4865
|
relationships: {
|
|
4741
4866
|
relatedTools: ["list-pages", "get-page", "update-page"],
|
|
4742
4867
|
followUps: ["get-page", "update-page"]
|
|
4868
|
+
},
|
|
4869
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
4870
|
+
annotations: {
|
|
4871
|
+
readOnlyHint: false,
|
|
4872
|
+
destructiveHint: false,
|
|
4873
|
+
idempotentHint: false,
|
|
4874
|
+
// create-* tools are NOT idempotent
|
|
4875
|
+
openWorldHint: true
|
|
4743
4876
|
}
|
|
4744
4877
|
},
|
|
4745
4878
|
handleCreatePage
|
|
@@ -4747,78 +4880,78 @@ function registerCreatePageTool() {
|
|
|
4747
4880
|
}
|
|
4748
4881
|
|
|
4749
4882
|
// src/tools/create-product.ts
|
|
4750
|
-
import { z as
|
|
4751
|
-
var inputSchema7 =
|
|
4883
|
+
import { z as z9 } from "zod";
|
|
4884
|
+
var inputSchema7 = z9.object({
|
|
4752
4885
|
// Required field
|
|
4753
|
-
title:
|
|
4886
|
+
title: z9.string().min(1, "title is required").describe('Product title (required). Example: "Premium Cotton T-Shirt"'),
|
|
4754
4887
|
// Optional core fields
|
|
4755
|
-
description:
|
|
4756
|
-
vendor:
|
|
4757
|
-
productType:
|
|
4758
|
-
tags:
|
|
4759
|
-
status:
|
|
4888
|
+
description: z9.string().optional().describe('Product description (HTML supported). Example: "<p>Soft cotton t-shirt.</p>"'),
|
|
4889
|
+
vendor: z9.string().optional().describe('Product vendor/brand name. Example: "Acme Corp"'),
|
|
4890
|
+
productType: z9.string().optional().describe('Product type for categorization. Example: "T-Shirts"'),
|
|
4891
|
+
tags: z9.array(z9.string()).optional().describe('Tags for search and filtering. Example: ["summer", "cotton", "casual"]'),
|
|
4892
|
+
status: z9.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().default("DRAFT").describe(
|
|
4760
4893
|
"Product status: ACTIVE (visible), DRAFT (hidden), ARCHIVED (hidden). Default: DRAFT"
|
|
4761
4894
|
),
|
|
4762
4895
|
// SEO metadata
|
|
4763
|
-
seo:
|
|
4764
|
-
title:
|
|
4765
|
-
description:
|
|
4896
|
+
seo: z9.object({
|
|
4897
|
+
title: z9.string().optional().describe("SEO title for search engine results"),
|
|
4898
|
+
description: z9.string().optional().describe("SEO meta description for search engines")
|
|
4766
4899
|
}).optional().describe(
|
|
4767
4900
|
"SEO metadata for search engine optimization. If not provided, Shopify uses product title/description."
|
|
4768
4901
|
),
|
|
4769
4902
|
// Variants
|
|
4770
|
-
variants:
|
|
4771
|
-
|
|
4772
|
-
price:
|
|
4773
|
-
compareAtPrice:
|
|
4774
|
-
sku:
|
|
4775
|
-
barcode:
|
|
4776
|
-
inventoryQuantity:
|
|
4777
|
-
options:
|
|
4903
|
+
variants: z9.array(
|
|
4904
|
+
z9.object({
|
|
4905
|
+
price: z9.string().describe('Variant price as decimal string. Example: "29.99"'),
|
|
4906
|
+
compareAtPrice: z9.string().optional().describe('Original price for sale display. Example: "39.99"'),
|
|
4907
|
+
sku: z9.string().optional().describe('Stock keeping unit. Example: "SHIRT-001-RED-M"'),
|
|
4908
|
+
barcode: z9.string().optional().describe("Barcode (UPC, EAN, etc.)"),
|
|
4909
|
+
inventoryQuantity: z9.number().int().min(0).optional().describe("Initial inventory quantity (note: may require separate inventory API)"),
|
|
4910
|
+
options: z9.array(z9.string()).optional().describe('Variant options. Example: ["Red", "Medium"]')
|
|
4778
4911
|
})
|
|
4779
4912
|
).optional().describe("Product variants. If not provided, a default variant is created."),
|
|
4780
4913
|
// Images
|
|
4781
|
-
images:
|
|
4782
|
-
|
|
4783
|
-
url:
|
|
4784
|
-
altText:
|
|
4914
|
+
images: z9.array(
|
|
4915
|
+
z9.object({
|
|
4916
|
+
url: z9.string().url("Invalid image URL format").describe("Publicly accessible image URL"),
|
|
4917
|
+
altText: z9.string().optional().describe("Alt text for accessibility")
|
|
4785
4918
|
})
|
|
4786
4919
|
).optional().describe("Product images. Shopify fetches images from URLs.")
|
|
4787
4920
|
});
|
|
4788
|
-
var outputSchema7 =
|
|
4789
|
-
id:
|
|
4790
|
-
title:
|
|
4791
|
-
handle:
|
|
4792
|
-
description:
|
|
4793
|
-
vendor:
|
|
4794
|
-
productType:
|
|
4795
|
-
status:
|
|
4796
|
-
tags:
|
|
4797
|
-
seo:
|
|
4798
|
-
title:
|
|
4799
|
-
description:
|
|
4921
|
+
var outputSchema7 = z9.object({
|
|
4922
|
+
id: z9.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
|
|
4923
|
+
title: z9.string().describe("Product title"),
|
|
4924
|
+
handle: z9.string().describe("URL handle/slug"),
|
|
4925
|
+
description: z9.string().nullable().describe("Product description (HTML)"),
|
|
4926
|
+
vendor: z9.string().describe("Vendor/brand name"),
|
|
4927
|
+
productType: z9.string().describe("Product type"),
|
|
4928
|
+
status: z9.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
|
|
4929
|
+
tags: z9.array(z9.string()).describe("Product tags"),
|
|
4930
|
+
seo: z9.object({
|
|
4931
|
+
title: z9.string().nullable().describe("SEO title for search engines"),
|
|
4932
|
+
description: z9.string().nullable().describe("SEO description/meta description")
|
|
4800
4933
|
}).describe("SEO metadata for search engine optimization"),
|
|
4801
|
-
createdAt:
|
|
4802
|
-
updatedAt:
|
|
4803
|
-
variants:
|
|
4804
|
-
|
|
4805
|
-
id:
|
|
4806
|
-
title:
|
|
4807
|
-
price:
|
|
4808
|
-
compareAtPrice:
|
|
4809
|
-
sku:
|
|
4810
|
-
barcode:
|
|
4811
|
-
inventoryQuantity:
|
|
4934
|
+
createdAt: z9.string().describe("Creation timestamp (ISO 8601)"),
|
|
4935
|
+
updatedAt: z9.string().describe("Last update timestamp (ISO 8601)"),
|
|
4936
|
+
variants: z9.array(
|
|
4937
|
+
z9.object({
|
|
4938
|
+
id: z9.string().describe("Variant GID"),
|
|
4939
|
+
title: z9.string().describe("Variant title"),
|
|
4940
|
+
price: z9.string().describe("Price"),
|
|
4941
|
+
compareAtPrice: z9.string().nullable().describe("Compare at price"),
|
|
4942
|
+
sku: z9.string().nullable().describe("SKU"),
|
|
4943
|
+
barcode: z9.string().nullable().describe("Barcode"),
|
|
4944
|
+
inventoryQuantity: z9.number().nullable().describe("Inventory quantity")
|
|
4812
4945
|
})
|
|
4813
4946
|
).describe("Product variants"),
|
|
4814
|
-
images:
|
|
4815
|
-
|
|
4816
|
-
id:
|
|
4817
|
-
url:
|
|
4818
|
-
altText:
|
|
4947
|
+
images: z9.array(
|
|
4948
|
+
z9.object({
|
|
4949
|
+
id: z9.string().describe("Image GID"),
|
|
4950
|
+
url: z9.string().describe("Image URL"),
|
|
4951
|
+
altText: z9.string().nullable().describe("Alt text")
|
|
4819
4952
|
})
|
|
4820
4953
|
).describe("Product images"),
|
|
4821
|
-
totalInventory:
|
|
4954
|
+
totalInventory: z9.number().describe("Total inventory across variants")
|
|
4822
4955
|
});
|
|
4823
4956
|
var handleCreateProduct = async (context, params) => {
|
|
4824
4957
|
log.debug(`Creating product on shop: ${context.shopDomain}`);
|
|
@@ -4861,6 +4994,14 @@ function registerCreateProductTool() {
|
|
|
4861
4994
|
relatedTools: ["get-product", "update-product", "list-products"],
|
|
4862
4995
|
followUps: ["add-product-image", "set-product-metafields", "add-products-to-collection"],
|
|
4863
4996
|
alternatives: ["update-product"]
|
|
4997
|
+
},
|
|
4998
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
4999
|
+
annotations: {
|
|
5000
|
+
readOnlyHint: false,
|
|
5001
|
+
destructiveHint: false,
|
|
5002
|
+
idempotentHint: false,
|
|
5003
|
+
// Each call creates a new product
|
|
5004
|
+
openWorldHint: true
|
|
4864
5005
|
}
|
|
4865
5006
|
},
|
|
4866
5007
|
handleCreateProduct
|
|
@@ -4868,7 +5009,7 @@ function registerCreateProductTool() {
|
|
|
4868
5009
|
}
|
|
4869
5010
|
|
|
4870
5011
|
// src/tools/create-redirect.ts
|
|
4871
|
-
import { z as
|
|
5012
|
+
import { z as z10 } from "zod";
|
|
4872
5013
|
|
|
4873
5014
|
// src/shopify/redirects.ts
|
|
4874
5015
|
var URL_REDIRECT_CREATE_MUTATION = `
|
|
@@ -5016,20 +5157,20 @@ async function deleteRedirect(redirectId) {
|
|
|
5016
5157
|
}
|
|
5017
5158
|
|
|
5018
5159
|
// src/tools/create-redirect.ts
|
|
5019
|
-
var inputSchema8 =
|
|
5020
|
-
path:
|
|
5160
|
+
var inputSchema8 = z10.object({
|
|
5161
|
+
path: z10.string().min(1).refine((val) => val.startsWith("/"), {
|
|
5021
5162
|
message: "Path must start with '/' (e.g., '/old-product-url')"
|
|
5022
5163
|
}).describe(
|
|
5023
5164
|
"The source path to redirect FROM. Must start with '/'. Example: '/old-product-url' or '/collections/old-collection'."
|
|
5024
5165
|
),
|
|
5025
|
-
target:
|
|
5166
|
+
target: z10.string().min(1).describe(
|
|
5026
5167
|
"The destination URL to redirect TO. Can be relative ('/new-product-url') or absolute ('https://example.com/page')."
|
|
5027
5168
|
)
|
|
5028
5169
|
});
|
|
5029
|
-
var outputSchema8 =
|
|
5030
|
-
id:
|
|
5031
|
-
path:
|
|
5032
|
-
target:
|
|
5170
|
+
var outputSchema8 = z10.object({
|
|
5171
|
+
id: z10.string().describe('Created redirect GID (e.g., "gid://shopify/UrlRedirect/123")'),
|
|
5172
|
+
path: z10.string().describe("Source path that will redirect"),
|
|
5173
|
+
target: z10.string().describe("Target URL visitors will be redirected to")
|
|
5033
5174
|
});
|
|
5034
5175
|
var handleCreateRedirect = async (context, params) => {
|
|
5035
5176
|
log.debug(`Creating redirect on shop: ${context.shopDomain}`);
|
|
@@ -5070,6 +5211,14 @@ function registerCreateRedirectTool() {
|
|
|
5070
5211
|
relationships: {
|
|
5071
5212
|
relatedTools: ["list-redirects", "delete-redirect"],
|
|
5072
5213
|
followUps: ["list-redirects"]
|
|
5214
|
+
},
|
|
5215
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5216
|
+
annotations: {
|
|
5217
|
+
readOnlyHint: false,
|
|
5218
|
+
destructiveHint: false,
|
|
5219
|
+
idempotentHint: false,
|
|
5220
|
+
// Each call creates a new redirect (duplicate path fails)
|
|
5221
|
+
openWorldHint: true
|
|
5073
5222
|
}
|
|
5074
5223
|
},
|
|
5075
5224
|
handleCreateRedirect
|
|
@@ -5077,15 +5226,15 @@ function registerCreateRedirectTool() {
|
|
|
5077
5226
|
}
|
|
5078
5227
|
|
|
5079
5228
|
// src/tools/delete-article.ts
|
|
5080
|
-
import { z as
|
|
5081
|
-
var inputSchema9 =
|
|
5082
|
-
id:
|
|
5229
|
+
import { z as z11 } from "zod";
|
|
5230
|
+
var inputSchema9 = z11.object({
|
|
5231
|
+
id: articleIdSchema.describe(
|
|
5083
5232
|
'The article ID to delete (required). Must be a valid Shopify GID format. Example: "gid://shopify/Article/12345". Use list-articles to find article IDs. This action cannot be undone - consider using update-article to unpublish instead.'
|
|
5084
5233
|
)
|
|
5085
5234
|
});
|
|
5086
|
-
var outputSchema9 =
|
|
5087
|
-
success:
|
|
5088
|
-
deletedArticleId:
|
|
5235
|
+
var outputSchema9 = z11.object({
|
|
5236
|
+
success: z11.boolean().describe("Whether the deletion was successful"),
|
|
5237
|
+
deletedArticleId: z11.string().describe("The ID of the deleted article")
|
|
5089
5238
|
});
|
|
5090
5239
|
var handleDeleteArticle = async (context, params) => {
|
|
5091
5240
|
log.debug(`Deleting article on shop: ${context.shopDomain}`);
|
|
@@ -5126,6 +5275,14 @@ function registerDeleteArticleTool() {
|
|
|
5126
5275
|
relatedTools: ["list-articles", "update-article"],
|
|
5127
5276
|
prerequisites: ["list-articles"],
|
|
5128
5277
|
alternatives: ["update-article"]
|
|
5278
|
+
},
|
|
5279
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5280
|
+
annotations: {
|
|
5281
|
+
readOnlyHint: false,
|
|
5282
|
+
destructiveHint: true,
|
|
5283
|
+
idempotentHint: false,
|
|
5284
|
+
// delete-* tools are NOT idempotent
|
|
5285
|
+
openWorldHint: true
|
|
5129
5286
|
}
|
|
5130
5287
|
},
|
|
5131
5288
|
handleDeleteArticle
|
|
@@ -5133,15 +5290,15 @@ function registerDeleteArticleTool() {
|
|
|
5133
5290
|
}
|
|
5134
5291
|
|
|
5135
5292
|
// src/tools/delete-blog.ts
|
|
5136
|
-
import { z as
|
|
5137
|
-
var inputSchema10 =
|
|
5138
|
-
id:
|
|
5293
|
+
import { z as z12 } from "zod";
|
|
5294
|
+
var inputSchema10 = z12.object({
|
|
5295
|
+
id: blogIdSchema.describe(
|
|
5139
5296
|
'The blog ID to delete (required). Must be a valid Shopify GID format. Example: "gid://shopify/Blog/12345". Use list-blogs to find blog IDs. WARNING: Deleting a blog also deletes ALL articles within it.'
|
|
5140
5297
|
)
|
|
5141
5298
|
});
|
|
5142
|
-
var outputSchema10 =
|
|
5143
|
-
success:
|
|
5144
|
-
deletedBlogId:
|
|
5299
|
+
var outputSchema10 = z12.object({
|
|
5300
|
+
success: z12.boolean().describe("Whether the deletion was successful"),
|
|
5301
|
+
deletedBlogId: z12.string().describe("The ID of the deleted blog")
|
|
5145
5302
|
});
|
|
5146
5303
|
var handleDeleteBlog = async (context, params) => {
|
|
5147
5304
|
log.debug(`Deleting blog on shop: ${context.shopDomain}`);
|
|
@@ -5181,6 +5338,14 @@ function registerDeleteBlogTool() {
|
|
|
5181
5338
|
relationships: {
|
|
5182
5339
|
relatedTools: ["list-blogs", "list-articles"],
|
|
5183
5340
|
prerequisites: ["list-blogs"]
|
|
5341
|
+
},
|
|
5342
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5343
|
+
annotations: {
|
|
5344
|
+
readOnlyHint: false,
|
|
5345
|
+
destructiveHint: true,
|
|
5346
|
+
idempotentHint: false,
|
|
5347
|
+
// delete-* tools are NOT idempotent
|
|
5348
|
+
openWorldHint: true
|
|
5184
5349
|
}
|
|
5185
5350
|
},
|
|
5186
5351
|
handleDeleteBlog
|
|
@@ -5188,14 +5353,14 @@ function registerDeleteBlogTool() {
|
|
|
5188
5353
|
}
|
|
5189
5354
|
|
|
5190
5355
|
// src/tools/delete-collection.ts
|
|
5191
|
-
import { z as
|
|
5192
|
-
var inputSchema11 =
|
|
5193
|
-
collectionId:
|
|
5356
|
+
import { z as z13 } from "zod";
|
|
5357
|
+
var inputSchema11 = z13.object({
|
|
5358
|
+
collectionId: collectionIdSchema.describe(
|
|
5194
5359
|
'Collection ID to delete (GID format, e.g., "gid://shopify/Collection/123"). Use list-collections or get-collection to find collection IDs.'
|
|
5195
5360
|
)
|
|
5196
5361
|
});
|
|
5197
|
-
var outputSchema11 =
|
|
5198
|
-
deletedCollectionId:
|
|
5362
|
+
var outputSchema11 = z13.object({
|
|
5363
|
+
deletedCollectionId: z13.string().describe("The GID of the deleted collection")
|
|
5199
5364
|
});
|
|
5200
5365
|
var handleDeleteCollection = async (context, params) => {
|
|
5201
5366
|
log.debug(`Deleting collection on shop: ${context.shopDomain}`);
|
|
@@ -5232,6 +5397,15 @@ function registerDeleteCollectionTool() {
|
|
|
5232
5397
|
relationships: {
|
|
5233
5398
|
relatedTools: ["list-collections"],
|
|
5234
5399
|
prerequisites: ["get-collection"]
|
|
5400
|
+
},
|
|
5401
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5402
|
+
annotations: {
|
|
5403
|
+
readOnlyHint: false,
|
|
5404
|
+
destructiveHint: true,
|
|
5405
|
+
// Permanently deletes collection
|
|
5406
|
+
idempotentHint: false,
|
|
5407
|
+
// Second call fails (already deleted)
|
|
5408
|
+
openWorldHint: true
|
|
5235
5409
|
}
|
|
5236
5410
|
},
|
|
5237
5411
|
handleDeleteCollection
|
|
@@ -5239,15 +5413,15 @@ function registerDeleteCollectionTool() {
|
|
|
5239
5413
|
}
|
|
5240
5414
|
|
|
5241
5415
|
// src/tools/delete-page.ts
|
|
5242
|
-
import { z as
|
|
5243
|
-
var inputSchema12 =
|
|
5244
|
-
id:
|
|
5416
|
+
import { z as z14 } from "zod";
|
|
5417
|
+
var inputSchema12 = z14.object({
|
|
5418
|
+
id: pageIdSchema.describe(
|
|
5245
5419
|
'The page ID to delete (required). Must be a valid Shopify GID format. Example: "gid://shopify/Page/123456789". Use list-pages to find page IDs. WARNING: This action is permanent and cannot be undone.'
|
|
5246
5420
|
)
|
|
5247
5421
|
});
|
|
5248
|
-
var outputSchema12 =
|
|
5249
|
-
success:
|
|
5250
|
-
deletedPageId:
|
|
5422
|
+
var outputSchema12 = z14.object({
|
|
5423
|
+
success: z14.boolean().describe("Whether the deletion was successful"),
|
|
5424
|
+
deletedPageId: z14.string().describe("The ID of the deleted page")
|
|
5251
5425
|
});
|
|
5252
5426
|
var handleDeletePage = async (context, params) => {
|
|
5253
5427
|
log.debug(`Deleting page on shop: ${context.shopDomain}`);
|
|
@@ -5288,6 +5462,14 @@ function registerDeletePageTool() {
|
|
|
5288
5462
|
relatedTools: ["get-page", "list-pages"],
|
|
5289
5463
|
prerequisites: ["get-page"],
|
|
5290
5464
|
alternatives: ["update-page"]
|
|
5465
|
+
},
|
|
5466
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5467
|
+
annotations: {
|
|
5468
|
+
readOnlyHint: false,
|
|
5469
|
+
destructiveHint: true,
|
|
5470
|
+
idempotentHint: false,
|
|
5471
|
+
// delete-* tools are NOT idempotent
|
|
5472
|
+
openWorldHint: true
|
|
5291
5473
|
}
|
|
5292
5474
|
},
|
|
5293
5475
|
handleDeletePage
|
|
@@ -5295,7 +5477,7 @@ function registerDeletePageTool() {
|
|
|
5295
5477
|
}
|
|
5296
5478
|
|
|
5297
5479
|
// src/tools/delete-product-image.ts
|
|
5298
|
-
import { z as
|
|
5480
|
+
import { z as z15 } from "zod";
|
|
5299
5481
|
var FILE_DELETE_MUTATION = `
|
|
5300
5482
|
mutation FileDelete($fileIds: [ID!]!) {
|
|
5301
5483
|
fileDelete(fileIds: $fileIds) {
|
|
@@ -5307,14 +5489,14 @@ var FILE_DELETE_MUTATION = `
|
|
|
5307
5489
|
}
|
|
5308
5490
|
}
|
|
5309
5491
|
`;
|
|
5310
|
-
var inputSchema13 =
|
|
5311
|
-
imageId:
|
|
5492
|
+
var inputSchema13 = z15.object({
|
|
5493
|
+
imageId: imageIdSchema.describe(
|
|
5312
5494
|
'Shopify image GID to delete (e.g., "gid://shopify/MediaImage/123"). Required. Use get-product to find image IDs in the images array.'
|
|
5313
5495
|
)
|
|
5314
5496
|
});
|
|
5315
|
-
var outputSchema13 =
|
|
5316
|
-
success:
|
|
5317
|
-
deletedImageId:
|
|
5497
|
+
var outputSchema13 = z15.object({
|
|
5498
|
+
success: z15.boolean().describe("Whether the deletion was successful"),
|
|
5499
|
+
deletedImageId: z15.string().describe("ID of the deleted image")
|
|
5318
5500
|
});
|
|
5319
5501
|
var handleDeleteProductImage = async (context, params) => {
|
|
5320
5502
|
log.debug(`Deleting image on shop: ${context.shopDomain}`);
|
|
@@ -5372,6 +5554,15 @@ function registerDeleteProductImageTool() {
|
|
|
5372
5554
|
relationships: {
|
|
5373
5555
|
relatedTools: ["get-product", "add-product-image"],
|
|
5374
5556
|
prerequisites: ["get-product"]
|
|
5557
|
+
},
|
|
5558
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5559
|
+
annotations: {
|
|
5560
|
+
readOnlyHint: false,
|
|
5561
|
+
destructiveHint: true,
|
|
5562
|
+
// Permanently deletes image from Shopify CDN
|
|
5563
|
+
idempotentHint: false,
|
|
5564
|
+
// Second call fails (already deleted)
|
|
5565
|
+
openWorldHint: true
|
|
5375
5566
|
}
|
|
5376
5567
|
},
|
|
5377
5568
|
handleDeleteProductImage
|
|
@@ -5379,7 +5570,7 @@ function registerDeleteProductImageTool() {
|
|
|
5379
5570
|
}
|
|
5380
5571
|
|
|
5381
5572
|
// src/tools/delete-product-metafields.ts
|
|
5382
|
-
import { z as
|
|
5573
|
+
import { z as z16 } from "zod";
|
|
5383
5574
|
|
|
5384
5575
|
// src/shopify/metafields.ts
|
|
5385
5576
|
var GET_PRODUCT_METAFIELDS_QUERY = `
|
|
@@ -5558,28 +5749,28 @@ async function deleteProductMetafields(productId, identifiers) {
|
|
|
5558
5749
|
}
|
|
5559
5750
|
|
|
5560
5751
|
// src/tools/delete-product-metafields.ts
|
|
5561
|
-
var metafieldIdentifierSchema =
|
|
5562
|
-
namespace:
|
|
5563
|
-
key:
|
|
5752
|
+
var metafieldIdentifierSchema = z16.object({
|
|
5753
|
+
namespace: z16.string().min(1).describe('Metafield namespace (e.g., "custom", "seo"). Must match existing metafield.'),
|
|
5754
|
+
key: z16.string().min(1).describe('Metafield key (e.g., "color_hex"). Must match existing metafield.')
|
|
5564
5755
|
});
|
|
5565
|
-
var inputSchema14 =
|
|
5566
|
-
productId:
|
|
5756
|
+
var inputSchema14 = z16.object({
|
|
5757
|
+
productId: productIdSchema.describe(
|
|
5567
5758
|
'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use get-product or list-products to find product IDs.'
|
|
5568
5759
|
),
|
|
5569
|
-
identifiers:
|
|
5760
|
+
identifiers: z16.array(metafieldIdentifierSchema).min(1, "At least one metafield identifier is required").describe(
|
|
5570
5761
|
"Array of metafield identifiers to delete. Each identifier must have namespace and key. Use get-product-metafields first to verify which metafields exist."
|
|
5571
5762
|
)
|
|
5572
5763
|
});
|
|
5573
|
-
var outputSchema14 =
|
|
5574
|
-
success:
|
|
5575
|
-
deletedMetafields:
|
|
5576
|
-
|
|
5577
|
-
ownerId:
|
|
5578
|
-
namespace:
|
|
5579
|
-
key:
|
|
5764
|
+
var outputSchema14 = z16.object({
|
|
5765
|
+
success: z16.boolean().describe("Whether the operation succeeded"),
|
|
5766
|
+
deletedMetafields: z16.array(
|
|
5767
|
+
z16.object({
|
|
5768
|
+
ownerId: z16.string().describe("Product GID that owned the metafield"),
|
|
5769
|
+
namespace: z16.string().describe("Namespace of deleted metafield"),
|
|
5770
|
+
key: z16.string().describe("Key of deleted metafield")
|
|
5580
5771
|
})
|
|
5581
5772
|
).describe("Identifiers of successfully deleted metafields"),
|
|
5582
|
-
deletedCount:
|
|
5773
|
+
deletedCount: z16.number().describe("Number of metafields deleted")
|
|
5583
5774
|
});
|
|
5584
5775
|
var handleDeleteProductMetafields = async (context, params) => {
|
|
5585
5776
|
log.debug(
|
|
@@ -5616,6 +5807,15 @@ function registerDeleteProductMetafieldsTool() {
|
|
|
5616
5807
|
relationships: {
|
|
5617
5808
|
relatedTools: ["get-product-metafields"],
|
|
5618
5809
|
prerequisites: ["get-product-metafields"]
|
|
5810
|
+
},
|
|
5811
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5812
|
+
annotations: {
|
|
5813
|
+
readOnlyHint: false,
|
|
5814
|
+
destructiveHint: true,
|
|
5815
|
+
// Partial data deletion - cannot be recovered
|
|
5816
|
+
idempotentHint: false,
|
|
5817
|
+
// Second call fails (already deleted)
|
|
5818
|
+
openWorldHint: true
|
|
5619
5819
|
}
|
|
5620
5820
|
},
|
|
5621
5821
|
handleDeleteProductMetafields
|
|
@@ -5623,13 +5823,15 @@ function registerDeleteProductMetafieldsTool() {
|
|
|
5623
5823
|
}
|
|
5624
5824
|
|
|
5625
5825
|
// src/tools/delete-product.ts
|
|
5626
|
-
import { z as
|
|
5627
|
-
var inputSchema15 =
|
|
5628
|
-
id:
|
|
5826
|
+
import { z as z17 } from "zod";
|
|
5827
|
+
var inputSchema15 = z17.object({
|
|
5828
|
+
id: productIdSchema.describe(
|
|
5829
|
+
'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Required.'
|
|
5830
|
+
)
|
|
5629
5831
|
});
|
|
5630
|
-
var outputSchema15 =
|
|
5631
|
-
deletedProductId:
|
|
5632
|
-
title:
|
|
5832
|
+
var outputSchema15 = z17.object({
|
|
5833
|
+
deletedProductId: z17.string().describe("The ID of the deleted product"),
|
|
5834
|
+
title: z17.string().describe("The title of the deleted product (for confirmation)")
|
|
5633
5835
|
});
|
|
5634
5836
|
var handleDeleteProduct = async (context, params) => {
|
|
5635
5837
|
log.debug(`Deleting product on shop: ${context.shopDomain}`);
|
|
@@ -5678,6 +5880,15 @@ function registerDeleteProductTool() {
|
|
|
5678
5880
|
relationships: {
|
|
5679
5881
|
relatedTools: ["list-products"],
|
|
5680
5882
|
prerequisites: ["get-product"]
|
|
5883
|
+
},
|
|
5884
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5885
|
+
annotations: {
|
|
5886
|
+
readOnlyHint: false,
|
|
5887
|
+
destructiveHint: true,
|
|
5888
|
+
// Permanently deletes product
|
|
5889
|
+
idempotentHint: false,
|
|
5890
|
+
// Second call fails (already deleted)
|
|
5891
|
+
openWorldHint: true
|
|
5681
5892
|
}
|
|
5682
5893
|
},
|
|
5683
5894
|
handleDeleteProduct
|
|
@@ -5685,15 +5896,15 @@ function registerDeleteProductTool() {
|
|
|
5685
5896
|
}
|
|
5686
5897
|
|
|
5687
5898
|
// src/tools/delete-redirect.ts
|
|
5688
|
-
import { z as
|
|
5689
|
-
var inputSchema16 =
|
|
5690
|
-
id:
|
|
5899
|
+
import { z as z18 } from "zod";
|
|
5900
|
+
var inputSchema16 = z18.object({
|
|
5901
|
+
id: redirectIdSchema.describe(
|
|
5691
5902
|
'Shopify redirect GID (e.g., "gid://shopify/UrlRedirect/123"). Use list-redirects to find redirect IDs.'
|
|
5692
5903
|
)
|
|
5693
5904
|
});
|
|
5694
|
-
var outputSchema16 =
|
|
5695
|
-
success:
|
|
5696
|
-
deletedRedirectId:
|
|
5905
|
+
var outputSchema16 = z18.object({
|
|
5906
|
+
success: z18.boolean().describe("Whether the deletion succeeded"),
|
|
5907
|
+
deletedRedirectId: z18.string().describe("ID of the deleted redirect")
|
|
5697
5908
|
});
|
|
5698
5909
|
var handleDeleteRedirect = async (context, params) => {
|
|
5699
5910
|
log.debug(`Deleting redirect on shop: ${context.shopDomain}`);
|
|
@@ -5742,6 +5953,15 @@ function registerDeleteRedirectTool() {
|
|
|
5742
5953
|
relationships: {
|
|
5743
5954
|
relatedTools: ["create-redirect"],
|
|
5744
5955
|
prerequisites: ["list-redirects"]
|
|
5956
|
+
},
|
|
5957
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
5958
|
+
annotations: {
|
|
5959
|
+
readOnlyHint: false,
|
|
5960
|
+
destructiveHint: true,
|
|
5961
|
+
// Permanently removes redirect - can hurt SEO
|
|
5962
|
+
idempotentHint: false,
|
|
5963
|
+
// Second call fails (already deleted)
|
|
5964
|
+
openWorldHint: true
|
|
5745
5965
|
}
|
|
5746
5966
|
},
|
|
5747
5967
|
handleDeleteRedirect
|
|
@@ -5749,7 +5969,7 @@ function registerDeleteRedirectTool() {
|
|
|
5749
5969
|
}
|
|
5750
5970
|
|
|
5751
5971
|
// src/tools/get-bulk-inventory.ts
|
|
5752
|
-
import { z as
|
|
5972
|
+
import { z as z19 } from "zod";
|
|
5753
5973
|
|
|
5754
5974
|
// src/shopify/inventory.ts
|
|
5755
5975
|
var GET_INVENTORY_ITEM_QUERY = `
|
|
@@ -6320,46 +6540,46 @@ async function getBulkInventory(options) {
|
|
|
6320
6540
|
}
|
|
6321
6541
|
|
|
6322
6542
|
// src/tools/get-bulk-inventory.ts
|
|
6323
|
-
var inputSchema17 =
|
|
6324
|
-
productIds:
|
|
6543
|
+
var inputSchema17 = z19.object({
|
|
6544
|
+
productIds: z19.array(productIdSchema).min(1, { message: "productIds array cannot be empty" }).max(50, { message: "Maximum 50 product IDs allowed per request" }).describe(
|
|
6325
6545
|
'Array of Shopify product IDs (e.g., ["gid://shopify/Product/123", "gid://shopify/Product/456"]). Maximum 50 products per request. Get product IDs from list-products or search.'
|
|
6326
6546
|
),
|
|
6327
|
-
includeVariants:
|
|
6547
|
+
includeVariants: z19.boolean().default(true).describe(
|
|
6328
6548
|
"Include per-variant inventory breakdown. Default: true. Set to false for faster response with only product totals."
|
|
6329
6549
|
),
|
|
6330
|
-
locationId:
|
|
6550
|
+
locationId: locationIdSchema.optional().describe(
|
|
6331
6551
|
'Optional location ID to filter inventory (e.g., "gid://shopify/Location/789"). If not provided, returns total inventory across all locations.'
|
|
6332
6552
|
)
|
|
6333
6553
|
});
|
|
6334
|
-
var outputSchema17 =
|
|
6335
|
-
products:
|
|
6336
|
-
|
|
6337
|
-
productId:
|
|
6338
|
-
productTitle:
|
|
6339
|
-
totalInventory:
|
|
6340
|
-
variants:
|
|
6341
|
-
|
|
6342
|
-
variantId:
|
|
6343
|
-
variantTitle:
|
|
6344
|
-
sku:
|
|
6345
|
-
inventoryItemId:
|
|
6346
|
-
available:
|
|
6347
|
-
locations:
|
|
6348
|
-
|
|
6349
|
-
locationId:
|
|
6350
|
-
locationName:
|
|
6351
|
-
available:
|
|
6554
|
+
var outputSchema17 = z19.object({
|
|
6555
|
+
products: z19.array(
|
|
6556
|
+
z19.object({
|
|
6557
|
+
productId: z19.string().describe("Product GID"),
|
|
6558
|
+
productTitle: z19.string().describe("Product title"),
|
|
6559
|
+
totalInventory: z19.number().describe("Total inventory across all variants"),
|
|
6560
|
+
variants: z19.array(
|
|
6561
|
+
z19.object({
|
|
6562
|
+
variantId: z19.string().describe("Variant GID"),
|
|
6563
|
+
variantTitle: z19.string().describe('Variant title (e.g., "Red / Large")'),
|
|
6564
|
+
sku: z19.string().nullable().describe("SKU (Stock Keeping Unit)"),
|
|
6565
|
+
inventoryItemId: z19.string().describe("Inventory item GID"),
|
|
6566
|
+
available: z19.number().describe("Available quantity"),
|
|
6567
|
+
locations: z19.array(
|
|
6568
|
+
z19.object({
|
|
6569
|
+
locationId: z19.string().describe("Location GID"),
|
|
6570
|
+
locationName: z19.string().describe("Human-readable location name"),
|
|
6571
|
+
available: z19.number().describe("Available quantity at this location")
|
|
6352
6572
|
})
|
|
6353
6573
|
).optional().describe("Per-location inventory breakdown")
|
|
6354
6574
|
})
|
|
6355
6575
|
).optional().describe("Variant details (if includeVariants=true)")
|
|
6356
6576
|
})
|
|
6357
6577
|
).describe("Products with inventory data"),
|
|
6358
|
-
notFound:
|
|
6359
|
-
summary:
|
|
6360
|
-
productsQueried:
|
|
6361
|
-
productsFound:
|
|
6362
|
-
totalInventory:
|
|
6578
|
+
notFound: z19.array(z19.string()).describe("Product IDs that were not found in the store"),
|
|
6579
|
+
summary: z19.object({
|
|
6580
|
+
productsQueried: z19.number().describe("Total number of product IDs provided"),
|
|
6581
|
+
productsFound: z19.number().describe("Number of products found in the store"),
|
|
6582
|
+
totalInventory: z19.number().describe("Sum of inventory across all found products")
|
|
6363
6583
|
}).describe("Summary statistics")
|
|
6364
6584
|
});
|
|
6365
6585
|
var handleGetBulkInventory = async (context, params) => {
|
|
@@ -6399,6 +6619,13 @@ function registerGetBulkInventoryTool() {
|
|
|
6399
6619
|
prerequisites: ["list-products"],
|
|
6400
6620
|
followUps: ["update-inventory", "get-inventory"],
|
|
6401
6621
|
alternatives: ["get-inventory"]
|
|
6622
|
+
},
|
|
6623
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
6624
|
+
annotations: {
|
|
6625
|
+
readOnlyHint: true,
|
|
6626
|
+
destructiveHint: false,
|
|
6627
|
+
idempotentHint: true,
|
|
6628
|
+
openWorldHint: true
|
|
6402
6629
|
}
|
|
6403
6630
|
},
|
|
6404
6631
|
handleGetBulkInventory
|
|
@@ -6406,30 +6633,30 @@ function registerGetBulkInventoryTool() {
|
|
|
6406
6633
|
}
|
|
6407
6634
|
|
|
6408
6635
|
// src/tools/get-collection.ts
|
|
6409
|
-
import { z as
|
|
6410
|
-
var inputSchema18 =
|
|
6411
|
-
collectionId:
|
|
6636
|
+
import { z as z20 } from "zod";
|
|
6637
|
+
var inputSchema18 = z20.object({
|
|
6638
|
+
collectionId: collectionIdSchema.describe(
|
|
6412
6639
|
'Collection ID (GID format, e.g., "gid://shopify/Collection/123"). Use list-collections to find collection IDs.'
|
|
6413
6640
|
)
|
|
6414
6641
|
});
|
|
6415
|
-
var outputSchema18 =
|
|
6416
|
-
id:
|
|
6417
|
-
title:
|
|
6418
|
-
handle:
|
|
6419
|
-
description:
|
|
6420
|
-
descriptionHtml:
|
|
6421
|
-
seo:
|
|
6422
|
-
title:
|
|
6423
|
-
description:
|
|
6642
|
+
var outputSchema18 = z20.object({
|
|
6643
|
+
id: z20.string().describe("Collection GID"),
|
|
6644
|
+
title: z20.string().describe("Collection title"),
|
|
6645
|
+
handle: z20.string().describe("URL handle/slug"),
|
|
6646
|
+
description: z20.string().describe("Plain text description"),
|
|
6647
|
+
descriptionHtml: z20.string().describe("HTML description"),
|
|
6648
|
+
seo: z20.object({
|
|
6649
|
+
title: z20.string().nullable().describe("SEO title"),
|
|
6650
|
+
description: z20.string().nullable().describe("SEO description")
|
|
6424
6651
|
}),
|
|
6425
|
-
image:
|
|
6426
|
-
id:
|
|
6427
|
-
url:
|
|
6428
|
-
altText:
|
|
6652
|
+
image: z20.object({
|
|
6653
|
+
id: z20.string().describe("Image GID"),
|
|
6654
|
+
url: z20.string().describe("Image URL"),
|
|
6655
|
+
altText: z20.string().nullable().describe("Alt text")
|
|
6429
6656
|
}).nullable(),
|
|
6430
|
-
sortOrder:
|
|
6431
|
-
productsCount:
|
|
6432
|
-
updatedAt:
|
|
6657
|
+
sortOrder: z20.string().describe("Product sort order within collection"),
|
|
6658
|
+
productsCount: z20.number().describe("Number of products in collection"),
|
|
6659
|
+
updatedAt: z20.string().describe("Last update timestamp (ISO 8601)")
|
|
6433
6660
|
});
|
|
6434
6661
|
var handleGetCollection = async (context, params) => {
|
|
6435
6662
|
log.debug(`Getting collection on shop: ${context.shopDomain}`);
|
|
@@ -6456,6 +6683,13 @@ function registerGetCollectionTool() {
|
|
|
6456
6683
|
relationships: {
|
|
6457
6684
|
relatedTools: ["list-collections"],
|
|
6458
6685
|
followUps: ["update-collection", "delete-collection", "add-products-to-collection"]
|
|
6686
|
+
},
|
|
6687
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
6688
|
+
annotations: {
|
|
6689
|
+
readOnlyHint: true,
|
|
6690
|
+
destructiveHint: false,
|
|
6691
|
+
idempotentHint: true,
|
|
6692
|
+
openWorldHint: true
|
|
6459
6693
|
}
|
|
6460
6694
|
},
|
|
6461
6695
|
handleGetCollection
|
|
@@ -6463,37 +6697,37 @@ function registerGetCollectionTool() {
|
|
|
6463
6697
|
}
|
|
6464
6698
|
|
|
6465
6699
|
// src/tools/get-inventory.ts
|
|
6466
|
-
import { z as
|
|
6467
|
-
var inputSchema19 =
|
|
6468
|
-
variantId:
|
|
6700
|
+
import { z as z21 } from "zod";
|
|
6701
|
+
var inputSchema19 = z21.object({
|
|
6702
|
+
variantId: variantIdSchema.optional().describe(
|
|
6469
6703
|
'The Shopify product variant ID (e.g., "gid://shopify/ProductVariant/123"). Use this when you have a variant ID from a product query (get-product or list-products).'
|
|
6470
6704
|
),
|
|
6471
|
-
inventoryItemId:
|
|
6705
|
+
inventoryItemId: inventoryItemIdSchema.optional().describe(
|
|
6472
6706
|
'The Shopify inventory item ID (e.g., "gid://shopify/InventoryItem/456"). Use this if you already have the inventory item ID from a previous query.'
|
|
6473
6707
|
),
|
|
6474
|
-
locationId:
|
|
6708
|
+
locationId: locationIdSchema.optional().describe(
|
|
6475
6709
|
'Optional location ID to filter results (e.g., "gid://shopify/Location/789"). If not provided, returns inventory at all locations.'
|
|
6476
6710
|
)
|
|
6477
6711
|
}).refine((data) => data.variantId || data.inventoryItemId, {
|
|
6478
6712
|
message: "Either variantId or inventoryItemId must be provided"
|
|
6479
6713
|
});
|
|
6480
|
-
var outputSchema19 =
|
|
6481
|
-
inventoryItemId:
|
|
6482
|
-
variantId:
|
|
6483
|
-
variantTitle:
|
|
6484
|
-
productId:
|
|
6485
|
-
productTitle:
|
|
6486
|
-
sku:
|
|
6487
|
-
tracked:
|
|
6488
|
-
totalAvailable:
|
|
6489
|
-
locations:
|
|
6490
|
-
|
|
6491
|
-
locationId:
|
|
6492
|
-
locationName:
|
|
6493
|
-
isActive:
|
|
6494
|
-
available:
|
|
6495
|
-
onHand:
|
|
6496
|
-
committed:
|
|
6714
|
+
var outputSchema19 = z21.object({
|
|
6715
|
+
inventoryItemId: z21.string().describe("Inventory item GID"),
|
|
6716
|
+
variantId: z21.string().describe("Product variant GID"),
|
|
6717
|
+
variantTitle: z21.string().describe('Variant title (e.g., "Red / Large")'),
|
|
6718
|
+
productId: z21.string().describe("Product GID"),
|
|
6719
|
+
productTitle: z21.string().describe("Product title"),
|
|
6720
|
+
sku: z21.string().nullable().describe("SKU (Stock Keeping Unit)"),
|
|
6721
|
+
tracked: z21.boolean().describe("Whether inventory tracking is enabled for this item"),
|
|
6722
|
+
totalAvailable: z21.number().describe("Total available quantity across all locations"),
|
|
6723
|
+
locations: z21.array(
|
|
6724
|
+
z21.object({
|
|
6725
|
+
locationId: z21.string().describe("Location GID"),
|
|
6726
|
+
locationName: z21.string().describe("Human-readable location name"),
|
|
6727
|
+
isActive: z21.boolean().describe("Whether the location is active"),
|
|
6728
|
+
available: z21.number().describe("Quantity available for sale"),
|
|
6729
|
+
onHand: z21.number().describe("Physical quantity on hand"),
|
|
6730
|
+
committed: z21.number().describe("Quantity committed to orders")
|
|
6497
6731
|
})
|
|
6498
6732
|
).describe("Inventory levels by location")
|
|
6499
6733
|
});
|
|
@@ -6567,6 +6801,13 @@ function registerGetInventoryTool() {
|
|
|
6567
6801
|
],
|
|
6568
6802
|
prerequisites: ["get-product", "list-products"],
|
|
6569
6803
|
followUps: ["update-inventory"]
|
|
6804
|
+
},
|
|
6805
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
6806
|
+
annotations: {
|
|
6807
|
+
readOnlyHint: true,
|
|
6808
|
+
destructiveHint: false,
|
|
6809
|
+
idempotentHint: true,
|
|
6810
|
+
openWorldHint: true
|
|
6570
6811
|
}
|
|
6571
6812
|
},
|
|
6572
6813
|
handleGetInventory
|
|
@@ -6574,23 +6815,23 @@ function registerGetInventoryTool() {
|
|
|
6574
6815
|
}
|
|
6575
6816
|
|
|
6576
6817
|
// src/tools/get-page.ts
|
|
6577
|
-
import { z as
|
|
6578
|
-
var inputSchema20 =
|
|
6579
|
-
id:
|
|
6818
|
+
import { z as z22 } from "zod";
|
|
6819
|
+
var inputSchema20 = z22.object({
|
|
6820
|
+
id: pageIdSchema.describe(
|
|
6580
6821
|
'The page ID to retrieve. Must be a valid Shopify GID format. Example: "gid://shopify/Page/123456789". Use list-pages to find page IDs.'
|
|
6581
6822
|
)
|
|
6582
6823
|
});
|
|
6583
|
-
var outputSchema20 =
|
|
6584
|
-
id:
|
|
6585
|
-
title:
|
|
6586
|
-
handle:
|
|
6587
|
-
body:
|
|
6588
|
-
bodySummary:
|
|
6589
|
-
isPublished:
|
|
6590
|
-
publishedAt:
|
|
6591
|
-
templateSuffix:
|
|
6592
|
-
createdAt:
|
|
6593
|
-
updatedAt:
|
|
6824
|
+
var outputSchema20 = z22.object({
|
|
6825
|
+
id: z22.string().describe("Page GID"),
|
|
6826
|
+
title: z22.string().describe("Page title"),
|
|
6827
|
+
handle: z22.string().describe("URL handle/slug"),
|
|
6828
|
+
body: z22.string().describe("HTML body content"),
|
|
6829
|
+
bodySummary: z22.string().describe("Plain text summary (first 150 chars)"),
|
|
6830
|
+
isPublished: z22.boolean().describe("Whether page is visible on storefront"),
|
|
6831
|
+
publishedAt: z22.string().nullable().describe("ISO timestamp when published"),
|
|
6832
|
+
templateSuffix: z22.string().nullable().describe("Custom template suffix"),
|
|
6833
|
+
createdAt: z22.string().describe("ISO timestamp of creation"),
|
|
6834
|
+
updatedAt: z22.string().describe("ISO timestamp of last update")
|
|
6594
6835
|
});
|
|
6595
6836
|
var handleGetPage = async (context, params) => {
|
|
6596
6837
|
log.debug(`Getting page on shop: ${context.shopDomain}`);
|
|
@@ -6617,6 +6858,13 @@ function registerGetPageTool() {
|
|
|
6617
6858
|
relatedTools: ["list-pages", "update-page", "delete-page"],
|
|
6618
6859
|
prerequisites: ["list-pages"],
|
|
6619
6860
|
followUps: ["update-page", "delete-page"]
|
|
6861
|
+
},
|
|
6862
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
6863
|
+
annotations: {
|
|
6864
|
+
readOnlyHint: true,
|
|
6865
|
+
destructiveHint: false,
|
|
6866
|
+
idempotentHint: true,
|
|
6867
|
+
openWorldHint: true
|
|
6620
6868
|
}
|
|
6621
6869
|
},
|
|
6622
6870
|
handleGetPage
|
|
@@ -6624,27 +6872,27 @@ function registerGetPageTool() {
|
|
|
6624
6872
|
}
|
|
6625
6873
|
|
|
6626
6874
|
// src/tools/get-product-metafields.ts
|
|
6627
|
-
import { z as
|
|
6628
|
-
var inputSchema21 =
|
|
6629
|
-
productId:
|
|
6875
|
+
import { z as z23 } from "zod";
|
|
6876
|
+
var inputSchema21 = z23.object({
|
|
6877
|
+
productId: productIdSchema.describe(
|
|
6630
6878
|
'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use get-product or list-products to find product IDs.'
|
|
6631
6879
|
),
|
|
6632
|
-
namespace:
|
|
6880
|
+
namespace: z23.string().optional().describe(
|
|
6633
6881
|
'Filter metafields by namespace (e.g., "custom", "seo", "my_app"). If not provided, returns metafields from all namespaces.'
|
|
6634
6882
|
),
|
|
6635
|
-
keys:
|
|
6883
|
+
keys: z23.array(z23.string()).optional().describe(
|
|
6636
6884
|
'Filter by specific metafield keys within the namespace (e.g., ["color", "size"]). Requires namespace to be specified for meaningful filtering.'
|
|
6637
6885
|
)
|
|
6638
6886
|
});
|
|
6639
|
-
var outputSchema21 =
|
|
6640
|
-
|
|
6641
|
-
id:
|
|
6642
|
-
namespace:
|
|
6643
|
-
key:
|
|
6644
|
-
value:
|
|
6645
|
-
type:
|
|
6646
|
-
createdAt:
|
|
6647
|
-
updatedAt:
|
|
6887
|
+
var outputSchema21 = z23.array(
|
|
6888
|
+
z23.object({
|
|
6889
|
+
id: z23.string().describe('Metafield GID (e.g., "gid://shopify/Metafield/123")'),
|
|
6890
|
+
namespace: z23.string().describe("Metafield namespace (grouping identifier)"),
|
|
6891
|
+
key: z23.string().describe("Metafield key (field name)"),
|
|
6892
|
+
value: z23.string().describe("Metafield value (the stored data)"),
|
|
6893
|
+
type: z23.string().describe('Metafield type (e.g., "single_line_text_field", "json", "number_integer")'),
|
|
6894
|
+
createdAt: z23.string().describe("Creation timestamp (ISO 8601)"),
|
|
6895
|
+
updatedAt: z23.string().describe("Last update timestamp (ISO 8601)")
|
|
6648
6896
|
})
|
|
6649
6897
|
);
|
|
6650
6898
|
var handleGetProductMetafields = async (context, params) => {
|
|
@@ -6677,6 +6925,13 @@ function registerGetProductMetafieldsTool() {
|
|
|
6677
6925
|
relatedTools: ["get-product", "set-product-metafields"],
|
|
6678
6926
|
prerequisites: ["get-product"],
|
|
6679
6927
|
followUps: ["set-product-metafields", "delete-product-metafields"]
|
|
6928
|
+
},
|
|
6929
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
6930
|
+
annotations: {
|
|
6931
|
+
readOnlyHint: true,
|
|
6932
|
+
destructiveHint: false,
|
|
6933
|
+
idempotentHint: true,
|
|
6934
|
+
openWorldHint: true
|
|
6680
6935
|
}
|
|
6681
6936
|
},
|
|
6682
6937
|
handleGetProductMetafields
|
|
@@ -6684,51 +6939,51 @@ function registerGetProductMetafieldsTool() {
|
|
|
6684
6939
|
}
|
|
6685
6940
|
|
|
6686
6941
|
// src/tools/get-product.ts
|
|
6687
|
-
import { z as
|
|
6688
|
-
var inputSchema22 =
|
|
6689
|
-
id:
|
|
6942
|
+
import { z as z24 } from "zod";
|
|
6943
|
+
var inputSchema22 = z24.object({
|
|
6944
|
+
id: productIdSchema.optional().describe(
|
|
6690
6945
|
'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use either id or handle.'
|
|
6691
6946
|
),
|
|
6692
|
-
handle:
|
|
6947
|
+
handle: z24.string().optional().describe(
|
|
6693
6948
|
'Product URL handle/slug (e.g., "premium-cotton-t-shirt"). Use either id or handle.'
|
|
6694
6949
|
)
|
|
6695
6950
|
}).refine((data) => data.id || data.handle, {
|
|
6696
6951
|
message: "Either id or handle must be provided"
|
|
6697
6952
|
});
|
|
6698
|
-
var outputSchema22 =
|
|
6699
|
-
id:
|
|
6700
|
-
title:
|
|
6701
|
-
handle:
|
|
6702
|
-
description:
|
|
6703
|
-
vendor:
|
|
6704
|
-
productType:
|
|
6705
|
-
status:
|
|
6706
|
-
tags:
|
|
6707
|
-
seo:
|
|
6708
|
-
title:
|
|
6709
|
-
description:
|
|
6953
|
+
var outputSchema22 = z24.object({
|
|
6954
|
+
id: z24.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
|
|
6955
|
+
title: z24.string().describe("Product title"),
|
|
6956
|
+
handle: z24.string().describe("URL handle/slug"),
|
|
6957
|
+
description: z24.string().nullable().describe("Product description (HTML)"),
|
|
6958
|
+
vendor: z24.string().describe("Vendor/brand name"),
|
|
6959
|
+
productType: z24.string().describe("Product type"),
|
|
6960
|
+
status: z24.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
|
|
6961
|
+
tags: z24.array(z24.string()).describe("Product tags"),
|
|
6962
|
+
seo: z24.object({
|
|
6963
|
+
title: z24.string().nullable().describe("SEO title for search engines"),
|
|
6964
|
+
description: z24.string().nullable().describe("SEO description/meta description")
|
|
6710
6965
|
}).describe("SEO metadata for search engine optimization"),
|
|
6711
|
-
createdAt:
|
|
6712
|
-
updatedAt:
|
|
6713
|
-
variants:
|
|
6714
|
-
|
|
6715
|
-
id:
|
|
6716
|
-
title:
|
|
6717
|
-
price:
|
|
6718
|
-
compareAtPrice:
|
|
6719
|
-
sku:
|
|
6720
|
-
barcode:
|
|
6721
|
-
inventoryQuantity:
|
|
6966
|
+
createdAt: z24.string().describe("Creation timestamp (ISO 8601)"),
|
|
6967
|
+
updatedAt: z24.string().describe("Last update timestamp (ISO 8601)"),
|
|
6968
|
+
variants: z24.array(
|
|
6969
|
+
z24.object({
|
|
6970
|
+
id: z24.string().describe("Variant GID"),
|
|
6971
|
+
title: z24.string().describe("Variant title"),
|
|
6972
|
+
price: z24.string().describe("Price"),
|
|
6973
|
+
compareAtPrice: z24.string().nullable().describe("Compare at price"),
|
|
6974
|
+
sku: z24.string().nullable().describe("SKU"),
|
|
6975
|
+
barcode: z24.string().nullable().describe("Barcode"),
|
|
6976
|
+
inventoryQuantity: z24.number().nullable().describe("Inventory quantity")
|
|
6722
6977
|
})
|
|
6723
6978
|
).describe("Product variants"),
|
|
6724
|
-
images:
|
|
6725
|
-
|
|
6726
|
-
id:
|
|
6727
|
-
url:
|
|
6728
|
-
altText:
|
|
6979
|
+
images: z24.array(
|
|
6980
|
+
z24.object({
|
|
6981
|
+
id: z24.string().describe("Image GID"),
|
|
6982
|
+
url: z24.string().describe("Image URL"),
|
|
6983
|
+
altText: z24.string().nullable().describe("Alt text")
|
|
6729
6984
|
})
|
|
6730
6985
|
).describe("Product images"),
|
|
6731
|
-
totalInventory:
|
|
6986
|
+
totalInventory: z24.number().describe("Total inventory across variants")
|
|
6732
6987
|
});
|
|
6733
6988
|
var handleGetProduct = async (context, params) => {
|
|
6734
6989
|
log.debug(`Getting product on shop: ${context.shopDomain}`);
|
|
@@ -6779,6 +7034,13 @@ function registerGetProductTool() {
|
|
|
6779
7034
|
"add-product-image",
|
|
6780
7035
|
"get-product-metafields"
|
|
6781
7036
|
]
|
|
7037
|
+
},
|
|
7038
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7039
|
+
annotations: {
|
|
7040
|
+
readOnlyHint: true,
|
|
7041
|
+
destructiveHint: false,
|
|
7042
|
+
idempotentHint: true,
|
|
7043
|
+
openWorldHint: true
|
|
6782
7044
|
}
|
|
6783
7045
|
},
|
|
6784
7046
|
handleGetProduct
|
|
@@ -6786,36 +7048,36 @@ function registerGetProductTool() {
|
|
|
6786
7048
|
}
|
|
6787
7049
|
|
|
6788
7050
|
// src/tools/list-articles.ts
|
|
6789
|
-
import { z as
|
|
6790
|
-
var inputSchema23 =
|
|
6791
|
-
first:
|
|
6792
|
-
cursor:
|
|
7051
|
+
import { z as z25 } from "zod";
|
|
7052
|
+
var inputSchema23 = z25.object({
|
|
7053
|
+
first: z25.number().int().min(1).max(50).optional().default(10).describe("Number of articles to return (1-50). Defaults to 10."),
|
|
7054
|
+
cursor: z25.string().optional().describe(
|
|
6793
7055
|
"Pagination cursor from a previous response. Use the endCursor from the previous response to get the next page of results."
|
|
6794
7056
|
),
|
|
6795
|
-
query:
|
|
7057
|
+
query: z25.string().optional().describe(
|
|
6796
7058
|
"Search query to filter articles. Supports Shopify search syntax. Examples: 'title:Guide', 'tag:tutorial', 'blog_id:123456789'."
|
|
6797
7059
|
),
|
|
6798
|
-
blogId:
|
|
7060
|
+
blogId: z25.string().optional().describe(
|
|
6799
7061
|
'Filter articles by blog ID. Provide the numeric ID (not the full GID) to filter. Example: To filter blog "gid://shopify/Blog/123456", pass blogId as "123456".'
|
|
6800
7062
|
),
|
|
6801
|
-
sortKey:
|
|
7063
|
+
sortKey: z25.enum(["ID", "TITLE", "UPDATED_AT", "PUBLISHED_AT", "AUTHOR", "BLOG_TITLE"]).optional().describe("Sort order for results. Defaults to ID.")
|
|
6802
7064
|
});
|
|
6803
|
-
var outputSchema23 =
|
|
6804
|
-
articles:
|
|
6805
|
-
|
|
6806
|
-
id:
|
|
6807
|
-
title:
|
|
6808
|
-
handle:
|
|
6809
|
-
blog:
|
|
6810
|
-
id:
|
|
6811
|
-
title:
|
|
7065
|
+
var outputSchema23 = z25.object({
|
|
7066
|
+
articles: z25.array(
|
|
7067
|
+
z25.object({
|
|
7068
|
+
id: z25.string().describe("Article GID"),
|
|
7069
|
+
title: z25.string().describe("Article title"),
|
|
7070
|
+
handle: z25.string().describe("URL handle/slug"),
|
|
7071
|
+
blog: z25.object({
|
|
7072
|
+
id: z25.string().describe("Parent blog GID"),
|
|
7073
|
+
title: z25.string().describe("Parent blog title")
|
|
6812
7074
|
}),
|
|
6813
|
-
isPublished:
|
|
6814
|
-
publishedAt:
|
|
7075
|
+
isPublished: z25.boolean().describe("Whether article is visible on storefront"),
|
|
7076
|
+
publishedAt: z25.string().nullable().describe("ISO timestamp when published")
|
|
6815
7077
|
})
|
|
6816
7078
|
),
|
|
6817
|
-
hasNextPage:
|
|
6818
|
-
endCursor:
|
|
7079
|
+
hasNextPage: z25.boolean().describe("Whether more articles are available"),
|
|
7080
|
+
endCursor: z25.string().nullable().describe("Cursor for next page pagination")
|
|
6819
7081
|
});
|
|
6820
7082
|
var handleListArticles = async (context, params) => {
|
|
6821
7083
|
log.debug(`Listing articles on shop: ${context.shopDomain}`);
|
|
@@ -6865,6 +7127,13 @@ function registerListArticlesTool() {
|
|
|
6865
7127
|
relationships: {
|
|
6866
7128
|
relatedTools: ["list-blogs", "create-article", "update-article"],
|
|
6867
7129
|
followUps: ["update-article", "delete-article", "create-article"]
|
|
7130
|
+
},
|
|
7131
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7132
|
+
annotations: {
|
|
7133
|
+
readOnlyHint: true,
|
|
7134
|
+
destructiveHint: false,
|
|
7135
|
+
idempotentHint: true,
|
|
7136
|
+
openWorldHint: true
|
|
6868
7137
|
}
|
|
6869
7138
|
},
|
|
6870
7139
|
handleListArticles
|
|
@@ -6872,29 +7141,29 @@ function registerListArticlesTool() {
|
|
|
6872
7141
|
}
|
|
6873
7142
|
|
|
6874
7143
|
// src/tools/list-blogs.ts
|
|
6875
|
-
import { z as
|
|
6876
|
-
var inputSchema24 =
|
|
6877
|
-
first:
|
|
6878
|
-
cursor:
|
|
7144
|
+
import { z as z26 } from "zod";
|
|
7145
|
+
var inputSchema24 = z26.object({
|
|
7146
|
+
first: z26.number().int().min(1).max(50).optional().default(10).describe("Number of blogs to return (1-50). Defaults to 10."),
|
|
7147
|
+
cursor: z26.string().optional().describe(
|
|
6879
7148
|
"Pagination cursor from a previous response. Use the endCursor from the previous response to get the next page of results."
|
|
6880
7149
|
),
|
|
6881
|
-
query:
|
|
7150
|
+
query: z26.string().optional().describe(
|
|
6882
7151
|
"Search query to filter blogs. Supports Shopify search syntax. Examples: 'title:News', 'handle:company-blog'."
|
|
6883
7152
|
),
|
|
6884
|
-
sortKey:
|
|
7153
|
+
sortKey: z26.enum(["ID", "TITLE", "UPDATED_AT"]).optional().describe("Sort order for results. Defaults to ID.")
|
|
6885
7154
|
});
|
|
6886
|
-
var outputSchema24 =
|
|
6887
|
-
blogs:
|
|
6888
|
-
|
|
6889
|
-
id:
|
|
6890
|
-
title:
|
|
6891
|
-
handle:
|
|
6892
|
-
commentPolicy:
|
|
6893
|
-
articlesCount:
|
|
7155
|
+
var outputSchema24 = z26.object({
|
|
7156
|
+
blogs: z26.array(
|
|
7157
|
+
z26.object({
|
|
7158
|
+
id: z26.string().describe("Blog GID"),
|
|
7159
|
+
title: z26.string().describe("Blog title"),
|
|
7160
|
+
handle: z26.string().describe("URL handle/slug"),
|
|
7161
|
+
commentPolicy: z26.enum(["CLOSED", "MODERATE", "OPEN"]).describe("Comment moderation policy"),
|
|
7162
|
+
articlesCount: z26.number().describe("Number of articles in the blog")
|
|
6894
7163
|
})
|
|
6895
7164
|
),
|
|
6896
|
-
hasNextPage:
|
|
6897
|
-
endCursor:
|
|
7165
|
+
hasNextPage: z26.boolean().describe("Whether more blogs are available"),
|
|
7166
|
+
endCursor: z26.string().nullable().describe("Cursor for next page pagination")
|
|
6898
7167
|
});
|
|
6899
7168
|
var handleListBlogs = async (context, params) => {
|
|
6900
7169
|
log.debug(`Listing blogs on shop: ${context.shopDomain}`);
|
|
@@ -6938,6 +7207,13 @@ function registerListBlogsTool() {
|
|
|
6938
7207
|
relationships: {
|
|
6939
7208
|
relatedTools: ["create-blog", "update-blog", "list-articles"],
|
|
6940
7209
|
followUps: ["create-article", "update-blog", "create-blog"]
|
|
7210
|
+
},
|
|
7211
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7212
|
+
annotations: {
|
|
7213
|
+
readOnlyHint: true,
|
|
7214
|
+
destructiveHint: false,
|
|
7215
|
+
idempotentHint: true,
|
|
7216
|
+
openWorldHint: true
|
|
6941
7217
|
}
|
|
6942
7218
|
},
|
|
6943
7219
|
handleListBlogs
|
|
@@ -6945,40 +7221,40 @@ function registerListBlogsTool() {
|
|
|
6945
7221
|
}
|
|
6946
7222
|
|
|
6947
7223
|
// src/tools/list-collections.ts
|
|
6948
|
-
import { z as
|
|
6949
|
-
var inputSchema25 =
|
|
6950
|
-
first:
|
|
6951
|
-
after:
|
|
7224
|
+
import { z as z27 } from "zod";
|
|
7225
|
+
var inputSchema25 = z27.object({
|
|
7226
|
+
first: z27.number().int().min(1).max(50).optional().default(10).describe("Number of collections to return (1-50). Default: 10"),
|
|
7227
|
+
after: z27.string().optional().describe(
|
|
6952
7228
|
"Pagination cursor from previous response. Use the endCursor value from the previous page to get the next page of results."
|
|
6953
7229
|
),
|
|
6954
|
-
query:
|
|
7230
|
+
query: z27.string().optional().describe(
|
|
6955
7231
|
'Search query to filter collections. Searches across collection titles and handles. Example: "summer" finds collections with "summer" in the title or handle.'
|
|
6956
7232
|
),
|
|
6957
|
-
sortKey:
|
|
7233
|
+
sortKey: z27.enum(["TITLE", "UPDATED_AT", "ID", "RELEVANCE"]).optional().describe(
|
|
6958
7234
|
"Sort collections by: TITLE (alphabetical), UPDATED_AT (most recently updated first), ID (by collection ID), or RELEVANCE (best match when using query filter)"
|
|
6959
7235
|
)
|
|
6960
7236
|
});
|
|
6961
|
-
var outputSchema25 =
|
|
6962
|
-
collections:
|
|
6963
|
-
|
|
6964
|
-
id:
|
|
6965
|
-
title:
|
|
6966
|
-
handle:
|
|
6967
|
-
seo:
|
|
6968
|
-
title:
|
|
6969
|
-
description:
|
|
7237
|
+
var outputSchema25 = z27.object({
|
|
7238
|
+
collections: z27.array(
|
|
7239
|
+
z27.object({
|
|
7240
|
+
id: z27.string().describe("Collection GID"),
|
|
7241
|
+
title: z27.string().describe("Collection title"),
|
|
7242
|
+
handle: z27.string().describe("URL handle"),
|
|
7243
|
+
seo: z27.object({
|
|
7244
|
+
title: z27.string().nullable().describe("SEO title"),
|
|
7245
|
+
description: z27.string().nullable().describe("SEO description")
|
|
6970
7246
|
}),
|
|
6971
|
-
productsCount:
|
|
7247
|
+
productsCount: z27.number().describe("Number of products in collection")
|
|
6972
7248
|
})
|
|
6973
7249
|
),
|
|
6974
|
-
pageInfo:
|
|
6975
|
-
hasNextPage:
|
|
6976
|
-
endCursor:
|
|
7250
|
+
pageInfo: z27.object({
|
|
7251
|
+
hasNextPage: z27.boolean().describe("Whether more pages exist"),
|
|
7252
|
+
endCursor: z27.string().nullable().describe("Cursor for next page")
|
|
6977
7253
|
}),
|
|
6978
|
-
summary:
|
|
6979
|
-
totalReturned:
|
|
6980
|
-
hasMore:
|
|
6981
|
-
hint:
|
|
7254
|
+
summary: z27.object({
|
|
7255
|
+
totalReturned: z27.number().describe("Number of collections in this response"),
|
|
7256
|
+
hasMore: z27.boolean().describe("Whether more collections are available"),
|
|
7257
|
+
hint: z27.string().describe("Suggestion for next action")
|
|
6982
7258
|
}).describe("AI-friendly summary for context management")
|
|
6983
7259
|
});
|
|
6984
7260
|
var handleListCollections = async (context, params) => {
|
|
@@ -7012,6 +7288,13 @@ function registerListCollectionsTool() {
|
|
|
7012
7288
|
relationships: {
|
|
7013
7289
|
relatedTools: ["get-collection"],
|
|
7014
7290
|
followUps: ["get-collection", "update-collection", "add-products-to-collection"]
|
|
7291
|
+
},
|
|
7292
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7293
|
+
annotations: {
|
|
7294
|
+
readOnlyHint: true,
|
|
7295
|
+
destructiveHint: false,
|
|
7296
|
+
idempotentHint: true,
|
|
7297
|
+
openWorldHint: true
|
|
7015
7298
|
}
|
|
7016
7299
|
},
|
|
7017
7300
|
handleListCollections
|
|
@@ -7019,47 +7302,47 @@ function registerListCollectionsTool() {
|
|
|
7019
7302
|
}
|
|
7020
7303
|
|
|
7021
7304
|
// src/tools/list-low-inventory.ts
|
|
7022
|
-
import { z as
|
|
7023
|
-
var inputSchema26 =
|
|
7024
|
-
threshold:
|
|
7305
|
+
import { z as z28 } from "zod";
|
|
7306
|
+
var inputSchema26 = z28.object({
|
|
7307
|
+
threshold: z28.number().int().min(0).default(10).describe(
|
|
7025
7308
|
"Products with total inventory at or below this number will be included. Default: 10. Use 0 to find only out-of-stock products."
|
|
7026
7309
|
),
|
|
7027
|
-
includeZero:
|
|
7310
|
+
includeZero: z28.boolean().default(true).describe(
|
|
7028
7311
|
"Include products with zero inventory. Default: true. Set to false to see only low-stock (not out-of-stock) products."
|
|
7029
7312
|
),
|
|
7030
|
-
status:
|
|
7313
|
+
status: z28.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe(
|
|
7031
7314
|
"Filter by product status. Default: ACTIVE. Use DRAFT to check pre-launch products, ARCHIVED for historical."
|
|
7032
7315
|
),
|
|
7033
|
-
first:
|
|
7034
|
-
after:
|
|
7316
|
+
first: z28.number().int().min(1).max(100).default(50).describe("Number of products to return per page. Default: 50, max: 100."),
|
|
7317
|
+
after: z28.string().optional().describe("Pagination cursor from previous response. Use pageInfo.endCursor to get next page.")
|
|
7035
7318
|
});
|
|
7036
|
-
var outputSchema26 =
|
|
7037
|
-
products:
|
|
7038
|
-
|
|
7039
|
-
productId:
|
|
7040
|
-
productTitle:
|
|
7041
|
-
status:
|
|
7042
|
-
totalInventory:
|
|
7043
|
-
isOutOfStock:
|
|
7044
|
-
variants:
|
|
7045
|
-
|
|
7046
|
-
variantId:
|
|
7047
|
-
variantTitle:
|
|
7048
|
-
sku:
|
|
7049
|
-
inventoryItemId:
|
|
7050
|
-
available:
|
|
7319
|
+
var outputSchema26 = z28.object({
|
|
7320
|
+
products: z28.array(
|
|
7321
|
+
z28.object({
|
|
7322
|
+
productId: z28.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
|
|
7323
|
+
productTitle: z28.string().describe("Product title"),
|
|
7324
|
+
status: z28.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
|
|
7325
|
+
totalInventory: z28.number().describe("Total inventory across all variants"),
|
|
7326
|
+
isOutOfStock: z28.boolean().describe("True if total inventory is zero"),
|
|
7327
|
+
variants: z28.array(
|
|
7328
|
+
z28.object({
|
|
7329
|
+
variantId: z28.string().describe("Variant GID"),
|
|
7330
|
+
variantTitle: z28.string().describe('Variant title (e.g., "Red / Large")'),
|
|
7331
|
+
sku: z28.string().nullable().describe("SKU (Stock Keeping Unit)"),
|
|
7332
|
+
inventoryItemId: z28.string().describe("Inventory item GID"),
|
|
7333
|
+
available: z28.number().describe("Available quantity for sale")
|
|
7051
7334
|
})
|
|
7052
7335
|
).describe("Variant breakdown with inventory")
|
|
7053
7336
|
})
|
|
7054
7337
|
).describe("Products with low inventory, sorted by total inventory ascending"),
|
|
7055
|
-
pageInfo:
|
|
7056
|
-
hasNextPage:
|
|
7057
|
-
endCursor:
|
|
7338
|
+
pageInfo: z28.object({
|
|
7339
|
+
hasNextPage: z28.boolean().describe("Whether more products exist after this page"),
|
|
7340
|
+
endCursor: z28.string().nullable().describe('Cursor for the last item - use as "after" to get next page')
|
|
7058
7341
|
}).describe("Pagination information"),
|
|
7059
|
-
summary:
|
|
7060
|
-
totalProducts:
|
|
7061
|
-
outOfStockCount:
|
|
7062
|
-
lowStockCount:
|
|
7342
|
+
summary: z28.object({
|
|
7343
|
+
totalProducts: z28.number().describe("Number of products in this response"),
|
|
7344
|
+
outOfStockCount: z28.number().describe("Number of products with zero inventory"),
|
|
7345
|
+
lowStockCount: z28.number().describe("Number of products with inventory > 0 but below threshold")
|
|
7063
7346
|
}).describe("Summary statistics")
|
|
7064
7347
|
});
|
|
7065
7348
|
var handleListLowInventory = async (context, params) => {
|
|
@@ -7089,6 +7372,13 @@ function registerListLowInventoryTool() {
|
|
|
7089
7372
|
relationships: {
|
|
7090
7373
|
relatedTools: ["get-inventory", "update-inventory", "get-bulk-inventory"],
|
|
7091
7374
|
followUps: ["get-inventory", "update-inventory"]
|
|
7375
|
+
},
|
|
7376
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7377
|
+
annotations: {
|
|
7378
|
+
readOnlyHint: true,
|
|
7379
|
+
destructiveHint: false,
|
|
7380
|
+
idempotentHint: true,
|
|
7381
|
+
openWorldHint: true
|
|
7092
7382
|
}
|
|
7093
7383
|
},
|
|
7094
7384
|
handleListLowInventory
|
|
@@ -7096,31 +7386,31 @@ function registerListLowInventoryTool() {
|
|
|
7096
7386
|
}
|
|
7097
7387
|
|
|
7098
7388
|
// src/tools/list-pages.ts
|
|
7099
|
-
import { z as
|
|
7100
|
-
var inputSchema27 =
|
|
7101
|
-
first:
|
|
7389
|
+
import { z as z29 } from "zod";
|
|
7390
|
+
var inputSchema27 = z29.object({
|
|
7391
|
+
first: z29.number().int().min(1).max(50).optional().describe(
|
|
7102
7392
|
"Number of pages to return per request. Default: 10, Maximum: 50. Use pagination (cursor) to retrieve more results."
|
|
7103
7393
|
),
|
|
7104
|
-
cursor:
|
|
7394
|
+
cursor: z29.string().optional().describe(
|
|
7105
7395
|
"Pagination cursor from a previous list-pages response (endCursor). Use this to get the next page of results."
|
|
7106
7396
|
),
|
|
7107
|
-
query:
|
|
7397
|
+
query: z29.string().optional().describe(
|
|
7108
7398
|
"Search query to filter pages. Supports Shopify search syntax. Examples: 'title:About', 'handle:contact', 'created_at:>2024-01-01'."
|
|
7109
7399
|
),
|
|
7110
|
-
sortKey:
|
|
7400
|
+
sortKey: z29.enum(["ID", "TITLE", "UPDATED_AT", "PUBLISHED_AT"]).optional().describe("Field to sort pages by. Options: ID (default), TITLE, UPDATED_AT, PUBLISHED_AT.")
|
|
7111
7401
|
});
|
|
7112
|
-
var outputSchema27 =
|
|
7113
|
-
pages:
|
|
7114
|
-
|
|
7115
|
-
id:
|
|
7116
|
-
title:
|
|
7117
|
-
handle:
|
|
7118
|
-
isPublished:
|
|
7119
|
-
updatedAt:
|
|
7402
|
+
var outputSchema27 = z29.object({
|
|
7403
|
+
pages: z29.array(
|
|
7404
|
+
z29.object({
|
|
7405
|
+
id: z29.string().describe("Page GID"),
|
|
7406
|
+
title: z29.string().describe("Page title"),
|
|
7407
|
+
handle: z29.string().describe("URL handle/slug"),
|
|
7408
|
+
isPublished: z29.boolean().describe("Whether page is visible"),
|
|
7409
|
+
updatedAt: z29.string().describe("ISO timestamp of last update")
|
|
7120
7410
|
})
|
|
7121
7411
|
),
|
|
7122
|
-
hasNextPage:
|
|
7123
|
-
endCursor:
|
|
7412
|
+
hasNextPage: z29.boolean().describe("Whether more pages exist"),
|
|
7413
|
+
endCursor: z29.string().nullable().describe("Cursor for next page of results")
|
|
7124
7414
|
});
|
|
7125
7415
|
var handleListPages = async (context, params) => {
|
|
7126
7416
|
log.debug(`Listing pages on shop: ${context.shopDomain}`);
|
|
@@ -7152,6 +7442,13 @@ function registerListPagesTool() {
|
|
|
7152
7442
|
relationships: {
|
|
7153
7443
|
relatedTools: ["get-page", "create-page"],
|
|
7154
7444
|
followUps: ["get-page", "update-page", "create-page"]
|
|
7445
|
+
},
|
|
7446
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7447
|
+
annotations: {
|
|
7448
|
+
readOnlyHint: true,
|
|
7449
|
+
destructiveHint: false,
|
|
7450
|
+
idempotentHint: true,
|
|
7451
|
+
openWorldHint: true
|
|
7155
7452
|
}
|
|
7156
7453
|
},
|
|
7157
7454
|
handleListPages
|
|
@@ -7159,46 +7456,46 @@ function registerListPagesTool() {
|
|
|
7159
7456
|
}
|
|
7160
7457
|
|
|
7161
7458
|
// src/tools/list-products.ts
|
|
7162
|
-
import { z as
|
|
7163
|
-
var inputSchema28 =
|
|
7164
|
-
first:
|
|
7165
|
-
after:
|
|
7166
|
-
query:
|
|
7459
|
+
import { z as z30 } from "zod";
|
|
7460
|
+
var inputSchema28 = z30.object({
|
|
7461
|
+
first: z30.number().int().min(1).max(50).optional().describe("Number of products to return (1-50). Default: 10"),
|
|
7462
|
+
after: z30.string().optional().describe("Pagination cursor from previous response's endCursor. Omit for first page."),
|
|
7463
|
+
query: z30.string().optional().describe(
|
|
7167
7464
|
'Search query for title, description, tags. Example: "summer dress", "organic cotton"'
|
|
7168
7465
|
),
|
|
7169
|
-
status:
|
|
7466
|
+
status: z30.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe(
|
|
7170
7467
|
"Filter by product status. ACTIVE = published, DRAFT = not published, ARCHIVED = hidden"
|
|
7171
7468
|
),
|
|
7172
|
-
vendor:
|
|
7173
|
-
productType:
|
|
7174
|
-
sortBy:
|
|
7175
|
-
sortOrder:
|
|
7469
|
+
vendor: z30.string().optional().describe('Filter by exact vendor name. Example: "Acme Corp", "Nike"'),
|
|
7470
|
+
productType: z30.string().optional().describe('Filter by exact product type. Example: "T-Shirts", "Shoes"'),
|
|
7471
|
+
sortBy: z30.enum(["TITLE", "CREATED_AT", "UPDATED_AT", "INVENTORY_TOTAL"]).optional().describe("Sort field. TITLE, CREATED_AT (default), UPDATED_AT, or INVENTORY_TOTAL"),
|
|
7472
|
+
sortOrder: z30.enum(["ASC", "DESC"]).optional().describe("Sort direction. ASC (default) for ascending, DESC for descending")
|
|
7176
7473
|
});
|
|
7177
|
-
var outputSchema28 =
|
|
7178
|
-
products:
|
|
7179
|
-
|
|
7180
|
-
id:
|
|
7181
|
-
title:
|
|
7182
|
-
handle:
|
|
7183
|
-
status:
|
|
7184
|
-
vendor:
|
|
7185
|
-
productType:
|
|
7186
|
-
totalInventory:
|
|
7187
|
-
primaryVariantPrice:
|
|
7188
|
-
imageUrl:
|
|
7474
|
+
var outputSchema28 = z30.object({
|
|
7475
|
+
products: z30.array(
|
|
7476
|
+
z30.object({
|
|
7477
|
+
id: z30.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
|
|
7478
|
+
title: z30.string().describe("Product title"),
|
|
7479
|
+
handle: z30.string().describe("URL handle/slug"),
|
|
7480
|
+
status: z30.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
|
|
7481
|
+
vendor: z30.string().describe("Vendor/brand name"),
|
|
7482
|
+
productType: z30.string().describe("Product type"),
|
|
7483
|
+
totalInventory: z30.number().describe("Total inventory across all variants"),
|
|
7484
|
+
primaryVariantPrice: z30.string().describe('Price of the first variant (e.g., "29.99")'),
|
|
7485
|
+
imageUrl: z30.string().nullable().describe("First image URL or null if no images")
|
|
7189
7486
|
})
|
|
7190
7487
|
).describe("Array of product summaries"),
|
|
7191
|
-
pageInfo:
|
|
7192
|
-
hasNextPage:
|
|
7193
|
-
hasPreviousPage:
|
|
7194
|
-
startCursor:
|
|
7195
|
-
endCursor:
|
|
7488
|
+
pageInfo: z30.object({
|
|
7489
|
+
hasNextPage: z30.boolean().describe("Whether more products exist after this page"),
|
|
7490
|
+
hasPreviousPage: z30.boolean().describe("Whether products exist before this page"),
|
|
7491
|
+
startCursor: z30.string().nullable().describe("Cursor for the first item in this page"),
|
|
7492
|
+
endCursor: z30.string().nullable().describe('Cursor for the last item - use as "after" to get next page')
|
|
7196
7493
|
}).describe("Pagination information"),
|
|
7197
|
-
totalCount:
|
|
7198
|
-
summary:
|
|
7199
|
-
totalReturned:
|
|
7200
|
-
hasMore:
|
|
7201
|
-
hint:
|
|
7494
|
+
totalCount: z30.number().describe("Number of products returned in this page"),
|
|
7495
|
+
summary: z30.object({
|
|
7496
|
+
totalReturned: z30.number().describe("Number of products in this response"),
|
|
7497
|
+
hasMore: z30.boolean().describe("Whether more products are available"),
|
|
7498
|
+
hint: z30.string().describe("Suggestion for next action")
|
|
7202
7499
|
}).describe("AI-friendly summary for context management")
|
|
7203
7500
|
});
|
|
7204
7501
|
var handleListProducts = async (context, params) => {
|
|
@@ -7239,6 +7536,13 @@ function registerListProductsTool() {
|
|
|
7239
7536
|
relationships: {
|
|
7240
7537
|
relatedTools: ["get-product"],
|
|
7241
7538
|
followUps: ["get-product", "update-product", "delete-product"]
|
|
7539
|
+
},
|
|
7540
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7541
|
+
annotations: {
|
|
7542
|
+
readOnlyHint: true,
|
|
7543
|
+
destructiveHint: false,
|
|
7544
|
+
idempotentHint: true,
|
|
7545
|
+
openWorldHint: true
|
|
7242
7546
|
}
|
|
7243
7547
|
},
|
|
7244
7548
|
handleListProducts
|
|
@@ -7246,30 +7550,30 @@ function registerListProductsTool() {
|
|
|
7246
7550
|
}
|
|
7247
7551
|
|
|
7248
7552
|
// src/tools/list-redirects.ts
|
|
7249
|
-
import { z as
|
|
7250
|
-
var inputSchema29 =
|
|
7251
|
-
first:
|
|
7252
|
-
cursor:
|
|
7553
|
+
import { z as z31 } from "zod";
|
|
7554
|
+
var inputSchema29 = z31.object({
|
|
7555
|
+
first: z31.number().int().min(1).max(50).optional().default(10).describe("Number of redirects to return (1-50, default: 10)."),
|
|
7556
|
+
cursor: z31.string().optional().describe(
|
|
7253
7557
|
"Pagination cursor from previous response (endCursor). Use this to fetch the next page of results."
|
|
7254
7558
|
),
|
|
7255
|
-
query:
|
|
7559
|
+
query: z31.string().optional().describe(
|
|
7256
7560
|
"Search filter. Supports 'path:' and 'target:' prefixes. Examples: 'path:/old-' to find redirects starting with /old-, 'target:/products/' to find redirects pointing to /products/."
|
|
7257
7561
|
)
|
|
7258
7562
|
});
|
|
7259
|
-
var outputSchema29 =
|
|
7260
|
-
redirects:
|
|
7261
|
-
|
|
7262
|
-
id:
|
|
7263
|
-
path:
|
|
7264
|
-
target:
|
|
7563
|
+
var outputSchema29 = z31.object({
|
|
7564
|
+
redirects: z31.array(
|
|
7565
|
+
z31.object({
|
|
7566
|
+
id: z31.string().describe("Redirect GID"),
|
|
7567
|
+
path: z31.string().describe("Source path"),
|
|
7568
|
+
target: z31.string().describe("Target URL")
|
|
7265
7569
|
})
|
|
7266
7570
|
),
|
|
7267
|
-
hasNextPage:
|
|
7268
|
-
endCursor:
|
|
7269
|
-
summary:
|
|
7270
|
-
totalReturned:
|
|
7271
|
-
hasMore:
|
|
7272
|
-
hint:
|
|
7571
|
+
hasNextPage: z31.boolean().describe("Whether more results exist"),
|
|
7572
|
+
endCursor: z31.string().nullable().describe("Cursor for fetching next page"),
|
|
7573
|
+
summary: z31.object({
|
|
7574
|
+
totalReturned: z31.number().describe("Number of redirects in this response"),
|
|
7575
|
+
hasMore: z31.boolean().describe("Whether more redirects are available"),
|
|
7576
|
+
hint: z31.string().describe("Suggestion for next action")
|
|
7273
7577
|
}).describe("AI-friendly summary for context management")
|
|
7274
7578
|
});
|
|
7275
7579
|
var handleListRedirects = async (context, params) => {
|
|
@@ -7314,6 +7618,13 @@ function registerListRedirectsTool() {
|
|
|
7314
7618
|
relationships: {
|
|
7315
7619
|
relatedTools: ["create-redirect"],
|
|
7316
7620
|
followUps: ["delete-redirect"]
|
|
7621
|
+
},
|
|
7622
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7623
|
+
annotations: {
|
|
7624
|
+
readOnlyHint: true,
|
|
7625
|
+
destructiveHint: false,
|
|
7626
|
+
idempotentHint: true,
|
|
7627
|
+
openWorldHint: true
|
|
7317
7628
|
}
|
|
7318
7629
|
},
|
|
7319
7630
|
handleListRedirects
|
|
@@ -7321,18 +7632,18 @@ function registerListRedirectsTool() {
|
|
|
7321
7632
|
}
|
|
7322
7633
|
|
|
7323
7634
|
// src/tools/remove-products-from-collection.ts
|
|
7324
|
-
import { z as
|
|
7325
|
-
var inputSchema30 =
|
|
7326
|
-
collectionId:
|
|
7635
|
+
import { z as z32 } from "zod";
|
|
7636
|
+
var inputSchema30 = z32.object({
|
|
7637
|
+
collectionId: collectionIdSchema.describe(
|
|
7327
7638
|
'Collection GID (e.g., "gid://shopify/Collection/123"). Use list-collections to find valid collection IDs.'
|
|
7328
7639
|
),
|
|
7329
|
-
productIds:
|
|
7640
|
+
productIds: z32.array(productIdSchema).min(1).max(250).describe(
|
|
7330
7641
|
'Array of product GIDs to remove (e.g., ["gid://shopify/Product/123", "gid://shopify/Product/456"]). Maximum 250 products per call. Use list-products to find valid product IDs.'
|
|
7331
7642
|
)
|
|
7332
7643
|
});
|
|
7333
|
-
var outputSchema30 =
|
|
7334
|
-
success:
|
|
7335
|
-
removedCount:
|
|
7644
|
+
var outputSchema30 = z32.object({
|
|
7645
|
+
success: z32.boolean().describe("Whether the operation succeeded"),
|
|
7646
|
+
removedCount: z32.number().describe("Number of products removed from the collection")
|
|
7336
7647
|
});
|
|
7337
7648
|
var handleRemoveProductsFromCollection = async (context, params) => {
|
|
7338
7649
|
log.debug(
|
|
@@ -7369,6 +7680,15 @@ function registerRemoveProductsFromCollectionTool() {
|
|
|
7369
7680
|
relationships: {
|
|
7370
7681
|
relatedTools: ["add-products-to-collection", "get-collection"],
|
|
7371
7682
|
prerequisites: ["get-collection"]
|
|
7683
|
+
},
|
|
7684
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7685
|
+
annotations: {
|
|
7686
|
+
readOnlyHint: false,
|
|
7687
|
+
destructiveHint: false,
|
|
7688
|
+
// Does not delete products, just removes from collection
|
|
7689
|
+
idempotentHint: true,
|
|
7690
|
+
// Removing already-removed products is idempotent
|
|
7691
|
+
openWorldHint: true
|
|
7372
7692
|
}
|
|
7373
7693
|
},
|
|
7374
7694
|
handleRemoveProductsFromCollection
|
|
@@ -7376,7 +7696,7 @@ function registerRemoveProductsFromCollectionTool() {
|
|
|
7376
7696
|
}
|
|
7377
7697
|
|
|
7378
7698
|
// src/tools/reorder-product-images.ts
|
|
7379
|
-
import { z as
|
|
7699
|
+
import { z as z33 } from "zod";
|
|
7380
7700
|
var PRODUCT_REORDER_MEDIA_MUTATION = `
|
|
7381
7701
|
mutation ProductReorderMedia($id: ID!, $moves: [MoveInput!]!) {
|
|
7382
7702
|
productReorderMedia(id: $id, moves: $moves) {
|
|
@@ -7391,23 +7711,23 @@ var PRODUCT_REORDER_MEDIA_MUTATION = `
|
|
|
7391
7711
|
}
|
|
7392
7712
|
}
|
|
7393
7713
|
`;
|
|
7394
|
-
var moveSchema =
|
|
7395
|
-
id:
|
|
7396
|
-
newPosition:
|
|
7714
|
+
var moveSchema = z33.object({
|
|
7715
|
+
id: imageIdSchema.describe('Image GID to move (e.g., "gid://shopify/MediaImage/123")'),
|
|
7716
|
+
newPosition: z33.number().int().min(0).describe("Zero-based target position. Position 0 is the featured image.")
|
|
7397
7717
|
});
|
|
7398
|
-
var inputSchema31 =
|
|
7399
|
-
productId:
|
|
7718
|
+
var inputSchema31 = z33.object({
|
|
7719
|
+
productId: productIdSchema.describe(
|
|
7400
7720
|
'Shopify product GID (e.g., "gid://shopify/Product/123"). Required. Use list-products to find product IDs.'
|
|
7401
7721
|
),
|
|
7402
|
-
moves:
|
|
7722
|
+
moves: z33.array(moveSchema).min(1).describe(
|
|
7403
7723
|
"Array of move operations. Each move specifies an image ID and its new position. Only include images whose position is actually changing. Positions are 0-indexed (position 0 is the featured/hero image)."
|
|
7404
7724
|
)
|
|
7405
7725
|
});
|
|
7406
|
-
var outputSchema31 =
|
|
7407
|
-
success:
|
|
7408
|
-
jobId:
|
|
7409
|
-
jobDone:
|
|
7410
|
-
message:
|
|
7726
|
+
var outputSchema31 = z33.object({
|
|
7727
|
+
success: z33.boolean().describe("Whether the reorder operation was initiated successfully"),
|
|
7728
|
+
jobId: z33.string().nullable().describe("Background job ID for tracking (may be null if processed immediately)"),
|
|
7729
|
+
jobDone: z33.boolean().describe("Whether the job completed immediately"),
|
|
7730
|
+
message: z33.string().describe("Human-readable message describing the result")
|
|
7411
7731
|
});
|
|
7412
7732
|
var handleReorderProductImages = async (context, params) => {
|
|
7413
7733
|
log.debug(`Reordering images on shop: ${context.shopDomain}`);
|
|
@@ -7482,6 +7802,14 @@ function registerReorderProductImagesTool() {
|
|
|
7482
7802
|
relationships: {
|
|
7483
7803
|
relatedTools: ["add-product-image", "update-product-image"],
|
|
7484
7804
|
prerequisites: ["get-product"]
|
|
7805
|
+
},
|
|
7806
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7807
|
+
annotations: {
|
|
7808
|
+
readOnlyHint: false,
|
|
7809
|
+
destructiveHint: false,
|
|
7810
|
+
idempotentHint: true,
|
|
7811
|
+
// Same order produces same result
|
|
7812
|
+
openWorldHint: true
|
|
7485
7813
|
}
|
|
7486
7814
|
},
|
|
7487
7815
|
handleReorderProductImages
|
|
@@ -7489,45 +7817,45 @@ function registerReorderProductImagesTool() {
|
|
|
7489
7817
|
}
|
|
7490
7818
|
|
|
7491
7819
|
// src/tools/set-product-metafields.ts
|
|
7492
|
-
import { z as
|
|
7820
|
+
import { z as z34 } from "zod";
|
|
7493
7821
|
var MAX_METAFIELDS_PER_CALL = 25;
|
|
7494
|
-
var metafieldInputSchema =
|
|
7495
|
-
namespace:
|
|
7822
|
+
var metafieldInputSchema = z34.object({
|
|
7823
|
+
namespace: z34.string().min(1).describe(
|
|
7496
7824
|
'Metafield namespace (grouping identifier, e.g., "custom", "seo", "my_app"). Use consistent namespaces to organize related metafields.'
|
|
7497
7825
|
),
|
|
7498
|
-
key:
|
|
7826
|
+
key: z34.string().min(1).describe(
|
|
7499
7827
|
'Metafield key (field name, e.g., "color_hex", "schema_markup"). Keys should be lowercase with underscores, unique within a namespace.'
|
|
7500
7828
|
),
|
|
7501
|
-
value:
|
|
7502
|
-
type:
|
|
7829
|
+
value: z34.string().describe("The value to store. All values are stored as strings; JSON should be stringified."),
|
|
7830
|
+
type: z34.string().min(1).describe(
|
|
7503
7831
|
'Metafield type. Required for creating new metafields. Common types: "single_line_text_field" (short text), "multi_line_text_field" (long text), "number_integer", "number_decimal", "boolean", "json", "url", "date", "date_time".'
|
|
7504
7832
|
)
|
|
7505
7833
|
});
|
|
7506
|
-
var inputSchema32 =
|
|
7507
|
-
productId:
|
|
7834
|
+
var inputSchema32 = z34.object({
|
|
7835
|
+
productId: productIdSchema.describe(
|
|
7508
7836
|
'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use get-product or list-products to find product IDs.'
|
|
7509
7837
|
),
|
|
7510
|
-
metafields:
|
|
7838
|
+
metafields: z34.array(metafieldInputSchema).min(1, "At least one metafield is required").max(
|
|
7511
7839
|
MAX_METAFIELDS_PER_CALL,
|
|
7512
7840
|
`Maximum ${MAX_METAFIELDS_PER_CALL} metafields per call. Split into multiple calls for larger batches.`
|
|
7513
7841
|
).describe(
|
|
7514
7842
|
`Array of metafields to create or update (max ${MAX_METAFIELDS_PER_CALL} per call). Existing metafields with matching namespace+key are updated; new ones are created.`
|
|
7515
7843
|
)
|
|
7516
7844
|
});
|
|
7517
|
-
var outputSchema32 =
|
|
7518
|
-
success:
|
|
7519
|
-
metafields:
|
|
7520
|
-
|
|
7521
|
-
id:
|
|
7522
|
-
namespace:
|
|
7523
|
-
key:
|
|
7524
|
-
value:
|
|
7525
|
-
type:
|
|
7526
|
-
createdAt:
|
|
7527
|
-
updatedAt:
|
|
7845
|
+
var outputSchema32 = z34.object({
|
|
7846
|
+
success: z34.boolean().describe("Whether the operation succeeded"),
|
|
7847
|
+
metafields: z34.array(
|
|
7848
|
+
z34.object({
|
|
7849
|
+
id: z34.string().describe("Metafield GID"),
|
|
7850
|
+
namespace: z34.string().describe("Metafield namespace"),
|
|
7851
|
+
key: z34.string().describe("Metafield key"),
|
|
7852
|
+
value: z34.string().describe("Metafield value"),
|
|
7853
|
+
type: z34.string().describe("Metafield type"),
|
|
7854
|
+
createdAt: z34.string().describe("Creation timestamp (ISO 8601)"),
|
|
7855
|
+
updatedAt: z34.string().describe("Last update timestamp (ISO 8601)")
|
|
7528
7856
|
})
|
|
7529
7857
|
).describe("Created or updated metafields"),
|
|
7530
|
-
count:
|
|
7858
|
+
count: z34.number().describe("Number of metafields processed")
|
|
7531
7859
|
});
|
|
7532
7860
|
var handleSetProductMetafields = async (context, params) => {
|
|
7533
7861
|
log.debug(
|
|
@@ -7571,6 +7899,14 @@ function registerSetProductMetafieldsTool() {
|
|
|
7571
7899
|
relatedTools: ["get-product", "get-product-metafields"],
|
|
7572
7900
|
prerequisites: ["get-product"],
|
|
7573
7901
|
followUps: ["get-product-metafields"]
|
|
7902
|
+
},
|
|
7903
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
7904
|
+
annotations: {
|
|
7905
|
+
readOnlyHint: false,
|
|
7906
|
+
destructiveHint: false,
|
|
7907
|
+
idempotentHint: true,
|
|
7908
|
+
// Same values produce same result
|
|
7909
|
+
openWorldHint: true
|
|
7574
7910
|
}
|
|
7575
7911
|
},
|
|
7576
7912
|
handleSetProductMetafields
|
|
@@ -7578,47 +7914,47 @@ function registerSetProductMetafieldsTool() {
|
|
|
7578
7914
|
}
|
|
7579
7915
|
|
|
7580
7916
|
// src/tools/update-article.ts
|
|
7581
|
-
import { z as
|
|
7582
|
-
var inputSchema33 =
|
|
7583
|
-
id:
|
|
7917
|
+
import { z as z35 } from "zod";
|
|
7918
|
+
var inputSchema33 = z35.object({
|
|
7919
|
+
id: articleIdSchema.describe(
|
|
7584
7920
|
'The article ID to update (required). Must be a valid Shopify GID format. Example: "gid://shopify/Article/12345". Use list-articles to find article IDs.'
|
|
7585
7921
|
),
|
|
7586
|
-
title:
|
|
7587
|
-
authorName:
|
|
7588
|
-
body:
|
|
7589
|
-
summary:
|
|
7590
|
-
tags:
|
|
7922
|
+
title: z35.string().optional().describe("The new title for the article. Only provided if changing the title."),
|
|
7923
|
+
authorName: z35.string().optional().describe("The new author name for the article."),
|
|
7924
|
+
body: z35.string().optional().describe("The new HTML body content of the article. Supports HTML markup for formatting."),
|
|
7925
|
+
summary: z35.string().optional().describe("A summary or excerpt of the article."),
|
|
7926
|
+
tags: z35.array(z35.string()).optional().describe(
|
|
7591
7927
|
"New tags for categorization. This replaces all existing tags. Example: ['guide', 'tutorial', 'beginner']."
|
|
7592
7928
|
),
|
|
7593
|
-
image:
|
|
7594
|
-
url:
|
|
7595
|
-
altText:
|
|
7929
|
+
image: z35.object({
|
|
7930
|
+
url: z35.string().url().describe("The URL of the featured image"),
|
|
7931
|
+
altText: z35.string().optional().describe("Alt text for accessibility")
|
|
7596
7932
|
}).optional().describe("Featured image for the article."),
|
|
7597
|
-
isPublished:
|
|
7933
|
+
isPublished: z35.boolean().optional().describe(
|
|
7598
7934
|
"Whether the article should be visible on the storefront. Set to true to publish, false to unpublish."
|
|
7599
7935
|
),
|
|
7600
|
-
publishDate:
|
|
7936
|
+
publishDate: z35.string().optional().describe(
|
|
7601
7937
|
"The date and time when the article should become visible (ISO 8601 format). Example: '2024-01-15T10:00:00Z'."
|
|
7602
7938
|
),
|
|
7603
|
-
handle:
|
|
7939
|
+
handle: z35.string().optional().describe(
|
|
7604
7940
|
"The new URL handle/slug for the article. Set redirectNewHandle to true to automatically redirect the old URL to the new one."
|
|
7605
7941
|
),
|
|
7606
|
-
templateSuffix:
|
|
7942
|
+
templateSuffix: z35.string().optional().describe(
|
|
7607
7943
|
"The suffix of the Liquid template used to render the article. For example, 'featured' would use the template 'article.featured.liquid'."
|
|
7608
7944
|
),
|
|
7609
|
-
redirectNewHandle:
|
|
7945
|
+
redirectNewHandle: z35.boolean().optional().describe(
|
|
7610
7946
|
"Whether to automatically redirect the old URL to the new handle. Set to true when changing the handle to preserve SEO and existing links."
|
|
7611
7947
|
)
|
|
7612
7948
|
});
|
|
7613
|
-
var outputSchema33 =
|
|
7614
|
-
id:
|
|
7615
|
-
title:
|
|
7616
|
-
handle:
|
|
7617
|
-
blog:
|
|
7618
|
-
id:
|
|
7619
|
-
title:
|
|
7949
|
+
var outputSchema33 = z35.object({
|
|
7950
|
+
id: z35.string().describe("Updated article GID"),
|
|
7951
|
+
title: z35.string().describe("Article title"),
|
|
7952
|
+
handle: z35.string().describe("URL handle/slug"),
|
|
7953
|
+
blog: z35.object({
|
|
7954
|
+
id: z35.string().describe("Parent blog GID"),
|
|
7955
|
+
title: z35.string().describe("Parent blog title")
|
|
7620
7956
|
}),
|
|
7621
|
-
isPublished:
|
|
7957
|
+
isPublished: z35.boolean().describe("Whether article is visible on storefront")
|
|
7622
7958
|
});
|
|
7623
7959
|
var handleUpdateArticle = async (context, params) => {
|
|
7624
7960
|
log.debug(`Updating article on shop: ${context.shopDomain}`);
|
|
@@ -7674,6 +8010,13 @@ function registerUpdateArticleTool() {
|
|
|
7674
8010
|
relatedTools: ["list-articles", "create-article", "delete-article"],
|
|
7675
8011
|
prerequisites: ["list-articles"],
|
|
7676
8012
|
followUps: ["list-articles"]
|
|
8013
|
+
},
|
|
8014
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8015
|
+
annotations: {
|
|
8016
|
+
readOnlyHint: false,
|
|
8017
|
+
destructiveHint: false,
|
|
8018
|
+
idempotentHint: true,
|
|
8019
|
+
openWorldHint: true
|
|
7677
8020
|
}
|
|
7678
8021
|
},
|
|
7679
8022
|
handleUpdateArticle
|
|
@@ -7681,30 +8024,30 @@ function registerUpdateArticleTool() {
|
|
|
7681
8024
|
}
|
|
7682
8025
|
|
|
7683
8026
|
// src/tools/update-blog.ts
|
|
7684
|
-
import { z as
|
|
7685
|
-
var inputSchema34 =
|
|
7686
|
-
id:
|
|
8027
|
+
import { z as z36 } from "zod";
|
|
8028
|
+
var inputSchema34 = z36.object({
|
|
8029
|
+
id: blogIdSchema.describe(
|
|
7687
8030
|
'The blog ID to update (required). Must be a valid Shopify GID format. Example: "gid://shopify/Blog/12345". Use list-blogs to find blog IDs.'
|
|
7688
8031
|
),
|
|
7689
|
-
title:
|
|
7690
|
-
handle:
|
|
8032
|
+
title: z36.string().optional().describe("The new title for the blog. Only provided if changing the title."),
|
|
8033
|
+
handle: z36.string().optional().describe(
|
|
7691
8034
|
"The new URL handle/slug for the blog. Set redirectNewHandle to true to automatically redirect the old URL to the new one."
|
|
7692
8035
|
),
|
|
7693
|
-
commentPolicy:
|
|
8036
|
+
commentPolicy: z36.enum(["CLOSED", "MODERATE", "OPEN"]).optional().describe(
|
|
7694
8037
|
"Comment moderation policy for the blog. CLOSED: Comments disabled. MODERATE: Comments require approval. OPEN: Comments appear immediately."
|
|
7695
8038
|
),
|
|
7696
|
-
templateSuffix:
|
|
8039
|
+
templateSuffix: z36.string().optional().describe(
|
|
7697
8040
|
"The suffix of the Liquid template used to render the blog. For example, 'news' would use the template 'blog.news.liquid'."
|
|
7698
8041
|
),
|
|
7699
|
-
redirectNewHandle:
|
|
8042
|
+
redirectNewHandle: z36.boolean().optional().describe(
|
|
7700
8043
|
"Whether to automatically redirect the old URL to the new handle. Set to true when changing the handle to preserve SEO and existing links."
|
|
7701
8044
|
)
|
|
7702
8045
|
});
|
|
7703
|
-
var outputSchema34 =
|
|
7704
|
-
id:
|
|
7705
|
-
title:
|
|
7706
|
-
handle:
|
|
7707
|
-
commentPolicy:
|
|
8046
|
+
var outputSchema34 = z36.object({
|
|
8047
|
+
id: z36.string().describe("Updated blog GID"),
|
|
8048
|
+
title: z36.string().describe("Blog title"),
|
|
8049
|
+
handle: z36.string().describe("URL handle/slug"),
|
|
8050
|
+
commentPolicy: z36.enum(["CLOSED", "MODERATE", "OPEN"]).describe("Comment moderation policy")
|
|
7708
8051
|
});
|
|
7709
8052
|
var handleUpdateBlog = async (context, params) => {
|
|
7710
8053
|
log.debug(`Updating blog on shop: ${context.shopDomain}`);
|
|
@@ -7754,6 +8097,13 @@ function registerUpdateBlogTool() {
|
|
|
7754
8097
|
relatedTools: ["list-blogs", "create-blog", "delete-blog"],
|
|
7755
8098
|
prerequisites: ["list-blogs"],
|
|
7756
8099
|
followUps: ["list-blogs"]
|
|
8100
|
+
},
|
|
8101
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8102
|
+
annotations: {
|
|
8103
|
+
readOnlyHint: false,
|
|
8104
|
+
destructiveHint: false,
|
|
8105
|
+
idempotentHint: true,
|
|
8106
|
+
openWorldHint: true
|
|
7757
8107
|
}
|
|
7758
8108
|
},
|
|
7759
8109
|
handleUpdateBlog
|
|
@@ -7761,21 +8111,21 @@ function registerUpdateBlogTool() {
|
|
|
7761
8111
|
}
|
|
7762
8112
|
|
|
7763
8113
|
// src/tools/update-collection.ts
|
|
7764
|
-
import { z as
|
|
7765
|
-
var inputSchema35 =
|
|
7766
|
-
collectionId:
|
|
8114
|
+
import { z as z37 } from "zod";
|
|
8115
|
+
var inputSchema35 = z37.object({
|
|
8116
|
+
collectionId: collectionIdSchema.describe(
|
|
7767
8117
|
'Collection ID to update (GID format, e.g., "gid://shopify/Collection/123"). Use list-collections or get-collection to find collection IDs.'
|
|
7768
8118
|
),
|
|
7769
|
-
title:
|
|
7770
|
-
handle:
|
|
8119
|
+
title: z37.string().min(1).optional().describe("New collection title"),
|
|
8120
|
+
handle: z37.string().optional().describe(
|
|
7771
8121
|
"New URL handle/slug. When changing, set redirectNewHandle: true to create automatic redirect from the old URL."
|
|
7772
8122
|
),
|
|
7773
|
-
descriptionHtml:
|
|
7774
|
-
seo:
|
|
7775
|
-
title:
|
|
7776
|
-
description:
|
|
8123
|
+
descriptionHtml: z37.string().optional().describe("New HTML description"),
|
|
8124
|
+
seo: z37.object({
|
|
8125
|
+
title: z37.string().optional().describe("New SEO title"),
|
|
8126
|
+
description: z37.string().optional().describe("New SEO meta description")
|
|
7777
8127
|
}).optional().describe("Updated SEO metadata"),
|
|
7778
|
-
sortOrder:
|
|
8128
|
+
sortOrder: z37.enum([
|
|
7779
8129
|
"ALPHA_ASC",
|
|
7780
8130
|
"ALPHA_DESC",
|
|
7781
8131
|
"BEST_SELLING",
|
|
@@ -7785,19 +8135,19 @@ var inputSchema35 = z36.object({
|
|
|
7785
8135
|
"PRICE_ASC",
|
|
7786
8136
|
"PRICE_DESC"
|
|
7787
8137
|
]).optional().describe("New product sort order within collection"),
|
|
7788
|
-
image:
|
|
7789
|
-
src:
|
|
7790
|
-
altText:
|
|
8138
|
+
image: z37.object({
|
|
8139
|
+
src: z37.string().url().describe("New image URL (publicly accessible)"),
|
|
8140
|
+
altText: z37.string().optional().describe("New alt text for accessibility")
|
|
7791
8141
|
}).optional().describe("New collection image"),
|
|
7792
|
-
templateSuffix:
|
|
7793
|
-
redirectNewHandle:
|
|
8142
|
+
templateSuffix: z37.string().optional().describe("New Liquid template suffix"),
|
|
8143
|
+
redirectNewHandle: z37.boolean().optional().describe(
|
|
7794
8144
|
"When changing the handle, set to true to create an automatic URL redirect from the old handle to the new one. Important for SEO to preserve link equity."
|
|
7795
8145
|
)
|
|
7796
8146
|
});
|
|
7797
|
-
var outputSchema35 =
|
|
7798
|
-
id:
|
|
7799
|
-
title:
|
|
7800
|
-
handle:
|
|
8147
|
+
var outputSchema35 = z37.object({
|
|
8148
|
+
id: z37.string().describe("Updated collection GID"),
|
|
8149
|
+
title: z37.string().describe("Collection title"),
|
|
8150
|
+
handle: z37.string().describe("Collection URL handle")
|
|
7801
8151
|
});
|
|
7802
8152
|
var handleUpdateCollection = async (context, params) => {
|
|
7803
8153
|
log.debug(`Updating collection on shop: ${context.shopDomain}`);
|
|
@@ -7844,6 +8194,14 @@ function registerUpdateCollectionTool() {
|
|
|
7844
8194
|
relatedTools: ["get-collection", "list-collections"],
|
|
7845
8195
|
prerequisites: ["get-collection"],
|
|
7846
8196
|
followUps: ["add-products-to-collection", "remove-products-from-collection"]
|
|
8197
|
+
},
|
|
8198
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8199
|
+
annotations: {
|
|
8200
|
+
readOnlyHint: false,
|
|
8201
|
+
destructiveHint: false,
|
|
8202
|
+
idempotentHint: true,
|
|
8203
|
+
// Same update produces same result
|
|
8204
|
+
openWorldHint: true
|
|
7847
8205
|
}
|
|
7848
8206
|
},
|
|
7849
8207
|
handleUpdateCollection
|
|
@@ -7851,8 +8209,8 @@ function registerUpdateCollectionTool() {
|
|
|
7851
8209
|
}
|
|
7852
8210
|
|
|
7853
8211
|
// src/tools/update-inventory.ts
|
|
7854
|
-
import { z as
|
|
7855
|
-
var inventoryReasonEnum =
|
|
8212
|
+
import { z as z38 } from "zod";
|
|
8213
|
+
var inventoryReasonEnum = z38.enum([
|
|
7856
8214
|
"correction",
|
|
7857
8215
|
"cycle_count_available",
|
|
7858
8216
|
"damaged",
|
|
@@ -7871,20 +8229,20 @@ var inventoryReasonEnum = z37.enum([
|
|
|
7871
8229
|
"safety_stock",
|
|
7872
8230
|
"shrinkage"
|
|
7873
8231
|
]);
|
|
7874
|
-
var inputSchema36 =
|
|
7875
|
-
inventoryItemId:
|
|
8232
|
+
var inputSchema36 = z38.object({
|
|
8233
|
+
inventoryItemId: inventoryItemIdSchema.describe(
|
|
7876
8234
|
'The Shopify inventory item ID (e.g., "gid://shopify/InventoryItem/456"). Get this from get-inventory tool or from get-product response.'
|
|
7877
8235
|
),
|
|
7878
|
-
locationId:
|
|
8236
|
+
locationId: locationIdSchema.describe(
|
|
7879
8237
|
'The location ID where inventory should be updated (e.g., "gid://shopify/Location/789"). Get this from get-inventory tool.'
|
|
7880
8238
|
),
|
|
7881
|
-
setQuantity:
|
|
8239
|
+
setQuantity: z38.number().int().min(0).optional().describe(
|
|
7882
8240
|
"Set inventory to this exact quantity. Example: 100. Use for absolute stock counts after physical inventory. Cannot be used together with adjustQuantity."
|
|
7883
8241
|
),
|
|
7884
|
-
adjustQuantity:
|
|
8242
|
+
adjustQuantity: z38.number().int().optional().describe(
|
|
7885
8243
|
"Adjust inventory by this amount. Positive to add, negative to subtract. Example: -5 to reduce by 5, +10 to add 10. Use for incremental changes. Cannot be used together with setQuantity."
|
|
7886
8244
|
),
|
|
7887
|
-
name:
|
|
8245
|
+
name: z38.enum(["available", "on_hand"]).optional().default("available").describe(
|
|
7888
8246
|
'Which quantity to modify. "available" (default) for sellable stock, "on_hand" for physical count.'
|
|
7889
8247
|
),
|
|
7890
8248
|
reason: inventoryReasonEnum.optional().default("correction").describe(
|
|
@@ -7895,16 +8253,16 @@ var inputSchema36 = z37.object({
|
|
|
7895
8253
|
}).refine((data) => !(data.setQuantity !== void 0 && data.adjustQuantity !== void 0), {
|
|
7896
8254
|
message: "Cannot provide both setQuantity and adjustQuantity. Choose one."
|
|
7897
8255
|
});
|
|
7898
|
-
var outputSchema36 =
|
|
7899
|
-
success:
|
|
7900
|
-
inventoryItemId:
|
|
7901
|
-
locationId:
|
|
7902
|
-
locationName:
|
|
7903
|
-
previousQuantity:
|
|
7904
|
-
newQuantity:
|
|
7905
|
-
delta:
|
|
7906
|
-
name:
|
|
7907
|
-
reason:
|
|
8256
|
+
var outputSchema36 = z38.object({
|
|
8257
|
+
success: z38.boolean().describe("Whether the operation succeeded"),
|
|
8258
|
+
inventoryItemId: z38.string().describe("Inventory item GID"),
|
|
8259
|
+
locationId: z38.string().describe("Location GID where inventory was updated"),
|
|
8260
|
+
locationName: z38.string().describe("Human-readable location name"),
|
|
8261
|
+
previousQuantity: z38.number().describe("Quantity before the change"),
|
|
8262
|
+
newQuantity: z38.number().describe("Quantity after the change"),
|
|
8263
|
+
delta: z38.number().describe("The actual change applied (positive or negative)"),
|
|
8264
|
+
name: z38.string().describe("Which quantity was modified (available or on_hand)"),
|
|
8265
|
+
reason: z38.string().describe("Reason for the adjustment")
|
|
7908
8266
|
});
|
|
7909
8267
|
var handleUpdateInventory = async (context, params) => {
|
|
7910
8268
|
log.debug(`Updating inventory on shop: ${context.shopDomain}`);
|
|
@@ -7985,6 +8343,13 @@ function registerUpdateInventoryTool() {
|
|
|
7985
8343
|
relatedTools: ["get-inventory", "get-bulk-inventory", "list-low-inventory"],
|
|
7986
8344
|
prerequisites: ["get-inventory"],
|
|
7987
8345
|
followUps: ["get-inventory"]
|
|
8346
|
+
},
|
|
8347
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8348
|
+
annotations: {
|
|
8349
|
+
readOnlyHint: false,
|
|
8350
|
+
destructiveHint: false,
|
|
8351
|
+
idempotentHint: true,
|
|
8352
|
+
openWorldHint: true
|
|
7988
8353
|
}
|
|
7989
8354
|
},
|
|
7990
8355
|
handleUpdateInventory
|
|
@@ -7992,33 +8357,33 @@ function registerUpdateInventoryTool() {
|
|
|
7992
8357
|
}
|
|
7993
8358
|
|
|
7994
8359
|
// src/tools/update-page.ts
|
|
7995
|
-
import { z as
|
|
7996
|
-
var inputSchema37 =
|
|
7997
|
-
id:
|
|
8360
|
+
import { z as z39 } from "zod";
|
|
8361
|
+
var inputSchema37 = z39.object({
|
|
8362
|
+
id: pageIdSchema.describe(
|
|
7998
8363
|
'The page ID to update (required). Must be a valid Shopify GID format. Example: "gid://shopify/Page/123456789". Use list-pages to find page IDs.'
|
|
7999
8364
|
),
|
|
8000
|
-
title:
|
|
8001
|
-
body:
|
|
8365
|
+
title: z39.string().optional().describe("The new title for the page. Only provide if you want to change it."),
|
|
8366
|
+
body: z39.string().optional().describe(
|
|
8002
8367
|
"The new HTML body content for the page. Supports HTML markup. Only provide if you want to change it."
|
|
8003
8368
|
),
|
|
8004
|
-
handle:
|
|
8369
|
+
handle: z39.string().optional().describe(
|
|
8005
8370
|
"The new URL handle/slug for the page. Only provide if you want to change the URL. Consider setting redirectNewHandle to true when changing handles."
|
|
8006
8371
|
),
|
|
8007
|
-
isPublished:
|
|
8372
|
+
isPublished: z39.boolean().optional().describe(
|
|
8008
8373
|
"Whether the page should be visible on the storefront. Set to true to publish, false to unpublish."
|
|
8009
8374
|
),
|
|
8010
|
-
templateSuffix:
|
|
8375
|
+
templateSuffix: z39.string().optional().describe(
|
|
8011
8376
|
"The suffix of the Liquid template used to render the page. Set to empty string to use the default template."
|
|
8012
8377
|
),
|
|
8013
|
-
redirectNewHandle:
|
|
8378
|
+
redirectNewHandle: z39.boolean().optional().describe(
|
|
8014
8379
|
"Whether to create a redirect from the old handle to the new handle when changing handles. Recommended to preserve SEO value when changing page URLs."
|
|
8015
8380
|
)
|
|
8016
8381
|
});
|
|
8017
|
-
var outputSchema37 =
|
|
8018
|
-
id:
|
|
8019
|
-
title:
|
|
8020
|
-
handle:
|
|
8021
|
-
isPublished:
|
|
8382
|
+
var outputSchema37 = z39.object({
|
|
8383
|
+
id: z39.string().describe("Updated page GID"),
|
|
8384
|
+
title: z39.string().describe("Page title"),
|
|
8385
|
+
handle: z39.string().describe("URL handle/slug"),
|
|
8386
|
+
isPublished: z39.boolean().describe("Whether page is visible on storefront")
|
|
8022
8387
|
});
|
|
8023
8388
|
var handleUpdatePage = async (context, params) => {
|
|
8024
8389
|
log.debug(`Updating page on shop: ${context.shopDomain}`);
|
|
@@ -8069,6 +8434,13 @@ function registerUpdatePageTool() {
|
|
|
8069
8434
|
relatedTools: ["get-page", "list-pages"],
|
|
8070
8435
|
prerequisites: ["get-page"],
|
|
8071
8436
|
followUps: ["get-page"]
|
|
8437
|
+
},
|
|
8438
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8439
|
+
annotations: {
|
|
8440
|
+
readOnlyHint: false,
|
|
8441
|
+
destructiveHint: false,
|
|
8442
|
+
idempotentHint: true,
|
|
8443
|
+
openWorldHint: true
|
|
8072
8444
|
}
|
|
8073
8445
|
},
|
|
8074
8446
|
handleUpdatePage
|
|
@@ -8076,7 +8448,7 @@ function registerUpdatePageTool() {
|
|
|
8076
8448
|
}
|
|
8077
8449
|
|
|
8078
8450
|
// src/tools/update-product-image.ts
|
|
8079
|
-
import { z as
|
|
8451
|
+
import { z as z40 } from "zod";
|
|
8080
8452
|
var FILE_UPDATE_MUTATION = `
|
|
8081
8453
|
mutation FileUpdate($files: [FileUpdateInput!]!) {
|
|
8082
8454
|
fileUpdate(files: $files) {
|
|
@@ -8096,18 +8468,18 @@ var FILE_UPDATE_MUTATION = `
|
|
|
8096
8468
|
}
|
|
8097
8469
|
}
|
|
8098
8470
|
`;
|
|
8099
|
-
var inputSchema38 =
|
|
8100
|
-
imageId:
|
|
8471
|
+
var inputSchema38 = z40.object({
|
|
8472
|
+
imageId: imageIdSchema.describe(
|
|
8101
8473
|
'Shopify image GID (e.g., "gid://shopify/MediaImage/123"). Required. Use get-product to find image IDs in the media field.'
|
|
8102
8474
|
),
|
|
8103
|
-
altText:
|
|
8475
|
+
altText: z40.string().describe(
|
|
8104
8476
|
"New alt text for the image. Pass an empty string to clear the alt text. Alt text describes the image for screen readers and helps search engines understand image content."
|
|
8105
8477
|
)
|
|
8106
8478
|
});
|
|
8107
|
-
var outputSchema38 =
|
|
8108
|
-
id:
|
|
8109
|
-
url:
|
|
8110
|
-
altText:
|
|
8479
|
+
var outputSchema38 = z40.object({
|
|
8480
|
+
id: z40.string().describe('Image GID (e.g., "gid://shopify/MediaImage/123")'),
|
|
8481
|
+
url: z40.string().nullable().describe("Shopify CDN URL for the image"),
|
|
8482
|
+
altText: z40.string().nullable().describe("Updated alt text for the image")
|
|
8111
8483
|
});
|
|
8112
8484
|
var handleUpdateProductImage = async (context, params) => {
|
|
8113
8485
|
log.debug(`Updating image alt text on shop: ${context.shopDomain}`);
|
|
@@ -8174,6 +8546,14 @@ function registerUpdateProductImageTool() {
|
|
|
8174
8546
|
relatedTools: ["reorder-product-images", "add-product-image"],
|
|
8175
8547
|
prerequisites: ["add-product-image"],
|
|
8176
8548
|
followUps: ["reorder-product-images"]
|
|
8549
|
+
},
|
|
8550
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8551
|
+
annotations: {
|
|
8552
|
+
readOnlyHint: false,
|
|
8553
|
+
destructiveHint: false,
|
|
8554
|
+
idempotentHint: true,
|
|
8555
|
+
// Same alt text update produces same result
|
|
8556
|
+
openWorldHint: true
|
|
8177
8557
|
}
|
|
8178
8558
|
},
|
|
8179
8559
|
handleUpdateProductImage
|
|
@@ -8181,19 +8561,19 @@ function registerUpdateProductImageTool() {
|
|
|
8181
8561
|
}
|
|
8182
8562
|
|
|
8183
8563
|
// src/tools/update-product-variant.ts
|
|
8184
|
-
import { z as
|
|
8185
|
-
var inputSchema39 =
|
|
8186
|
-
productId:
|
|
8564
|
+
import { z as z41 } from "zod";
|
|
8565
|
+
var inputSchema39 = z41.object({
|
|
8566
|
+
productId: productIdSchema.describe(
|
|
8187
8567
|
'Shopify product GID (e.g., "gid://shopify/Product/123"). Required. Use get-product to find the product ID containing the variant.'
|
|
8188
8568
|
),
|
|
8189
|
-
id:
|
|
8569
|
+
id: variantIdSchema.describe(
|
|
8190
8570
|
'Shopify variant GID (e.g., "gid://shopify/ProductVariant/456"). Required. Use get-product to find variant IDs for a product.'
|
|
8191
8571
|
),
|
|
8192
|
-
price:
|
|
8193
|
-
compareAtPrice:
|
|
8572
|
+
price: z41.string().optional().describe('New variant price as decimal string (e.g., "29.99")'),
|
|
8573
|
+
compareAtPrice: z41.string().nullable().optional().describe(
|
|
8194
8574
|
'Original price for sale display (e.g., "39.99"). When set higher than price, Shopify shows strikethrough pricing. Set to null to remove the compare at price.'
|
|
8195
8575
|
),
|
|
8196
|
-
barcode:
|
|
8576
|
+
barcode: z41.string().nullable().optional().describe("Barcode (UPC, EAN, ISBN, etc.). Set to null to remove.")
|
|
8197
8577
|
}).refine(
|
|
8198
8578
|
(data) => {
|
|
8199
8579
|
const { productId: _productId, id: _id, ...updateFields } = data;
|
|
@@ -8201,14 +8581,14 @@ var inputSchema39 = z40.object({
|
|
|
8201
8581
|
},
|
|
8202
8582
|
{ message: "At least one field to update must be provided (price, compareAtPrice, or barcode)" }
|
|
8203
8583
|
);
|
|
8204
|
-
var outputSchema39 =
|
|
8205
|
-
id:
|
|
8206
|
-
title:
|
|
8207
|
-
price:
|
|
8208
|
-
compareAtPrice:
|
|
8209
|
-
sku:
|
|
8210
|
-
barcode:
|
|
8211
|
-
inventoryQuantity:
|
|
8584
|
+
var outputSchema39 = z41.object({
|
|
8585
|
+
id: z41.string().describe('Variant GID (e.g., "gid://shopify/ProductVariant/123")'),
|
|
8586
|
+
title: z41.string().describe('Variant title (e.g., "Red / Medium")'),
|
|
8587
|
+
price: z41.string().describe("Current price as decimal string"),
|
|
8588
|
+
compareAtPrice: z41.string().nullable().describe("Compare at price for sale display"),
|
|
8589
|
+
sku: z41.string().nullable().describe("Stock keeping unit"),
|
|
8590
|
+
barcode: z41.string().nullable().describe("Barcode (UPC, EAN, etc.)"),
|
|
8591
|
+
inventoryQuantity: z41.number().nullable().describe("Available inventory quantity")
|
|
8212
8592
|
});
|
|
8213
8593
|
var handleUpdateProductVariant = async (context, params) => {
|
|
8214
8594
|
log.debug(`Updating product variant on shop: ${context.shopDomain}`);
|
|
@@ -8244,6 +8624,14 @@ function registerUpdateProductVariantTool() {
|
|
|
8244
8624
|
relationships: {
|
|
8245
8625
|
relatedTools: ["get-product", "get-inventory"],
|
|
8246
8626
|
prerequisites: ["get-product"]
|
|
8627
|
+
},
|
|
8628
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8629
|
+
annotations: {
|
|
8630
|
+
readOnlyHint: false,
|
|
8631
|
+
destructiveHint: false,
|
|
8632
|
+
idempotentHint: true,
|
|
8633
|
+
// Same update produces same result
|
|
8634
|
+
openWorldHint: true
|
|
8247
8635
|
}
|
|
8248
8636
|
},
|
|
8249
8637
|
handleUpdateProductVariant
|
|
@@ -8251,23 +8639,25 @@ function registerUpdateProductVariantTool() {
|
|
|
8251
8639
|
}
|
|
8252
8640
|
|
|
8253
8641
|
// src/tools/update-product.ts
|
|
8254
|
-
import { z as
|
|
8255
|
-
var inputSchema40 =
|
|
8256
|
-
id:
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8642
|
+
import { z as z42 } from "zod";
|
|
8643
|
+
var inputSchema40 = z42.object({
|
|
8644
|
+
id: productIdSchema.describe(
|
|
8645
|
+
'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Required.'
|
|
8646
|
+
),
|
|
8647
|
+
title: z42.string().min(1).optional().describe("New product title"),
|
|
8648
|
+
description: z42.string().optional().describe("New product description (HTML supported)"),
|
|
8649
|
+
vendor: z42.string().optional().describe("New product vendor/brand name"),
|
|
8650
|
+
productType: z42.string().optional().describe("New product type for categorization"),
|
|
8651
|
+
tags: z42.array(z42.string()).optional().describe("New tags (replaces existing tags)"),
|
|
8652
|
+
status: z42.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe("New product status: ACTIVE (visible), DRAFT (hidden), ARCHIVED (hidden)"),
|
|
8653
|
+
seo: z42.object({
|
|
8654
|
+
title: z42.string().optional().describe("SEO title for search engine results"),
|
|
8655
|
+
description: z42.string().optional().describe("SEO meta description for search engines")
|
|
8266
8656
|
}).optional().describe("SEO metadata for search engine optimization"),
|
|
8267
|
-
handle:
|
|
8657
|
+
handle: z42.string().optional().describe(
|
|
8268
8658
|
'New URL handle/slug (e.g., "my-product"). Use with redirectNewHandle for URL changes.'
|
|
8269
8659
|
),
|
|
8270
|
-
redirectNewHandle:
|
|
8660
|
+
redirectNewHandle: z42.boolean().optional().describe(
|
|
8271
8661
|
"If true, creates automatic redirect from old handle to new handle. Use when changing handle."
|
|
8272
8662
|
)
|
|
8273
8663
|
}).refine(
|
|
@@ -8277,40 +8667,40 @@ var inputSchema40 = z41.object({
|
|
|
8277
8667
|
},
|
|
8278
8668
|
{ message: "At least one field to update must be provided" }
|
|
8279
8669
|
);
|
|
8280
|
-
var outputSchema40 =
|
|
8281
|
-
id:
|
|
8282
|
-
title:
|
|
8283
|
-
handle:
|
|
8284
|
-
description:
|
|
8285
|
-
vendor:
|
|
8286
|
-
productType:
|
|
8287
|
-
status:
|
|
8288
|
-
tags:
|
|
8289
|
-
seo:
|
|
8290
|
-
title:
|
|
8291
|
-
description:
|
|
8670
|
+
var outputSchema40 = z42.object({
|
|
8671
|
+
id: z42.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
|
|
8672
|
+
title: z42.string().describe("Product title"),
|
|
8673
|
+
handle: z42.string().describe("URL handle/slug"),
|
|
8674
|
+
description: z42.string().nullable().describe("Product description (HTML)"),
|
|
8675
|
+
vendor: z42.string().describe("Vendor/brand name"),
|
|
8676
|
+
productType: z42.string().describe("Product type"),
|
|
8677
|
+
status: z42.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
|
|
8678
|
+
tags: z42.array(z42.string()).describe("Product tags"),
|
|
8679
|
+
seo: z42.object({
|
|
8680
|
+
title: z42.string().nullable().describe("SEO title for search engines"),
|
|
8681
|
+
description: z42.string().nullable().describe("SEO description/meta description")
|
|
8292
8682
|
}).describe("SEO metadata for search engine optimization"),
|
|
8293
|
-
createdAt:
|
|
8294
|
-
updatedAt:
|
|
8295
|
-
variants:
|
|
8296
|
-
|
|
8297
|
-
id:
|
|
8298
|
-
title:
|
|
8299
|
-
price:
|
|
8300
|
-
compareAtPrice:
|
|
8301
|
-
sku:
|
|
8302
|
-
barcode:
|
|
8303
|
-
inventoryQuantity:
|
|
8683
|
+
createdAt: z42.string().describe("Creation timestamp (ISO 8601)"),
|
|
8684
|
+
updatedAt: z42.string().describe("Last update timestamp (ISO 8601)"),
|
|
8685
|
+
variants: z42.array(
|
|
8686
|
+
z42.object({
|
|
8687
|
+
id: z42.string().describe("Variant GID"),
|
|
8688
|
+
title: z42.string().describe("Variant title"),
|
|
8689
|
+
price: z42.string().describe("Price"),
|
|
8690
|
+
compareAtPrice: z42.string().nullable().describe("Compare at price"),
|
|
8691
|
+
sku: z42.string().nullable().describe("SKU"),
|
|
8692
|
+
barcode: z42.string().nullable().describe("Barcode"),
|
|
8693
|
+
inventoryQuantity: z42.number().nullable().describe("Inventory quantity")
|
|
8304
8694
|
})
|
|
8305
8695
|
).describe("Product variants"),
|
|
8306
|
-
images:
|
|
8307
|
-
|
|
8308
|
-
id:
|
|
8309
|
-
url:
|
|
8310
|
-
altText:
|
|
8696
|
+
images: z42.array(
|
|
8697
|
+
z42.object({
|
|
8698
|
+
id: z42.string().describe("Image GID"),
|
|
8699
|
+
url: z42.string().describe("Image URL"),
|
|
8700
|
+
altText: z42.string().nullable().describe("Alt text")
|
|
8311
8701
|
})
|
|
8312
8702
|
).describe("Product images"),
|
|
8313
|
-
totalInventory:
|
|
8703
|
+
totalInventory: z42.number().describe("Total inventory across variants")
|
|
8314
8704
|
});
|
|
8315
8705
|
var handleUpdateProduct = async (context, params) => {
|
|
8316
8706
|
log.debug(`Updating product on shop: ${context.shopDomain}`);
|
|
@@ -8347,6 +8737,14 @@ function registerUpdateProductTool() {
|
|
|
8347
8737
|
relatedTools: ["list-products", "get-product"],
|
|
8348
8738
|
prerequisites: ["get-product"],
|
|
8349
8739
|
followUps: ["add-product-image", "set-product-metafields"]
|
|
8740
|
+
},
|
|
8741
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
8742
|
+
annotations: {
|
|
8743
|
+
readOnlyHint: false,
|
|
8744
|
+
destructiveHint: false,
|
|
8745
|
+
idempotentHint: true,
|
|
8746
|
+
// Same update produces same result
|
|
8747
|
+
openWorldHint: true
|
|
8350
8748
|
}
|
|
8351
8749
|
},
|
|
8352
8750
|
handleUpdateProduct
|
|
@@ -8362,7 +8760,9 @@ function setupToolHandlers(server) {
|
|
|
8362
8760
|
tools: tools.map((tool) => ({
|
|
8363
8761
|
name: tool.name,
|
|
8364
8762
|
description: tool.description,
|
|
8365
|
-
inputSchema: tool.inputSchema
|
|
8763
|
+
inputSchema: tool.inputSchema,
|
|
8764
|
+
// Include MCP tool annotations (Epic 9.5 - MCP Best Practices)
|
|
8765
|
+
annotations: tool.annotations
|
|
8366
8766
|
}))
|
|
8367
8767
|
};
|
|
8368
8768
|
});
|