@anton.andrusenko/shopify-mcp-admin 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +20 -6
  2. package/dist/index.js +1250 -649
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2451,7 +2451,7 @@ function wrapToolHandler(toolName, schema, handler) {
2451
2451
  };
2452
2452
  }
2453
2453
  function registerTool(definition, handler, options = {}) {
2454
- const { name, title, description, inputSchema: inputSchema41, annotations } = definition;
2454
+ const { name, title, description, inputSchema: inputSchema46, annotations } = definition;
2455
2455
  try {
2456
2456
  if (!options.skipNameValidation) {
2457
2457
  validateToolName(name);
@@ -2459,8 +2459,8 @@ function registerTool(definition, handler, options = {}) {
2459
2459
  if (registeredTools.has(name)) {
2460
2460
  throw new Error(`Tool "${name}" is already registered. Tool names must be unique.`);
2461
2461
  }
2462
- const jsonSchema = convertZodToJsonSchema(inputSchema41);
2463
- const wrappedHandler = wrapToolHandler(name, inputSchema41, handler);
2462
+ const jsonSchema = convertZodToJsonSchema(inputSchema46);
2463
+ const wrappedHandler = wrapToolHandler(name, inputSchema46, handler);
2464
2464
  const finalAnnotations = {
2465
2465
  ...deriveDefaultAnnotations(name),
2466
2466
  ...annotations,
@@ -2472,7 +2472,7 @@ function registerTool(definition, handler, options = {}) {
2472
2472
  title,
2473
2473
  description,
2474
2474
  inputSchema: jsonSchema,
2475
- zodSchema: inputSchema41,
2475
+ zodSchema: inputSchema46,
2476
2476
  handler: wrappedHandler,
2477
2477
  annotations: finalAnnotations
2478
2478
  };
@@ -3172,6 +3172,7 @@ var articleIdSchema = gidSchema("Article");
3172
3172
  var redirectIdSchema = gidSchema("UrlRedirect");
3173
3173
  var inventoryItemIdSchema = gidSchema("InventoryItem");
3174
3174
  var locationIdSchema = gidSchema("Location");
3175
+ var marketIdSchema = gidSchema("Market");
3175
3176
 
3176
3177
  // src/tools/add-product-image.ts
3177
3178
  var inputSchema = z3.object({
@@ -4518,9 +4519,339 @@ function registerCreateCollectionTool() {
4518
4519
  );
4519
4520
  }
4520
4521
 
4521
- // src/tools/create-page.ts
4522
+ // src/tools/create-market.ts
4522
4523
  import { z as z8 } from "zod";
4523
4524
 
4525
+ // src/shopify/markets.ts
4526
+ var LIST_MARKETS_QUERY = `
4527
+ query Markets($first: Int!, $after: String) {
4528
+ markets(first: $first, after: $after) {
4529
+ nodes {
4530
+ id
4531
+ name
4532
+ handle
4533
+ enabled
4534
+ status
4535
+ type
4536
+ currencySettings {
4537
+ baseCurrency { currencyCode }
4538
+ localCurrencies
4539
+ }
4540
+ }
4541
+ pageInfo {
4542
+ hasNextPage
4543
+ endCursor
4544
+ }
4545
+ }
4546
+ }
4547
+ `;
4548
+ var GET_MARKET_QUERY = `
4549
+ query Market($id: ID!) {
4550
+ market(id: $id) {
4551
+ id
4552
+ name
4553
+ handle
4554
+ enabled
4555
+ status
4556
+ type
4557
+ currencySettings {
4558
+ baseCurrency { currencyCode currencyName }
4559
+ localCurrencies
4560
+ }
4561
+ webPresences(first: 10) {
4562
+ nodes {
4563
+ id
4564
+ defaultLocale { locale name }
4565
+ alternateLocales { locale name }
4566
+ subfolderSuffix
4567
+ domain { host }
4568
+ rootUrls { locale url }
4569
+ }
4570
+ }
4571
+ }
4572
+ }
4573
+ `;
4574
+ var MARKET_CREATE_MUTATION = `
4575
+ mutation MarketCreate($input: MarketCreateInput!) {
4576
+ marketCreate(input: $input) {
4577
+ market { id name handle enabled }
4578
+ userErrors { field message }
4579
+ }
4580
+ }
4581
+ `;
4582
+ var MARKET_UPDATE_MUTATION = `
4583
+ mutation MarketUpdate($id: ID!, $input: MarketUpdateInput!) {
4584
+ marketUpdate(id: $id, input: $input) {
4585
+ market { id name handle enabled }
4586
+ userErrors { field message }
4587
+ }
4588
+ }
4589
+ `;
4590
+ var MARKET_DELETE_MUTATION = `
4591
+ mutation MarketDelete($id: ID!) {
4592
+ marketDelete(id: $id) {
4593
+ deletedId
4594
+ userErrors { field message }
4595
+ }
4596
+ }
4597
+ `;
4598
+ async function listMarkets(first = 50, after) {
4599
+ const client = await getShopifyClient();
4600
+ log.debug(`Listing markets: first=${first}, after=${after}`);
4601
+ const response = await withRateLimit(
4602
+ () => client.graphql.request(LIST_MARKETS_QUERY, {
4603
+ variables: { first, after }
4604
+ }),
4605
+ "list-markets"
4606
+ );
4607
+ if (response.errors && response.errors.length > 0) {
4608
+ const errorMessage = response.errors.map((e) => e.message).join(", ");
4609
+ throw new Error(`GraphQL error: ${errorMessage}`);
4610
+ }
4611
+ const marketsData = response.data?.markets;
4612
+ if (!marketsData) {
4613
+ throw new Error("No response data from markets query");
4614
+ }
4615
+ const markets = marketsData.nodes.map((node) => ({
4616
+ id: node.id,
4617
+ name: node.name,
4618
+ handle: node.handle,
4619
+ enabled: node.enabled,
4620
+ status: node.status,
4621
+ type: node.type,
4622
+ currencySettings: node.currencySettings ? {
4623
+ baseCurrency: {
4624
+ currencyCode: node.currencySettings.baseCurrency.currencyCode
4625
+ },
4626
+ localCurrencies: node.currencySettings.localCurrencies
4627
+ } : void 0
4628
+ }));
4629
+ log.debug(`Listed ${markets.length} markets`);
4630
+ return {
4631
+ markets,
4632
+ pageInfo: {
4633
+ hasNextPage: marketsData.pageInfo.hasNextPage,
4634
+ endCursor: marketsData.pageInfo.endCursor
4635
+ }
4636
+ };
4637
+ }
4638
+ async function getMarket(marketId) {
4639
+ const client = await getShopifyClient();
4640
+ log.debug(`Fetching market: ${marketId}`);
4641
+ const response = await withRateLimit(
4642
+ () => client.graphql.request(GET_MARKET_QUERY, {
4643
+ variables: { id: marketId }
4644
+ }),
4645
+ "get-market"
4646
+ );
4647
+ if (response.errors && response.errors.length > 0) {
4648
+ const errorMessage = response.errors.map((e) => e.message).join(", ");
4649
+ throw new Error(`GraphQL error: ${errorMessage}`);
4650
+ }
4651
+ const market = response.data?.market;
4652
+ if (!market) {
4653
+ log.debug(`Market not found: ${marketId}`);
4654
+ return null;
4655
+ }
4656
+ return {
4657
+ id: market.id,
4658
+ name: market.name,
4659
+ handle: market.handle,
4660
+ enabled: market.enabled,
4661
+ status: market.status,
4662
+ type: market.type,
4663
+ currencySettings: market.currencySettings ? {
4664
+ baseCurrency: {
4665
+ currencyCode: market.currencySettings.baseCurrency.currencyCode,
4666
+ currencyName: market.currencySettings.baseCurrency.currencyName
4667
+ },
4668
+ localCurrencies: market.currencySettings.localCurrencies
4669
+ } : void 0,
4670
+ webPresences: market.webPresences?.nodes.map((wp) => ({
4671
+ id: wp.id,
4672
+ defaultLocale: {
4673
+ locale: wp.defaultLocale.locale,
4674
+ name: wp.defaultLocale.name
4675
+ },
4676
+ alternateLocales: wp.alternateLocales?.map((al) => ({
4677
+ locale: al.locale,
4678
+ name: al.name
4679
+ })),
4680
+ subfolderSuffix: wp.subfolderSuffix ?? void 0,
4681
+ domain: wp.domain ? { host: wp.domain.host } : void 0,
4682
+ rootUrls: wp.rootUrls?.map((ru) => ({
4683
+ locale: ru.locale,
4684
+ url: ru.url
4685
+ }))
4686
+ }))
4687
+ };
4688
+ }
4689
+ async function createMarket(input) {
4690
+ const client = await getShopifyClient();
4691
+ log.debug(`Creating market: ${input.name}`);
4692
+ const graphqlInput = {
4693
+ name: input.name
4694
+ };
4695
+ if (input.handle !== void 0) {
4696
+ graphqlInput.handle = input.handle;
4697
+ }
4698
+ if (input.regions !== void 0) {
4699
+ graphqlInput.regions = input.regions;
4700
+ }
4701
+ if (input.enabled !== void 0) {
4702
+ graphqlInput.enabled = input.enabled;
4703
+ }
4704
+ const response = await withRateLimit(
4705
+ () => client.graphql.request(MARKET_CREATE_MUTATION, {
4706
+ variables: { input: graphqlInput }
4707
+ }),
4708
+ "create-market"
4709
+ );
4710
+ if (response.errors && response.errors.length > 0) {
4711
+ const errorMessage = response.errors.map((e) => e.message).join(", ");
4712
+ throw new Error(`GraphQL error: ${errorMessage}`);
4713
+ }
4714
+ const result = response.data?.marketCreate;
4715
+ if (!result) {
4716
+ throw new Error("No response data from marketCreate mutation");
4717
+ }
4718
+ if (result.userErrors && result.userErrors.length > 0) {
4719
+ throw new ShopifyUserErrorException(result.userErrors);
4720
+ }
4721
+ if (!result.market) {
4722
+ throw new Error("Market creation succeeded but no market was returned");
4723
+ }
4724
+ log.debug(`Created market: ${result.market.id}`);
4725
+ return result.market;
4726
+ }
4727
+ async function updateMarket(marketId, input) {
4728
+ const client = await getShopifyClient();
4729
+ log.debug(`Updating market: ${marketId}`);
4730
+ const graphqlInput = {};
4731
+ if (input.name !== void 0) {
4732
+ graphqlInput.name = input.name;
4733
+ }
4734
+ if (input.handle !== void 0) {
4735
+ graphqlInput.handle = input.handle;
4736
+ }
4737
+ if (input.enabled !== void 0) {
4738
+ graphqlInput.enabled = input.enabled;
4739
+ }
4740
+ const response = await withRateLimit(
4741
+ () => client.graphql.request(MARKET_UPDATE_MUTATION, {
4742
+ variables: { id: marketId, input: graphqlInput }
4743
+ }),
4744
+ "update-market"
4745
+ );
4746
+ if (response.errors && response.errors.length > 0) {
4747
+ const errorMessage = response.errors.map((e) => e.message).join(", ");
4748
+ throw new Error(`GraphQL error: ${errorMessage}`);
4749
+ }
4750
+ const result = response.data?.marketUpdate;
4751
+ if (!result) {
4752
+ throw new Error("No response data from marketUpdate mutation");
4753
+ }
4754
+ if (result.userErrors && result.userErrors.length > 0) {
4755
+ throw new ShopifyUserErrorException(result.userErrors);
4756
+ }
4757
+ if (!result.market) {
4758
+ throw new Error("Market update succeeded but no market was returned");
4759
+ }
4760
+ log.debug(`Updated market: ${result.market.id}`);
4761
+ return result.market;
4762
+ }
4763
+ async function deleteMarket(marketId) {
4764
+ const client = await getShopifyClient();
4765
+ log.debug(`Deleting market: ${marketId}`);
4766
+ const response = await withRateLimit(
4767
+ () => client.graphql.request(MARKET_DELETE_MUTATION, {
4768
+ variables: { id: marketId }
4769
+ }),
4770
+ "delete-market"
4771
+ );
4772
+ if (response.errors && response.errors.length > 0) {
4773
+ const errorMessage = response.errors.map((e) => e.message).join(", ");
4774
+ throw new Error(`GraphQL error: ${errorMessage}`);
4775
+ }
4776
+ const result = response.data?.marketDelete;
4777
+ if (!result) {
4778
+ throw new Error("No response data from marketDelete mutation");
4779
+ }
4780
+ if (result.userErrors && result.userErrors.length > 0) {
4781
+ throw new ShopifyUserErrorException(result.userErrors);
4782
+ }
4783
+ if (!result.deletedId) {
4784
+ throw new Error("Market deletion succeeded but no deletedId was returned");
4785
+ }
4786
+ log.debug(`Deleted market: ${result.deletedId}`);
4787
+ return result.deletedId;
4788
+ }
4789
+
4790
+ // src/tools/create-market.ts
4791
+ var inputSchema6 = z8.object({
4792
+ name: z8.string().min(1).describe('Market name (required). Not shown to customers. Example: "Canada", "Europe"'),
4793
+ handle: z8.string().optional().describe(
4794
+ 'URL handle/slug for the market. Auto-generated from name if not provided. Example: "ca" for Canada, "eu" for Europe'
4795
+ ),
4796
+ countryCodes: z8.array(z8.string().length(2)).optional().describe(
4797
+ 'Array of ISO 3166-1 alpha-2 country codes to include in this market. Example: ["CA"] for Canada, ["FR", "DE", "IT"] for multiple European countries'
4798
+ ),
4799
+ enabled: z8.boolean().optional().default(true).describe("Whether the market should be enabled. Default: true")
4800
+ });
4801
+ var outputSchema6 = z8.object({
4802
+ id: z8.string().describe("Created market GID"),
4803
+ name: z8.string().describe("Market name"),
4804
+ handle: z8.string().describe("URL handle"),
4805
+ enabled: z8.boolean().describe("Whether the market is enabled")
4806
+ });
4807
+ var handleCreateMarket = async (context, params) => {
4808
+ log.debug(`Creating market "${params.name}" on shop: ${context.shopDomain}`);
4809
+ const input = {
4810
+ name: params.name
4811
+ };
4812
+ if (params.handle !== void 0) {
4813
+ input.handle = params.handle;
4814
+ }
4815
+ if (params.countryCodes !== void 0 && params.countryCodes.length > 0) {
4816
+ input.regions = { countryCodes: params.countryCodes };
4817
+ }
4818
+ if (params.enabled !== void 0) {
4819
+ input.enabled = params.enabled;
4820
+ }
4821
+ const market = await createMarket(input);
4822
+ log.debug(`Created market: ${market.id}`);
4823
+ return market;
4824
+ };
4825
+ function registerCreateMarketTool() {
4826
+ registerContextAwareTool(
4827
+ {
4828
+ name: "create-market",
4829
+ title: "Create Market",
4830
+ description: "Create a new market for regional targeting. Markets group one or more regions (countries) for localized shopping experiences. Provide a name (required) and optionally: handle, country codes, and enabled status. **Typical workflow:** create-market \u2192 create-web-presence (configure SEO URLs) \u2192 enable-shop-locale (add languages). **Prerequisites:** None. Store must have Markets feature enabled on their plan. **Follow-ups:** get-market, create-web-presence, update-market.",
4831
+ inputSchema: inputSchema6,
4832
+ outputSchema: outputSchema6,
4833
+ // AI Agent Optimization
4834
+ category: "market",
4835
+ relationships: {
4836
+ relatedTools: ["list-markets", "get-market"],
4837
+ followUps: ["get-market", "update-market"]
4838
+ },
4839
+ // MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
4840
+ annotations: {
4841
+ readOnlyHint: false,
4842
+ destructiveHint: false,
4843
+ idempotentHint: false,
4844
+ // Creating markets is not idempotent
4845
+ openWorldHint: true
4846
+ }
4847
+ },
4848
+ handleCreateMarket
4849
+ );
4850
+ }
4851
+
4852
+ // src/tools/create-page.ts
4853
+ import { z as z9 } from "zod";
4854
+
4524
4855
  // src/shopify/pages.ts
4525
4856
  var PAGE_QUERY = `
4526
4857
  query GetPage($id: ID!) {
@@ -4801,28 +5132,28 @@ async function deletePage(pageId) {
4801
5132
  }
4802
5133
 
4803
5134
  // src/tools/create-page.ts
4804
- var inputSchema6 = z8.object({
4805
- title: z8.string().min(1).describe(
5135
+ var inputSchema7 = z9.object({
5136
+ title: z9.string().min(1).describe(
4806
5137
  "The title of the page (required). This will be displayed as the page heading. Example: 'About Us', 'Contact', 'Privacy Policy'."
4807
5138
  ),
4808
- body: z8.string().optional().describe(
5139
+ body: z9.string().optional().describe(
4809
5140
  "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>'"
4810
5141
  ),
4811
- handle: z8.string().optional().describe(
5142
+ handle: z9.string().optional().describe(
4812
5143
  "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."
4813
5144
  ),
4814
- isPublished: z8.boolean().optional().describe(
5145
+ isPublished: z9.boolean().optional().describe(
4815
5146
  "Whether the page should be visible on the storefront. Defaults to false (unpublished). Set to true to make the page immediately visible."
4816
5147
  ),
4817
- templateSuffix: z8.string().optional().describe(
5148
+ templateSuffix: z9.string().optional().describe(
4818
5149
  "The suffix of the Liquid template used to render the page. For example, 'contact' would use the template 'page.contact.liquid'."
4819
5150
  )
4820
5151
  });
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")
5152
+ var outputSchema7 = z9.object({
5153
+ id: z9.string().describe('Created page GID (e.g., "gid://shopify/Page/123")'),
5154
+ title: z9.string().describe("Page title"),
5155
+ handle: z9.string().describe("URL handle/slug"),
5156
+ isPublished: z9.boolean().describe("Whether page is visible on storefront")
4826
5157
  });
4827
5158
  var handleCreatePage = async (context, params) => {
4828
5159
  log.debug(`Creating page on shop: ${context.shopDomain}`);
@@ -4859,8 +5190,8 @@ function registerCreatePageTool() {
4859
5190
  name: "create-page",
4860
5191
  title: "Create Page",
4861
5192
  description: "Create a new online store page. Pages are static content like 'About Us', 'Contact', 'FAQ', or policy pages. Provide a title (required) and optional body content (HTML supported), handle (URL slug), and publish status. New pages are unpublished by default. Useful for: creating informational pages, legal pages, landing pages. **Typical workflow:** create-page \u2192 update-page (to publish). **Prerequisites:** None. **Follow-ups:** get-page, update-page. Returns created page ID, title, handle, and publish status.",
4862
- inputSchema: inputSchema6,
4863
- outputSchema: outputSchema6,
5193
+ inputSchema: inputSchema7,
5194
+ outputSchema: outputSchema7,
4864
5195
  category: "content",
4865
5196
  relationships: {
4866
5197
  relatedTools: ["list-pages", "get-page", "update-page"],
@@ -4880,78 +5211,78 @@ function registerCreatePageTool() {
4880
5211
  }
4881
5212
 
4882
5213
  // src/tools/create-product.ts
4883
- import { z as z9 } from "zod";
4884
- var inputSchema7 = z9.object({
5214
+ import { z as z10 } from "zod";
5215
+ var inputSchema8 = z10.object({
4885
5216
  // Required field
4886
- title: z9.string().min(1, "title is required").describe('Product title (required). Example: "Premium Cotton T-Shirt"'),
5217
+ title: z10.string().min(1, "title is required").describe('Product title (required). Example: "Premium Cotton T-Shirt"'),
4887
5218
  // Optional core fields
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(
5219
+ description: z10.string().optional().describe('Product description (HTML supported). Example: "<p>Soft cotton t-shirt.</p>"'),
5220
+ vendor: z10.string().optional().describe('Product vendor/brand name. Example: "Acme Corp"'),
5221
+ productType: z10.string().optional().describe('Product type for categorization. Example: "T-Shirts"'),
5222
+ tags: z10.array(z10.string()).optional().describe('Tags for search and filtering. Example: ["summer", "cotton", "casual"]'),
5223
+ status: z10.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().default("DRAFT").describe(
4893
5224
  "Product status: ACTIVE (visible), DRAFT (hidden), ARCHIVED (hidden). Default: DRAFT"
4894
5225
  ),
4895
5226
  // SEO metadata
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")
5227
+ seo: z10.object({
5228
+ title: z10.string().optional().describe("SEO title for search engine results"),
5229
+ description: z10.string().optional().describe("SEO meta description for search engines")
4899
5230
  }).optional().describe(
4900
5231
  "SEO metadata for search engine optimization. If not provided, Shopify uses product title/description."
4901
5232
  ),
4902
5233
  // Variants
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"]')
5234
+ variants: z10.array(
5235
+ z10.object({
5236
+ price: z10.string().describe('Variant price as decimal string. Example: "29.99"'),
5237
+ compareAtPrice: z10.string().optional().describe('Original price for sale display. Example: "39.99"'),
5238
+ sku: z10.string().optional().describe('Stock keeping unit. Example: "SHIRT-001-RED-M"'),
5239
+ barcode: z10.string().optional().describe("Barcode (UPC, EAN, etc.)"),
5240
+ inventoryQuantity: z10.number().int().min(0).optional().describe("Initial inventory quantity (note: may require separate inventory API)"),
5241
+ options: z10.array(z10.string()).optional().describe('Variant options. Example: ["Red", "Medium"]')
4911
5242
  })
4912
5243
  ).optional().describe("Product variants. If not provided, a default variant is created."),
4913
5244
  // Images
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")
5245
+ images: z10.array(
5246
+ z10.object({
5247
+ url: z10.string().url("Invalid image URL format").describe("Publicly accessible image URL"),
5248
+ altText: z10.string().optional().describe("Alt text for accessibility")
4918
5249
  })
4919
5250
  ).optional().describe("Product images. Shopify fetches images from URLs.")
4920
5251
  });
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")
5252
+ var outputSchema8 = z10.object({
5253
+ id: z10.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
5254
+ title: z10.string().describe("Product title"),
5255
+ handle: z10.string().describe("URL handle/slug"),
5256
+ description: z10.string().nullable().describe("Product description (HTML)"),
5257
+ vendor: z10.string().describe("Vendor/brand name"),
5258
+ productType: z10.string().describe("Product type"),
5259
+ status: z10.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
5260
+ tags: z10.array(z10.string()).describe("Product tags"),
5261
+ seo: z10.object({
5262
+ title: z10.string().nullable().describe("SEO title for search engines"),
5263
+ description: z10.string().nullable().describe("SEO description/meta description")
4933
5264
  }).describe("SEO metadata for search engine optimization"),
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")
5265
+ createdAt: z10.string().describe("Creation timestamp (ISO 8601)"),
5266
+ updatedAt: z10.string().describe("Last update timestamp (ISO 8601)"),
5267
+ variants: z10.array(
5268
+ z10.object({
5269
+ id: z10.string().describe("Variant GID"),
5270
+ title: z10.string().describe("Variant title"),
5271
+ price: z10.string().describe("Price"),
5272
+ compareAtPrice: z10.string().nullable().describe("Compare at price"),
5273
+ sku: z10.string().nullable().describe("SKU"),
5274
+ barcode: z10.string().nullable().describe("Barcode"),
5275
+ inventoryQuantity: z10.number().nullable().describe("Inventory quantity")
4945
5276
  })
4946
5277
  ).describe("Product variants"),
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")
5278
+ images: z10.array(
5279
+ z10.object({
5280
+ id: z10.string().describe("Image GID"),
5281
+ url: z10.string().describe("Image URL"),
5282
+ altText: z10.string().nullable().describe("Alt text")
4952
5283
  })
4953
5284
  ).describe("Product images"),
4954
- totalInventory: z9.number().describe("Total inventory across variants")
5285
+ totalInventory: z10.number().describe("Total inventory across variants")
4955
5286
  });
4956
5287
  var handleCreateProduct = async (context, params) => {
4957
5288
  log.debug(`Creating product on shop: ${context.shopDomain}`);
@@ -4986,8 +5317,8 @@ function registerCreateProductTool() {
4986
5317
  name: "create-product",
4987
5318
  title: "Create Product",
4988
5319
  description: "Create a new product in the Shopify store. Supports setting title, description, vendor, type, tags, status, and SEO metadata. Optionally include variants with pricing/SKU and images with URLs. SEO (seo.title, seo.description) is optional - defaults to product title/description if not set. Returns the created product with ID, handle, SEO, variants, and images. Status defaults to DRAFT if not specified. **Typical workflow:** create-product \u2192 add-product-image \u2192 set-product-metafields \u2192 add-products-to-collection. **Prerequisites:** None. **Follow-ups:** add-product-image, set-product-metafields, add-products-to-collection.",
4989
- inputSchema: inputSchema7,
4990
- outputSchema: outputSchema7,
5320
+ inputSchema: inputSchema8,
5321
+ outputSchema: outputSchema8,
4991
5322
  // AI Agent Optimization (Story 5.5)
4992
5323
  category: "product",
4993
5324
  relationships: {
@@ -5009,7 +5340,7 @@ function registerCreateProductTool() {
5009
5340
  }
5010
5341
 
5011
5342
  // src/tools/create-redirect.ts
5012
- import { z as z10 } from "zod";
5343
+ import { z as z11 } from "zod";
5013
5344
 
5014
5345
  // src/shopify/redirects.ts
5015
5346
  var URL_REDIRECT_CREATE_MUTATION = `
@@ -5157,20 +5488,20 @@ async function deleteRedirect(redirectId) {
5157
5488
  }
5158
5489
 
5159
5490
  // src/tools/create-redirect.ts
5160
- var inputSchema8 = z10.object({
5161
- path: z10.string().min(1).refine((val) => val.startsWith("/"), {
5491
+ var inputSchema9 = z11.object({
5492
+ path: z11.string().min(1).refine((val) => val.startsWith("/"), {
5162
5493
  message: "Path must start with '/' (e.g., '/old-product-url')"
5163
5494
  }).describe(
5164
5495
  "The source path to redirect FROM. Must start with '/'. Example: '/old-product-url' or '/collections/old-collection'."
5165
5496
  ),
5166
- target: z10.string().min(1).describe(
5497
+ target: z11.string().min(1).describe(
5167
5498
  "The destination URL to redirect TO. Can be relative ('/new-product-url') or absolute ('https://example.com/page')."
5168
5499
  )
5169
5500
  });
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")
5501
+ var outputSchema9 = z11.object({
5502
+ id: z11.string().describe('Created redirect GID (e.g., "gid://shopify/UrlRedirect/123")'),
5503
+ path: z11.string().describe("Source path that will redirect"),
5504
+ target: z11.string().describe("Target URL visitors will be redirected to")
5174
5505
  });
5175
5506
  var handleCreateRedirect = async (context, params) => {
5176
5507
  log.debug(`Creating redirect on shop: ${context.shopDomain}`);
@@ -5204,8 +5535,8 @@ function registerCreateRedirectTool() {
5204
5535
  name: "create-redirect",
5205
5536
  title: "Create URL Redirect",
5206
5537
  description: "Create a URL redirect to preserve SEO value when URLs change. Redirects automatically forward visitors and search engines from an old path to a new target URL. Path must start with '/'. Useful for: product URL changes, collection restructuring, page migrations, fixing broken links. **Prerequisites:** None. **Follow-ups:** list-redirects to verify creation.",
5207
- inputSchema: inputSchema8,
5208
- outputSchema: outputSchema8,
5538
+ inputSchema: inputSchema9,
5539
+ outputSchema: outputSchema9,
5209
5540
  // AI Agent Optimization (Story 5.5)
5210
5541
  category: "seo",
5211
5542
  relationships: {
@@ -5226,15 +5557,15 @@ function registerCreateRedirectTool() {
5226
5557
  }
5227
5558
 
5228
5559
  // src/tools/delete-article.ts
5229
- import { z as z11 } from "zod";
5230
- var inputSchema9 = z11.object({
5560
+ import { z as z12 } from "zod";
5561
+ var inputSchema10 = z12.object({
5231
5562
  id: articleIdSchema.describe(
5232
5563
  '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.'
5233
5564
  )
5234
5565
  });
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")
5566
+ var outputSchema10 = z12.object({
5567
+ success: z12.boolean().describe("Whether the deletion was successful"),
5568
+ deletedArticleId: z12.string().describe("The ID of the deleted article")
5238
5569
  });
5239
5570
  var handleDeleteArticle = async (context, params) => {
5240
5571
  log.debug(`Deleting article on shop: ${context.shopDomain}`);
@@ -5268,8 +5599,8 @@ function registerDeleteArticleTool() {
5268
5599
  name: "delete-article",
5269
5600
  title: "Delete Article",
5270
5601
  description: "Permanently delete an article by ID. This action cannot be undone. The article will no longer be accessible on the storefront. Use list-articles to find the article ID first. Consider unpublishing instead of deleting if you may need the content later. **Typical workflow:** list-articles \u2192 delete-article. **Prerequisites:** Article ID. **Follow-ups:** None. Returns success confirmation and deleted article ID.",
5271
- inputSchema: inputSchema9,
5272
- outputSchema: outputSchema9,
5602
+ inputSchema: inputSchema10,
5603
+ outputSchema: outputSchema10,
5273
5604
  category: "content",
5274
5605
  relationships: {
5275
5606
  relatedTools: ["list-articles", "update-article"],
@@ -5290,15 +5621,15 @@ function registerDeleteArticleTool() {
5290
5621
  }
5291
5622
 
5292
5623
  // src/tools/delete-blog.ts
5293
- import { z as z12 } from "zod";
5294
- var inputSchema10 = z12.object({
5624
+ import { z as z13 } from "zod";
5625
+ var inputSchema11 = z13.object({
5295
5626
  id: blogIdSchema.describe(
5296
5627
  '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.'
5297
5628
  )
5298
5629
  });
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")
5630
+ var outputSchema11 = z13.object({
5631
+ success: z13.boolean().describe("Whether the deletion was successful"),
5632
+ deletedBlogId: z13.string().describe("The ID of the deleted blog")
5302
5633
  });
5303
5634
  var handleDeleteBlog = async (context, params) => {
5304
5635
  log.debug(`Deleting blog on shop: ${context.shopDomain}`);
@@ -5332,8 +5663,8 @@ function registerDeleteBlogTool() {
5332
5663
  name: "delete-blog",
5333
5664
  title: "Delete Blog",
5334
5665
  description: "Permanently delete a blog by ID. This will also delete ALL articles within the blog. This action cannot be undone. Use list-blogs to find the blog ID first. Consider archiving articles before deletion if content may be needed later. **Typical workflow:** list-blogs \u2192 list-articles \u2192 delete-blog. **Prerequisites:** Blog ID. **Follow-ups:** None. Returns success confirmation and deleted blog ID.",
5335
- inputSchema: inputSchema10,
5336
- outputSchema: outputSchema10,
5666
+ inputSchema: inputSchema11,
5667
+ outputSchema: outputSchema11,
5337
5668
  category: "content",
5338
5669
  relationships: {
5339
5670
  relatedTools: ["list-blogs", "list-articles"],
@@ -5353,14 +5684,14 @@ function registerDeleteBlogTool() {
5353
5684
  }
5354
5685
 
5355
5686
  // src/tools/delete-collection.ts
5356
- import { z as z13 } from "zod";
5357
- var inputSchema11 = z13.object({
5687
+ import { z as z14 } from "zod";
5688
+ var inputSchema12 = z14.object({
5358
5689
  collectionId: collectionIdSchema.describe(
5359
5690
  'Collection ID to delete (GID format, e.g., "gid://shopify/Collection/123"). Use list-collections or get-collection to find collection IDs.'
5360
5691
  )
5361
5692
  });
5362
- var outputSchema11 = z13.object({
5363
- deletedCollectionId: z13.string().describe("The GID of the deleted collection")
5693
+ var outputSchema12 = z14.object({
5694
+ deletedCollectionId: z14.string().describe("The GID of the deleted collection")
5364
5695
  });
5365
5696
  var handleDeleteCollection = async (context, params) => {
5366
5697
  log.debug(`Deleting collection on shop: ${context.shopDomain}`);
@@ -5390,8 +5721,8 @@ function registerDeleteCollectionTool() {
5390
5721
  name: "delete-collection",
5391
5722
  title: "Delete Collection",
5392
5723
  description: "Permanently delete a collection from the store. This removes the collection but does NOT delete the products within it - they remain in the catalog. **Warning:** This action cannot be undone. **Prerequisites:** get-collection to verify the correct collection before deleting.",
5393
- inputSchema: inputSchema11,
5394
- outputSchema: outputSchema11,
5724
+ inputSchema: inputSchema12,
5725
+ outputSchema: outputSchema12,
5395
5726
  // AI Agent Optimization (Story 5.5)
5396
5727
  category: "collection",
5397
5728
  relationships: {
@@ -5412,16 +5743,66 @@ function registerDeleteCollectionTool() {
5412
5743
  );
5413
5744
  }
5414
5745
 
5746
+ // src/tools/delete-market.ts
5747
+ import { z as z15 } from "zod";
5748
+ var inputSchema13 = z15.object({
5749
+ id: marketIdSchema.describe(
5750
+ 'Shopify Market GID to delete (e.g., "gid://shopify/Market/123"). Use list-markets or get-market to find market IDs. WARNING: This action cannot be undone.'
5751
+ )
5752
+ });
5753
+ var outputSchema13 = z15.object({
5754
+ deletedId: z15.string().describe("Deleted market GID"),
5755
+ success: z15.boolean().describe("Whether the deletion was successful"),
5756
+ message: z15.string().describe("Confirmation message")
5757
+ });
5758
+ var handleDeleteMarket = async (context, params) => {
5759
+ log.debug(`Deleting market ${params.id} on shop: ${context.shopDomain}`);
5760
+ const deletedId = await deleteMarket(params.id);
5761
+ log.debug(`Deleted market: ${deletedId}`);
5762
+ return {
5763
+ deletedId,
5764
+ success: true,
5765
+ message: `Market ${deletedId} has been permanently deleted. All associated web presences, currency settings, and region configurations have been removed.`
5766
+ };
5767
+ };
5768
+ function registerDeleteMarketTool() {
5769
+ registerContextAwareTool(
5770
+ {
5771
+ name: "delete-market",
5772
+ title: "Delete Market",
5773
+ description: "Permanently delete a market from the store. **WARNING:** This action cannot be undone. Deleting a market removes all its configurations including web presences, currency settings, and region assignments. Products assigned to this market will no longer be available in those regions. **Prerequisites:** get-market to verify the correct market before deleting. **Note:** The primary market cannot be deleted.",
5774
+ inputSchema: inputSchema13,
5775
+ outputSchema: outputSchema13,
5776
+ // AI Agent Optimization
5777
+ category: "market",
5778
+ relationships: {
5779
+ relatedTools: ["get-market", "list-markets"],
5780
+ followUps: ["list-markets"]
5781
+ },
5782
+ // MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
5783
+ annotations: {
5784
+ readOnlyHint: false,
5785
+ destructiveHint: true,
5786
+ // This is a destructive operation
5787
+ idempotentHint: false,
5788
+ // Deleting twice will fail the second time
5789
+ openWorldHint: true
5790
+ }
5791
+ },
5792
+ handleDeleteMarket
5793
+ );
5794
+ }
5795
+
5415
5796
  // src/tools/delete-page.ts
5416
- import { z as z14 } from "zod";
5417
- var inputSchema12 = z14.object({
5797
+ import { z as z16 } from "zod";
5798
+ var inputSchema14 = z16.object({
5418
5799
  id: pageIdSchema.describe(
5419
5800
  '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.'
5420
5801
  )
5421
5802
  });
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")
5803
+ var outputSchema14 = z16.object({
5804
+ success: z16.boolean().describe("Whether the deletion was successful"),
5805
+ deletedPageId: z16.string().describe("The ID of the deleted page")
5425
5806
  });
5426
5807
  var handleDeletePage = async (context, params) => {
5427
5808
  log.debug(`Deleting page on shop: ${context.shopDomain}`);
@@ -5455,8 +5836,8 @@ function registerDeletePageTool() {
5455
5836
  name: "delete-page",
5456
5837
  title: "Delete Page",
5457
5838
  description: "Permanently delete a page by ID. This action cannot be undone. The page will no longer be accessible on the storefront. Use list-pages to find the page ID first. Consider unpublishing instead of deleting if you may need the content later. **Typical workflow:** get-page \u2192 delete-page. **Prerequisites:** Page ID. **Follow-ups:** None. Returns success confirmation and deleted page ID.",
5458
- inputSchema: inputSchema12,
5459
- outputSchema: outputSchema12,
5839
+ inputSchema: inputSchema14,
5840
+ outputSchema: outputSchema14,
5460
5841
  category: "content",
5461
5842
  relationships: {
5462
5843
  relatedTools: ["get-page", "list-pages"],
@@ -5477,7 +5858,7 @@ function registerDeletePageTool() {
5477
5858
  }
5478
5859
 
5479
5860
  // src/tools/delete-product-image.ts
5480
- import { z as z15 } from "zod";
5861
+ import { z as z17 } from "zod";
5481
5862
  var FILE_DELETE_MUTATION = `
5482
5863
  mutation FileDelete($fileIds: [ID!]!) {
5483
5864
  fileDelete(fileIds: $fileIds) {
@@ -5489,14 +5870,14 @@ var FILE_DELETE_MUTATION = `
5489
5870
  }
5490
5871
  }
5491
5872
  `;
5492
- var inputSchema13 = z15.object({
5873
+ var inputSchema15 = z17.object({
5493
5874
  imageId: imageIdSchema.describe(
5494
5875
  'Shopify image GID to delete (e.g., "gid://shopify/MediaImage/123"). Required. Use get-product to find image IDs in the images array.'
5495
5876
  )
5496
5877
  });
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")
5878
+ var outputSchema15 = z17.object({
5879
+ success: z17.boolean().describe("Whether the deletion was successful"),
5880
+ deletedImageId: z17.string().describe("ID of the deleted image")
5500
5881
  });
5501
5882
  var handleDeleteProductImage = async (context, params) => {
5502
5883
  log.debug(`Deleting image on shop: ${context.shopDomain}`);
@@ -5547,8 +5928,8 @@ function registerDeleteProductImageTool() {
5547
5928
  name: "delete-product-image",
5548
5929
  title: "Delete Product Image",
5549
5930
  description: "Remove an image from a product. This permanently deletes the image from Shopify's CDN. **Warning:** Cannot be undone - the image will need to be re-uploaded using add-product-image if needed again. **Prerequisites:** get-product to find image IDs in the images array.",
5550
- inputSchema: inputSchema13,
5551
- outputSchema: outputSchema13,
5931
+ inputSchema: inputSchema15,
5932
+ outputSchema: outputSchema15,
5552
5933
  // AI Agent Optimization (Story 5.5)
5553
5934
  category: "media",
5554
5935
  relationships: {
@@ -5570,7 +5951,7 @@ function registerDeleteProductImageTool() {
5570
5951
  }
5571
5952
 
5572
5953
  // src/tools/delete-product-metafields.ts
5573
- import { z as z16 } from "zod";
5954
+ import { z as z18 } from "zod";
5574
5955
 
5575
5956
  // src/shopify/metafields.ts
5576
5957
  var GET_PRODUCT_METAFIELDS_QUERY = `
@@ -5749,28 +6130,28 @@ async function deleteProductMetafields(productId, identifiers) {
5749
6130
  }
5750
6131
 
5751
6132
  // src/tools/delete-product-metafields.ts
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.')
6133
+ var metafieldIdentifierSchema = z18.object({
6134
+ namespace: z18.string().min(1).describe('Metafield namespace (e.g., "custom", "seo"). Must match existing metafield.'),
6135
+ key: z18.string().min(1).describe('Metafield key (e.g., "color_hex"). Must match existing metafield.')
5755
6136
  });
5756
- var inputSchema14 = z16.object({
6137
+ var inputSchema16 = z18.object({
5757
6138
  productId: productIdSchema.describe(
5758
6139
  'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use get-product or list-products to find product IDs.'
5759
6140
  ),
5760
- identifiers: z16.array(metafieldIdentifierSchema).min(1, "At least one metafield identifier is required").describe(
6141
+ identifiers: z18.array(metafieldIdentifierSchema).min(1, "At least one metafield identifier is required").describe(
5761
6142
  "Array of metafield identifiers to delete. Each identifier must have namespace and key. Use get-product-metafields first to verify which metafields exist."
5762
6143
  )
5763
6144
  });
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")
6145
+ var outputSchema16 = z18.object({
6146
+ success: z18.boolean().describe("Whether the operation succeeded"),
6147
+ deletedMetafields: z18.array(
6148
+ z18.object({
6149
+ ownerId: z18.string().describe("Product GID that owned the metafield"),
6150
+ namespace: z18.string().describe("Namespace of deleted metafield"),
6151
+ key: z18.string().describe("Key of deleted metafield")
5771
6152
  })
5772
6153
  ).describe("Identifiers of successfully deleted metafields"),
5773
- deletedCount: z16.number().describe("Number of metafields deleted")
6154
+ deletedCount: z18.number().describe("Number of metafields deleted")
5774
6155
  });
5775
6156
  var handleDeleteProductMetafields = async (context, params) => {
5776
6157
  log.debug(
@@ -5800,8 +6181,8 @@ function registerDeleteProductMetafieldsTool() {
5800
6181
  name: "delete-product-metafields",
5801
6182
  title: "Delete Product Metafields",
5802
6183
  description: "Remove metafields from a product. Identify metafields to delete by their namespace and key combination. Non-existent metafields are silently ignored (no error). **Warning:** This operation is irreversible - deleted metafield data cannot be recovered. **Prerequisites:** get-product-metafields to verify which metafields exist before deletion.",
5803
- inputSchema: inputSchema14,
5804
- outputSchema: outputSchema14,
6184
+ inputSchema: inputSchema16,
6185
+ outputSchema: outputSchema16,
5805
6186
  // AI Agent Optimization (Story 5.5)
5806
6187
  category: "seo",
5807
6188
  relationships: {
@@ -5823,15 +6204,15 @@ function registerDeleteProductMetafieldsTool() {
5823
6204
  }
5824
6205
 
5825
6206
  // src/tools/delete-product.ts
5826
- import { z as z17 } from "zod";
5827
- var inputSchema15 = z17.object({
6207
+ import { z as z19 } from "zod";
6208
+ var inputSchema17 = z19.object({
5828
6209
  id: productIdSchema.describe(
5829
6210
  'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Required.'
5830
6211
  )
5831
6212
  });
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)")
6213
+ var outputSchema17 = z19.object({
6214
+ deletedProductId: z19.string().describe("The ID of the deleted product"),
6215
+ title: z19.string().describe("The title of the deleted product (for confirmation)")
5835
6216
  });
5836
6217
  var handleDeleteProduct = async (context, params) => {
5837
6218
  log.debug(`Deleting product on shop: ${context.shopDomain}`);
@@ -5873,8 +6254,8 @@ function registerDeleteProductTool() {
5873
6254
  name: "delete-product",
5874
6255
  title: "Delete Product",
5875
6256
  description: "Delete a product from the Shopify store permanently. Requires the product ID (GID format). Returns confirmation with the deleted product ID and title. **Prerequisites:** get-product or list-products to find the correct product ID. **Warning:** This action cannot be undone.",
5876
- inputSchema: inputSchema15,
5877
- outputSchema: outputSchema15,
6257
+ inputSchema: inputSchema17,
6258
+ outputSchema: outputSchema17,
5878
6259
  // AI Agent Optimization (Story 5.5)
5879
6260
  category: "product",
5880
6261
  relationships: {
@@ -5896,15 +6277,15 @@ function registerDeleteProductTool() {
5896
6277
  }
5897
6278
 
5898
6279
  // src/tools/delete-redirect.ts
5899
- import { z as z18 } from "zod";
5900
- var inputSchema16 = z18.object({
6280
+ import { z as z20 } from "zod";
6281
+ var inputSchema18 = z20.object({
5901
6282
  id: redirectIdSchema.describe(
5902
6283
  'Shopify redirect GID (e.g., "gid://shopify/UrlRedirect/123"). Use list-redirects to find redirect IDs.'
5903
6284
  )
5904
6285
  });
5905
- var outputSchema16 = z18.object({
5906
- success: z18.boolean().describe("Whether the deletion succeeded"),
5907
- deletedRedirectId: z18.string().describe("ID of the deleted redirect")
6286
+ var outputSchema18 = z20.object({
6287
+ success: z20.boolean().describe("Whether the deletion succeeded"),
6288
+ deletedRedirectId: z20.string().describe("ID of the deleted redirect")
5908
6289
  });
5909
6290
  var handleDeleteRedirect = async (context, params) => {
5910
6291
  log.debug(`Deleting redirect on shop: ${context.shopDomain}`);
@@ -5946,8 +6327,8 @@ function registerDeleteRedirectTool() {
5946
6327
  name: "delete-redirect",
5947
6328
  title: "Delete URL Redirect",
5948
6329
  description: "Remove a URL redirect by ID. **Warning:** Deleting means visitors to that path will see 404 error - can hurt SEO if old URL still receives traffic. **Prerequisites:** list-redirects to find redirect IDs.",
5949
- inputSchema: inputSchema16,
5950
- outputSchema: outputSchema16,
6330
+ inputSchema: inputSchema18,
6331
+ outputSchema: outputSchema18,
5951
6332
  // AI Agent Optimization (Story 5.5)
5952
6333
  category: "seo",
5953
6334
  relationships: {
@@ -5969,7 +6350,7 @@ function registerDeleteRedirectTool() {
5969
6350
  }
5970
6351
 
5971
6352
  // src/tools/get-bulk-inventory.ts
5972
- import { z as z19 } from "zod";
6353
+ import { z as z21 } from "zod";
5973
6354
 
5974
6355
  // src/shopify/inventory.ts
5975
6356
  var GET_INVENTORY_ITEM_QUERY = `
@@ -6540,46 +6921,46 @@ async function getBulkInventory(options) {
6540
6921
  }
6541
6922
 
6542
6923
  // src/tools/get-bulk-inventory.ts
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(
6924
+ var inputSchema19 = z21.object({
6925
+ productIds: z21.array(productIdSchema).min(1, { message: "productIds array cannot be empty" }).max(50, { message: "Maximum 50 product IDs allowed per request" }).describe(
6545
6926
  '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.'
6546
6927
  ),
6547
- includeVariants: z19.boolean().default(true).describe(
6928
+ includeVariants: z21.boolean().default(true).describe(
6548
6929
  "Include per-variant inventory breakdown. Default: true. Set to false for faster response with only product totals."
6549
6930
  ),
6550
6931
  locationId: locationIdSchema.optional().describe(
6551
6932
  'Optional location ID to filter inventory (e.g., "gid://shopify/Location/789"). If not provided, returns total inventory across all locations.'
6552
6933
  )
6553
6934
  });
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")
6935
+ var outputSchema19 = z21.object({
6936
+ products: z21.array(
6937
+ z21.object({
6938
+ productId: z21.string().describe("Product GID"),
6939
+ productTitle: z21.string().describe("Product title"),
6940
+ totalInventory: z21.number().describe("Total inventory across all variants"),
6941
+ variants: z21.array(
6942
+ z21.object({
6943
+ variantId: z21.string().describe("Variant GID"),
6944
+ variantTitle: z21.string().describe('Variant title (e.g., "Red / Large")'),
6945
+ sku: z21.string().nullable().describe("SKU (Stock Keeping Unit)"),
6946
+ inventoryItemId: z21.string().describe("Inventory item GID"),
6947
+ available: z21.number().describe("Available quantity"),
6948
+ locations: z21.array(
6949
+ z21.object({
6950
+ locationId: z21.string().describe("Location GID"),
6951
+ locationName: z21.string().describe("Human-readable location name"),
6952
+ available: z21.number().describe("Available quantity at this location")
6572
6953
  })
6573
6954
  ).optional().describe("Per-location inventory breakdown")
6574
6955
  })
6575
6956
  ).optional().describe("Variant details (if includeVariants=true)")
6576
6957
  })
6577
6958
  ).describe("Products with inventory data"),
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")
6959
+ notFound: z21.array(z21.string()).describe("Product IDs that were not found in the store"),
6960
+ summary: z21.object({
6961
+ productsQueried: z21.number().describe("Total number of product IDs provided"),
6962
+ productsFound: z21.number().describe("Number of products found in the store"),
6963
+ totalInventory: z21.number().describe("Sum of inventory across all found products")
6583
6964
  }).describe("Summary statistics")
6584
6965
  });
6585
6966
  var handleGetBulkInventory = async (context, params) => {
@@ -6611,8 +6992,8 @@ function registerGetBulkInventoryTool() {
6611
6992
  name: "get-bulk-inventory",
6612
6993
  title: "Get Bulk Inventory",
6613
6994
  description: "Efficiently query inventory levels for multiple products in a single request. Accepts up to 50 product IDs and returns total inventory plus optional per-variant breakdown. Use this instead of multiple get-inventory calls when checking stock across several products. Returns a summary with counts of products queried vs found, plus total inventory across all products. Products not found in the store are listed in the notFound array rather than causing an error. Perfect for cart verification, collection audits, and batch inventory checks. **Typical workflow:** list-products \u2192 get-bulk-inventory. **Prerequisites:** Product IDs. **Follow-ups:** update-inventory, get-inventory. Returns products with inventory data and summary statistics.",
6614
- inputSchema: inputSchema17,
6615
- outputSchema: outputSchema17,
6995
+ inputSchema: inputSchema19,
6996
+ outputSchema: outputSchema19,
6616
6997
  category: "inventory",
6617
6998
  relationships: {
6618
6999
  relatedTools: ["list-products", "get-inventory", "update-inventory"],
@@ -6633,30 +7014,30 @@ function registerGetBulkInventoryTool() {
6633
7014
  }
6634
7015
 
6635
7016
  // src/tools/get-collection.ts
6636
- import { z as z20 } from "zod";
6637
- var inputSchema18 = z20.object({
7017
+ import { z as z22 } from "zod";
7018
+ var inputSchema20 = z22.object({
6638
7019
  collectionId: collectionIdSchema.describe(
6639
7020
  'Collection ID (GID format, e.g., "gid://shopify/Collection/123"). Use list-collections to find collection IDs.'
6640
7021
  )
6641
7022
  });
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")
7023
+ var outputSchema20 = z22.object({
7024
+ id: z22.string().describe("Collection GID"),
7025
+ title: z22.string().describe("Collection title"),
7026
+ handle: z22.string().describe("URL handle/slug"),
7027
+ description: z22.string().describe("Plain text description"),
7028
+ descriptionHtml: z22.string().describe("HTML description"),
7029
+ seo: z22.object({
7030
+ title: z22.string().nullable().describe("SEO title"),
7031
+ description: z22.string().nullable().describe("SEO description")
6651
7032
  }),
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")
7033
+ image: z22.object({
7034
+ id: z22.string().describe("Image GID"),
7035
+ url: z22.string().describe("Image URL"),
7036
+ altText: z22.string().nullable().describe("Alt text")
6656
7037
  }).nullable(),
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)")
7038
+ sortOrder: z22.string().describe("Product sort order within collection"),
7039
+ productsCount: z22.number().describe("Number of products in collection"),
7040
+ updatedAt: z22.string().describe("Last update timestamp (ISO 8601)")
6660
7041
  });
6661
7042
  var handleGetCollection = async (context, params) => {
6662
7043
  log.debug(`Getting collection on shop: ${context.shopDomain}`);
@@ -6676,8 +7057,8 @@ function registerGetCollectionTool() {
6676
7057
  name: "get-collection",
6677
7058
  title: "Get Collection",
6678
7059
  description: "Retrieve complete details of a collection by ID. Returns all collection attributes including SEO metadata, product count, sort order, and timestamps. **Prerequisites:** list-collections to find collection IDs. **Follow-ups:** update-collection, delete-collection, add-products-to-collection.",
6679
- inputSchema: inputSchema18,
6680
- outputSchema: outputSchema18,
7060
+ inputSchema: inputSchema20,
7061
+ outputSchema: outputSchema20,
6681
7062
  // AI Agent Optimization (Story 5.5)
6682
7063
  category: "collection",
6683
7064
  relationships: {
@@ -6697,8 +7078,8 @@ function registerGetCollectionTool() {
6697
7078
  }
6698
7079
 
6699
7080
  // src/tools/get-inventory.ts
6700
- import { z as z21 } from "zod";
6701
- var inputSchema19 = z21.object({
7081
+ import { z as z23 } from "zod";
7082
+ var inputSchema21 = z23.object({
6702
7083
  variantId: variantIdSchema.optional().describe(
6703
7084
  '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).'
6704
7085
  ),
@@ -6711,23 +7092,23 @@ var inputSchema19 = z21.object({
6711
7092
  }).refine((data) => data.variantId || data.inventoryItemId, {
6712
7093
  message: "Either variantId or inventoryItemId must be provided"
6713
7094
  });
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")
7095
+ var outputSchema21 = z23.object({
7096
+ inventoryItemId: z23.string().describe("Inventory item GID"),
7097
+ variantId: z23.string().describe("Product variant GID"),
7098
+ variantTitle: z23.string().describe('Variant title (e.g., "Red / Large")'),
7099
+ productId: z23.string().describe("Product GID"),
7100
+ productTitle: z23.string().describe("Product title"),
7101
+ sku: z23.string().nullable().describe("SKU (Stock Keeping Unit)"),
7102
+ tracked: z23.boolean().describe("Whether inventory tracking is enabled for this item"),
7103
+ totalAvailable: z23.number().describe("Total available quantity across all locations"),
7104
+ locations: z23.array(
7105
+ z23.object({
7106
+ locationId: z23.string().describe("Location GID"),
7107
+ locationName: z23.string().describe("Human-readable location name"),
7108
+ isActive: z23.boolean().describe("Whether the location is active"),
7109
+ available: z23.number().describe("Quantity available for sale"),
7110
+ onHand: z23.number().describe("Physical quantity on hand"),
7111
+ committed: z23.number().describe("Quantity committed to orders")
6731
7112
  })
6732
7113
  ).describe("Inventory levels by location")
6733
7114
  });
@@ -6789,8 +7170,8 @@ function registerGetInventoryTool() {
6789
7170
  name: "get-inventory",
6790
7171
  title: "Get Inventory",
6791
7172
  description: "Retrieve inventory levels for a product variant across all store locations. Provide either a variantId (from get-product or list-products) or inventoryItemId (if you have it directly). Returns available, on-hand, and committed quantities per location, plus totals. Useful for: checking stock before making availability promises, identifying which locations have stock, understanding inventory distribution across warehouse locations. Optionally filter by locationId to get inventory at a specific location only. **Typical workflow:** get-product \u2192 get-inventory \u2192 update-inventory. **Prerequisites:** Variant ID or Inventory Item ID. **Follow-ups:** update-inventory. Returns inventory levels per location with totals.",
6792
- inputSchema: inputSchema19,
6793
- outputSchema: outputSchema19,
7173
+ inputSchema: inputSchema21,
7174
+ outputSchema: outputSchema21,
6794
7175
  category: "inventory",
6795
7176
  relationships: {
6796
7177
  relatedTools: [
@@ -6814,24 +7195,106 @@ function registerGetInventoryTool() {
6814
7195
  );
6815
7196
  }
6816
7197
 
7198
+ // src/tools/get-market.ts
7199
+ import { z as z24 } from "zod";
7200
+ var inputSchema22 = z24.object({
7201
+ id: marketIdSchema.describe(
7202
+ 'Shopify Market GID (e.g., "gid://shopify/Market/123"). Use list-markets to find market IDs.'
7203
+ )
7204
+ });
7205
+ var outputSchema22 = z24.object({
7206
+ id: z24.string().describe("Market GID"),
7207
+ name: z24.string().describe("Market name (not shown to customers)"),
7208
+ handle: z24.string().describe("URL handle"),
7209
+ enabled: z24.boolean().describe("Whether the market is enabled"),
7210
+ status: z24.enum(["ACTIVE", "INACTIVE"]).describe("Market status"),
7211
+ type: z24.string().describe("Market type (PRIMARY, SINGLE_COUNTRY, MULTI_COUNTRY, CONTINENT)"),
7212
+ currencySettings: z24.object({
7213
+ baseCurrency: z24.object({
7214
+ currencyCode: z24.string().describe("Base currency code (e.g., USD, EUR)"),
7215
+ currencyName: z24.string().optional().describe("Base currency name")
7216
+ }),
7217
+ localCurrencies: z24.boolean().describe("Whether local currencies are enabled")
7218
+ }).optional(),
7219
+ webPresences: z24.array(
7220
+ z24.object({
7221
+ id: z24.string().describe("Web presence GID"),
7222
+ defaultLocale: z24.object({
7223
+ locale: z24.string().describe("Locale code (e.g., en-CA)"),
7224
+ name: z24.string().describe("Locale display name")
7225
+ }),
7226
+ alternateLocales: z24.array(
7227
+ z24.object({
7228
+ locale: z24.string(),
7229
+ name: z24.string()
7230
+ })
7231
+ ).optional(),
7232
+ subfolderSuffix: z24.string().optional().describe("Subfolder suffix (e.g., /en-ca)"),
7233
+ domain: z24.object({
7234
+ host: z24.string().describe("Domain host")
7235
+ }).optional(),
7236
+ rootUrls: z24.array(
7237
+ z24.object({
7238
+ locale: z24.string(),
7239
+ url: z24.string()
7240
+ })
7241
+ ).optional()
7242
+ })
7243
+ ).optional().describe("Web presences (SEO URL configurations)")
7244
+ });
7245
+ var handleGetMarket = async (context, params) => {
7246
+ log.debug(`Getting market ${params.id} on shop: ${context.shopDomain}`);
7247
+ const market = await getMarket(params.id);
7248
+ if (!market) {
7249
+ throw new Error(`Market not found: ${params.id}. Use list-markets to see available markets.`);
7250
+ }
7251
+ log.debug(`Retrieved market: ${market.name}`);
7252
+ return market;
7253
+ };
7254
+ function registerGetMarketTool() {
7255
+ registerContextAwareTool(
7256
+ {
7257
+ name: "get-market",
7258
+ title: "Get Market",
7259
+ description: "Retrieve full details of a specific market by ID. Returns market name, handle, status, type, currency settings, and web presences. Web presences define SEO URL structures (domains, subfolders, locales). **Prerequisites:** Use list-markets to find market IDs. **Follow-ups:** update-market, delete-market, create-web-presence.",
7260
+ inputSchema: inputSchema22,
7261
+ outputSchema: outputSchema22,
7262
+ // AI Agent Optimization
7263
+ category: "market",
7264
+ relationships: {
7265
+ relatedTools: ["list-markets"],
7266
+ followUps: ["update-market", "delete-market"]
7267
+ },
7268
+ // MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
7269
+ annotations: {
7270
+ readOnlyHint: true,
7271
+ destructiveHint: false,
7272
+ idempotentHint: true,
7273
+ openWorldHint: true
7274
+ }
7275
+ },
7276
+ handleGetMarket
7277
+ );
7278
+ }
7279
+
6817
7280
  // src/tools/get-page.ts
6818
- import { z as z22 } from "zod";
6819
- var inputSchema20 = z22.object({
7281
+ import { z as z25 } from "zod";
7282
+ var inputSchema23 = z25.object({
6820
7283
  id: pageIdSchema.describe(
6821
7284
  'The page ID to retrieve. Must be a valid Shopify GID format. Example: "gid://shopify/Page/123456789". Use list-pages to find page IDs.'
6822
7285
  )
6823
7286
  });
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")
7287
+ var outputSchema23 = z25.object({
7288
+ id: z25.string().describe("Page GID"),
7289
+ title: z25.string().describe("Page title"),
7290
+ handle: z25.string().describe("URL handle/slug"),
7291
+ body: z25.string().describe("HTML body content"),
7292
+ bodySummary: z25.string().describe("Plain text summary (first 150 chars)"),
7293
+ isPublished: z25.boolean().describe("Whether page is visible on storefront"),
7294
+ publishedAt: z25.string().nullable().describe("ISO timestamp when published"),
7295
+ templateSuffix: z25.string().nullable().describe("Custom template suffix"),
7296
+ createdAt: z25.string().describe("ISO timestamp of creation"),
7297
+ updatedAt: z25.string().describe("ISO timestamp of last update")
6835
7298
  });
6836
7299
  var handleGetPage = async (context, params) => {
6837
7300
  log.debug(`Getting page on shop: ${context.shopDomain}`);
@@ -6851,8 +7314,8 @@ function registerGetPageTool() {
6851
7314
  name: "get-page",
6852
7315
  title: "Get Page",
6853
7316
  description: "Retrieve details of a specific page by ID. Returns the full page including title, body content, handle, publish status, and timestamps. Use list-pages first to find page IDs. Useful for: inspecting page content before editing, verifying page details. **Typical workflow:** list-pages \u2192 get-page \u2192 update-page. **Prerequisites:** Page ID. **Follow-ups:** update-page, delete-page. Returns a single page object with full content.",
6854
- inputSchema: inputSchema20,
6855
- outputSchema: outputSchema20,
7317
+ inputSchema: inputSchema23,
7318
+ outputSchema: outputSchema23,
6856
7319
  category: "content",
6857
7320
  relationships: {
6858
7321
  relatedTools: ["list-pages", "update-page", "delete-page"],
@@ -6872,27 +7335,27 @@ function registerGetPageTool() {
6872
7335
  }
6873
7336
 
6874
7337
  // src/tools/get-product-metafields.ts
6875
- import { z as z23 } from "zod";
6876
- var inputSchema21 = z23.object({
7338
+ import { z as z26 } from "zod";
7339
+ var inputSchema24 = z26.object({
6877
7340
  productId: productIdSchema.describe(
6878
7341
  'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use get-product or list-products to find product IDs.'
6879
7342
  ),
6880
- namespace: z23.string().optional().describe(
7343
+ namespace: z26.string().optional().describe(
6881
7344
  'Filter metafields by namespace (e.g., "custom", "seo", "my_app"). If not provided, returns metafields from all namespaces.'
6882
7345
  ),
6883
- keys: z23.array(z23.string()).optional().describe(
7346
+ keys: z26.array(z26.string()).optional().describe(
6884
7347
  'Filter by specific metafield keys within the namespace (e.g., ["color", "size"]). Requires namespace to be specified for meaningful filtering.'
6885
7348
  )
6886
7349
  });
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)")
7350
+ var outputSchema24 = z26.array(
7351
+ z26.object({
7352
+ id: z26.string().describe('Metafield GID (e.g., "gid://shopify/Metafield/123")'),
7353
+ namespace: z26.string().describe("Metafield namespace (grouping identifier)"),
7354
+ key: z26.string().describe("Metafield key (field name)"),
7355
+ value: z26.string().describe("Metafield value (the stored data)"),
7356
+ type: z26.string().describe('Metafield type (e.g., "single_line_text_field", "json", "number_integer")'),
7357
+ createdAt: z26.string().describe("Creation timestamp (ISO 8601)"),
7358
+ updatedAt: z26.string().describe("Last update timestamp (ISO 8601)")
6896
7359
  })
6897
7360
  );
6898
7361
  var handleGetProductMetafields = async (context, params) => {
@@ -6917,8 +7380,8 @@ function registerGetProductMetafieldsTool() {
6917
7380
  name: "get-product-metafields",
6918
7381
  title: "Get Product Metafields",
6919
7382
  description: 'Retrieve metafields attached to a product. Metafields store custom data like SEO schema markup, product specifications, or custom attributes. Filter by namespace to get specific app/integration data, or by keys to retrieve specific fields. Common namespaces include "custom" (user-defined), "seo" (search optimization), and app-specific namespaces. Returns all matching metafields with their id, namespace, key, value, type, and timestamps. **Prerequisites:** get-product to obtain the product ID. **Follow-ups:** set-product-metafields to update values, delete-product-metafields to remove.',
6920
- inputSchema: inputSchema21,
6921
- outputSchema: outputSchema21,
7383
+ inputSchema: inputSchema24,
7384
+ outputSchema: outputSchema24,
6922
7385
  // AI Agent Optimization (Story 5.5)
6923
7386
  category: "seo",
6924
7387
  relationships: {
@@ -6939,51 +7402,51 @@ function registerGetProductMetafieldsTool() {
6939
7402
  }
6940
7403
 
6941
7404
  // src/tools/get-product.ts
6942
- import { z as z24 } from "zod";
6943
- var inputSchema22 = z24.object({
7405
+ import { z as z27 } from "zod";
7406
+ var inputSchema25 = z27.object({
6944
7407
  id: productIdSchema.optional().describe(
6945
7408
  'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use either id or handle.'
6946
7409
  ),
6947
- handle: z24.string().optional().describe(
7410
+ handle: z27.string().optional().describe(
6948
7411
  'Product URL handle/slug (e.g., "premium-cotton-t-shirt"). Use either id or handle.'
6949
7412
  )
6950
7413
  }).refine((data) => data.id || data.handle, {
6951
7414
  message: "Either id or handle must be provided"
6952
7415
  });
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")
7416
+ var outputSchema25 = z27.object({
7417
+ id: z27.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
7418
+ title: z27.string().describe("Product title"),
7419
+ handle: z27.string().describe("URL handle/slug"),
7420
+ description: z27.string().nullable().describe("Product description (HTML)"),
7421
+ vendor: z27.string().describe("Vendor/brand name"),
7422
+ productType: z27.string().describe("Product type"),
7423
+ status: z27.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
7424
+ tags: z27.array(z27.string()).describe("Product tags"),
7425
+ seo: z27.object({
7426
+ title: z27.string().nullable().describe("SEO title for search engines"),
7427
+ description: z27.string().nullable().describe("SEO description/meta description")
6965
7428
  }).describe("SEO metadata for search engine optimization"),
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")
7429
+ createdAt: z27.string().describe("Creation timestamp (ISO 8601)"),
7430
+ updatedAt: z27.string().describe("Last update timestamp (ISO 8601)"),
7431
+ variants: z27.array(
7432
+ z27.object({
7433
+ id: z27.string().describe("Variant GID"),
7434
+ title: z27.string().describe("Variant title"),
7435
+ price: z27.string().describe("Price"),
7436
+ compareAtPrice: z27.string().nullable().describe("Compare at price"),
7437
+ sku: z27.string().nullable().describe("SKU"),
7438
+ barcode: z27.string().nullable().describe("Barcode"),
7439
+ inventoryQuantity: z27.number().nullable().describe("Inventory quantity")
6977
7440
  })
6978
7441
  ).describe("Product variants"),
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")
7442
+ images: z27.array(
7443
+ z27.object({
7444
+ id: z27.string().describe("Image GID"),
7445
+ url: z27.string().describe("Image URL"),
7446
+ altText: z27.string().nullable().describe("Alt text")
6984
7447
  })
6985
7448
  ).describe("Product images"),
6986
- totalInventory: z24.number().describe("Total inventory across variants")
7449
+ totalInventory: z27.number().describe("Total inventory across variants")
6987
7450
  });
6988
7451
  var handleGetProduct = async (context, params) => {
6989
7452
  log.debug(`Getting product on shop: ${context.shopDomain}`);
@@ -7022,8 +7485,8 @@ function registerGetProductTool() {
7022
7485
  name: "get-product",
7023
7486
  title: "Get Product",
7024
7487
  description: "Retrieve details of a specific product from the Shopify store. Provide either the product ID (GID format) or handle (URL slug). Returns full product details including title, description, vendor, type, status, tags, SEO metadata (title/description for search engines), variants (with pricing/SKU/inventory), and images. **Prerequisites:** Use list-products to find product IDs or handles. **Follow-ups:** update-product, delete-product, add-product-image, get-product-metafields.",
7025
- inputSchema: inputSchema22,
7026
- outputSchema: outputSchema22,
7488
+ inputSchema: inputSchema25,
7489
+ outputSchema: outputSchema25,
7027
7490
  // AI Agent Optimization (Story 5.5)
7028
7491
  category: "product",
7029
7492
  relationships: {
@@ -7048,36 +7511,36 @@ function registerGetProductTool() {
7048
7511
  }
7049
7512
 
7050
7513
  // src/tools/list-articles.ts
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(
7514
+ import { z as z28 } from "zod";
7515
+ var inputSchema26 = z28.object({
7516
+ first: z28.number().int().min(1).max(50).optional().default(10).describe("Number of articles to return (1-50). Defaults to 10."),
7517
+ cursor: z28.string().optional().describe(
7055
7518
  "Pagination cursor from a previous response. Use the endCursor from the previous response to get the next page of results."
7056
7519
  ),
7057
- query: z25.string().optional().describe(
7520
+ query: z28.string().optional().describe(
7058
7521
  "Search query to filter articles. Supports Shopify search syntax. Examples: 'title:Guide', 'tag:tutorial', 'blog_id:123456789'."
7059
7522
  ),
7060
- blogId: z25.string().optional().describe(
7523
+ blogId: z28.string().optional().describe(
7061
7524
  '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".'
7062
7525
  ),
7063
- sortKey: z25.enum(["ID", "TITLE", "UPDATED_AT", "PUBLISHED_AT", "AUTHOR", "BLOG_TITLE"]).optional().describe("Sort order for results. Defaults to ID.")
7526
+ sortKey: z28.enum(["ID", "TITLE", "UPDATED_AT", "PUBLISHED_AT", "AUTHOR", "BLOG_TITLE"]).optional().describe("Sort order for results. Defaults to ID.")
7064
7527
  });
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")
7528
+ var outputSchema26 = z28.object({
7529
+ articles: z28.array(
7530
+ z28.object({
7531
+ id: z28.string().describe("Article GID"),
7532
+ title: z28.string().describe("Article title"),
7533
+ handle: z28.string().describe("URL handle/slug"),
7534
+ blog: z28.object({
7535
+ id: z28.string().describe("Parent blog GID"),
7536
+ title: z28.string().describe("Parent blog title")
7074
7537
  }),
7075
- isPublished: z25.boolean().describe("Whether article is visible on storefront"),
7076
- publishedAt: z25.string().nullable().describe("ISO timestamp when published")
7538
+ isPublished: z28.boolean().describe("Whether article is visible on storefront"),
7539
+ publishedAt: z28.string().nullable().describe("ISO timestamp when published")
7077
7540
  })
7078
7541
  ),
7079
- hasNextPage: z25.boolean().describe("Whether more articles are available"),
7080
- endCursor: z25.string().nullable().describe("Cursor for next page pagination")
7542
+ hasNextPage: z28.boolean().describe("Whether more articles are available"),
7543
+ endCursor: z28.string().nullable().describe("Cursor for next page pagination")
7081
7544
  });
7082
7545
  var handleListArticles = async (context, params) => {
7083
7546
  log.debug(`Listing articles on shop: ${context.shopDomain}`);
@@ -7121,8 +7584,8 @@ function registerListArticlesTool() {
7121
7584
  name: "list-articles",
7122
7585
  title: "List Articles",
7123
7586
  description: "List articles with optional filtering and pagination. Use blogId to filter articles from a specific blog. Use query to search by title or tags. Returns article ID, title, handle, blog reference, and publish status. Useful for: finding articles to edit, auditing blog content, bulk operations. **Typical workflow:** list-blogs \u2192 list-articles \u2192 update-article. **Prerequisites:** None (optional Blog ID for filtering). **Follow-ups:** update-article, delete-article, create-article. Returns a paginated list of article summaries with a response summary.",
7124
- inputSchema: inputSchema23,
7125
- outputSchema: outputSchema23,
7587
+ inputSchema: inputSchema26,
7588
+ outputSchema: outputSchema26,
7126
7589
  category: "content",
7127
7590
  relationships: {
7128
7591
  relatedTools: ["list-blogs", "create-article", "update-article"],
@@ -7141,29 +7604,29 @@ function registerListArticlesTool() {
7141
7604
  }
7142
7605
 
7143
7606
  // src/tools/list-blogs.ts
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(
7607
+ import { z as z29 } from "zod";
7608
+ var inputSchema27 = z29.object({
7609
+ first: z29.number().int().min(1).max(50).optional().default(10).describe("Number of blogs to return (1-50). Defaults to 10."),
7610
+ cursor: z29.string().optional().describe(
7148
7611
  "Pagination cursor from a previous response. Use the endCursor from the previous response to get the next page of results."
7149
7612
  ),
7150
- query: z26.string().optional().describe(
7613
+ query: z29.string().optional().describe(
7151
7614
  "Search query to filter blogs. Supports Shopify search syntax. Examples: 'title:News', 'handle:company-blog'."
7152
7615
  ),
7153
- sortKey: z26.enum(["ID", "TITLE", "UPDATED_AT"]).optional().describe("Sort order for results. Defaults to ID.")
7616
+ sortKey: z29.enum(["ID", "TITLE", "UPDATED_AT"]).optional().describe("Sort order for results. Defaults to ID.")
7154
7617
  });
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")
7618
+ var outputSchema27 = z29.object({
7619
+ blogs: z29.array(
7620
+ z29.object({
7621
+ id: z29.string().describe("Blog GID"),
7622
+ title: z29.string().describe("Blog title"),
7623
+ handle: z29.string().describe("URL handle/slug"),
7624
+ commentPolicy: z29.enum(["CLOSED", "MODERATE", "OPEN"]).describe("Comment moderation policy"),
7625
+ articlesCount: z29.number().describe("Number of articles in the blog")
7163
7626
  })
7164
7627
  ),
7165
- hasNextPage: z26.boolean().describe("Whether more blogs are available"),
7166
- endCursor: z26.string().nullable().describe("Cursor for next page pagination")
7628
+ hasNextPage: z29.boolean().describe("Whether more blogs are available"),
7629
+ endCursor: z29.string().nullable().describe("Cursor for next page pagination")
7167
7630
  });
7168
7631
  var handleListBlogs = async (context, params) => {
7169
7632
  log.debug(`Listing blogs on shop: ${context.shopDomain}`);
@@ -7201,8 +7664,8 @@ function registerListBlogsTool() {
7201
7664
  name: "list-blogs",
7202
7665
  title: "List Blogs",
7203
7666
  description: "List all blogs with optional filtering and pagination. Use the query parameter to search blogs by title or handle. Returns blog ID, title, handle, and article count. Useful for: finding blogs to manage, auditing content, selecting a blog for new articles. **Typical workflow:** list-blogs \u2192 list-articles. **Prerequisites:** None. **Follow-ups:** create-article, update-blog, create-blog. Returns a paginated list of blog summaries with a response summary.",
7204
- inputSchema: inputSchema24,
7205
- outputSchema: outputSchema24,
7667
+ inputSchema: inputSchema27,
7668
+ outputSchema: outputSchema27,
7206
7669
  category: "content",
7207
7670
  relationships: {
7208
7671
  relatedTools: ["create-blog", "update-blog", "list-articles"],
@@ -7221,40 +7684,40 @@ function registerListBlogsTool() {
7221
7684
  }
7222
7685
 
7223
7686
  // src/tools/list-collections.ts
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(
7687
+ import { z as z30 } from "zod";
7688
+ var inputSchema28 = z30.object({
7689
+ first: z30.number().int().min(1).max(50).optional().default(10).describe("Number of collections to return (1-50). Default: 10"),
7690
+ after: z30.string().optional().describe(
7228
7691
  "Pagination cursor from previous response. Use the endCursor value from the previous page to get the next page of results."
7229
7692
  ),
7230
- query: z27.string().optional().describe(
7693
+ query: z30.string().optional().describe(
7231
7694
  'Search query to filter collections. Searches across collection titles and handles. Example: "summer" finds collections with "summer" in the title or handle.'
7232
7695
  ),
7233
- sortKey: z27.enum(["TITLE", "UPDATED_AT", "ID", "RELEVANCE"]).optional().describe(
7696
+ sortKey: z30.enum(["TITLE", "UPDATED_AT", "ID", "RELEVANCE"]).optional().describe(
7234
7697
  "Sort collections by: TITLE (alphabetical), UPDATED_AT (most recently updated first), ID (by collection ID), or RELEVANCE (best match when using query filter)"
7235
7698
  )
7236
7699
  });
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")
7700
+ var outputSchema28 = z30.object({
7701
+ collections: z30.array(
7702
+ z30.object({
7703
+ id: z30.string().describe("Collection GID"),
7704
+ title: z30.string().describe("Collection title"),
7705
+ handle: z30.string().describe("URL handle"),
7706
+ seo: z30.object({
7707
+ title: z30.string().nullable().describe("SEO title"),
7708
+ description: z30.string().nullable().describe("SEO description")
7246
7709
  }),
7247
- productsCount: z27.number().describe("Number of products in collection")
7710
+ productsCount: z30.number().describe("Number of products in collection")
7248
7711
  })
7249
7712
  ),
7250
- pageInfo: z27.object({
7251
- hasNextPage: z27.boolean().describe("Whether more pages exist"),
7252
- endCursor: z27.string().nullable().describe("Cursor for next page")
7713
+ pageInfo: z30.object({
7714
+ hasNextPage: z30.boolean().describe("Whether more pages exist"),
7715
+ endCursor: z30.string().nullable().describe("Cursor for next page")
7253
7716
  }),
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")
7717
+ summary: z30.object({
7718
+ totalReturned: z30.number().describe("Number of collections in this response"),
7719
+ hasMore: z30.boolean().describe("Whether more collections are available"),
7720
+ hint: z30.string().describe("Suggestion for next action")
7258
7721
  }).describe("AI-friendly summary for context management")
7259
7722
  });
7260
7723
  var handleListCollections = async (context, params) => {
@@ -7281,8 +7744,8 @@ function registerListCollectionsTool() {
7281
7744
  name: "list-collections",
7282
7745
  title: "List Collections",
7283
7746
  description: 'List and search collections with pagination and filtering. Use the query parameter for text search across collection titles and handles. Supports sorting by TITLE, UPDATED_AT, ID, or RELEVANCE. Returns up to 50 collections per page. Response includes summary with totalReturned, hasMore, and pagination hint. **Pagination:** Use "after" cursor from pageInfo.endCursor to get next page. **Follow-ups:** get-collection (for full details), update-collection, add-products-to-collection.',
7284
- inputSchema: inputSchema25,
7285
- outputSchema: outputSchema25,
7747
+ inputSchema: inputSchema28,
7748
+ outputSchema: outputSchema28,
7286
7749
  // AI Agent Optimization (Story 5.5)
7287
7750
  category: "collection",
7288
7751
  relationships: {
@@ -7302,47 +7765,47 @@ function registerListCollectionsTool() {
7302
7765
  }
7303
7766
 
7304
7767
  // src/tools/list-low-inventory.ts
7305
- import { z as z28 } from "zod";
7306
- var inputSchema26 = z28.object({
7307
- threshold: z28.number().int().min(0).default(10).describe(
7768
+ import { z as z31 } from "zod";
7769
+ var inputSchema29 = z31.object({
7770
+ threshold: z31.number().int().min(0).default(10).describe(
7308
7771
  "Products with total inventory at or below this number will be included. Default: 10. Use 0 to find only out-of-stock products."
7309
7772
  ),
7310
- includeZero: z28.boolean().default(true).describe(
7773
+ includeZero: z31.boolean().default(true).describe(
7311
7774
  "Include products with zero inventory. Default: true. Set to false to see only low-stock (not out-of-stock) products."
7312
7775
  ),
7313
- status: z28.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe(
7776
+ status: z31.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe(
7314
7777
  "Filter by product status. Default: ACTIVE. Use DRAFT to check pre-launch products, ARCHIVED for historical."
7315
7778
  ),
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.")
7779
+ first: z31.number().int().min(1).max(100).default(50).describe("Number of products to return per page. Default: 50, max: 100."),
7780
+ after: z31.string().optional().describe("Pagination cursor from previous response. Use pageInfo.endCursor to get next page.")
7318
7781
  });
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")
7782
+ var outputSchema29 = z31.object({
7783
+ products: z31.array(
7784
+ z31.object({
7785
+ productId: z31.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
7786
+ productTitle: z31.string().describe("Product title"),
7787
+ status: z31.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
7788
+ totalInventory: z31.number().describe("Total inventory across all variants"),
7789
+ isOutOfStock: z31.boolean().describe("True if total inventory is zero"),
7790
+ variants: z31.array(
7791
+ z31.object({
7792
+ variantId: z31.string().describe("Variant GID"),
7793
+ variantTitle: z31.string().describe('Variant title (e.g., "Red / Large")'),
7794
+ sku: z31.string().nullable().describe("SKU (Stock Keeping Unit)"),
7795
+ inventoryItemId: z31.string().describe("Inventory item GID"),
7796
+ available: z31.number().describe("Available quantity for sale")
7334
7797
  })
7335
7798
  ).describe("Variant breakdown with inventory")
7336
7799
  })
7337
7800
  ).describe("Products with low inventory, sorted by total inventory ascending"),
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')
7801
+ pageInfo: z31.object({
7802
+ hasNextPage: z31.boolean().describe("Whether more products exist after this page"),
7803
+ endCursor: z31.string().nullable().describe('Cursor for the last item - use as "after" to get next page')
7341
7804
  }).describe("Pagination information"),
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")
7805
+ summary: z31.object({
7806
+ totalProducts: z31.number().describe("Number of products in this response"),
7807
+ outOfStockCount: z31.number().describe("Number of products with zero inventory"),
7808
+ lowStockCount: z31.number().describe("Number of products with inventory > 0 but below threshold")
7346
7809
  }).describe("Summary statistics")
7347
7810
  });
7348
7811
  var handleListLowInventory = async (context, params) => {
@@ -7366,8 +7829,8 @@ function registerListLowInventoryTool() {
7366
7829
  name: "list-low-inventory",
7367
7830
  title: "List Low Inventory",
7368
7831
  description: 'Find products with low or zero inventory that may need restocking. Returns products sorted by inventory level (lowest first). Use threshold parameter to customize what counts as "low stock" (default: 10 units). Includes variant breakdown and summary statistics showing how many products are out-of-stock vs. low-stock. Perfect for daily inventory checks, restocking alerts, and identifying products at risk of stockout. Use includeZero=false to exclude completely sold-out items. Use status=DRAFT to check pre-launch products before publishing. **Typical workflow:** list-low-inventory \u2192 get-inventory \u2192 update-inventory. **Prerequisites:** None. **Follow-ups:** get-inventory, update-inventory. Returns paginated products with inventory levels and summary statistics.',
7369
- inputSchema: inputSchema26,
7370
- outputSchema: outputSchema26,
7832
+ inputSchema: inputSchema29,
7833
+ outputSchema: outputSchema29,
7371
7834
  category: "inventory",
7372
7835
  relationships: {
7373
7836
  relatedTools: ["get-inventory", "update-inventory", "get-bulk-inventory"],
@@ -7385,32 +7848,106 @@ function registerListLowInventoryTool() {
7385
7848
  );
7386
7849
  }
7387
7850
 
7851
+ // src/tools/list-markets.ts
7852
+ import { z as z32 } from "zod";
7853
+ var inputSchema30 = z32.object({
7854
+ first: z32.number().int().min(1).max(50).optional().default(10).describe("Number of markets to return (1-50). Default: 10"),
7855
+ after: z32.string().optional().describe(
7856
+ "Pagination cursor from previous response. Use the endCursor value from the previous page to get the next page of results."
7857
+ )
7858
+ });
7859
+ var outputSchema30 = z32.object({
7860
+ markets: z32.array(
7861
+ z32.object({
7862
+ id: z32.string().describe("Market GID"),
7863
+ name: z32.string().describe("Market name (not shown to customers)"),
7864
+ handle: z32.string().describe("URL handle"),
7865
+ enabled: z32.boolean().describe("Whether the market is enabled"),
7866
+ status: z32.enum(["ACTIVE", "INACTIVE"]).describe("Market status"),
7867
+ type: z32.string().describe("Market type (PRIMARY, SINGLE_COUNTRY, MULTI_COUNTRY, CONTINENT)"),
7868
+ currencySettings: z32.object({
7869
+ baseCurrency: z32.object({
7870
+ currencyCode: z32.string().describe("Base currency code (e.g., USD, EUR)")
7871
+ }),
7872
+ localCurrencies: z32.boolean().describe("Whether local currencies are enabled")
7873
+ }).optional()
7874
+ })
7875
+ ),
7876
+ pageInfo: z32.object({
7877
+ hasNextPage: z32.boolean().describe("Whether more pages exist"),
7878
+ endCursor: z32.string().nullable().describe("Cursor for next page")
7879
+ }),
7880
+ summary: z32.object({
7881
+ totalReturned: z32.number().describe("Number of markets in this response"),
7882
+ hasMore: z32.boolean().describe("Whether more markets are available"),
7883
+ hint: z32.string().describe("Suggestion for next action")
7884
+ }).describe("AI-friendly summary for context management")
7885
+ });
7886
+ var handleListMarkets = async (context, params) => {
7887
+ log.debug(`Listing markets on shop: ${context.shopDomain}`);
7888
+ const result = await listMarkets(params.first, params.after);
7889
+ log.debug(`Listed ${result.markets.length} markets via tool`);
7890
+ return {
7891
+ ...result,
7892
+ summary: {
7893
+ totalReturned: result.markets.length,
7894
+ hasMore: result.pageInfo.hasNextPage,
7895
+ hint: result.pageInfo.hasNextPage ? `Use "after" parameter with value "${result.pageInfo.endCursor}" to fetch next page` : "All markets returned"
7896
+ }
7897
+ };
7898
+ };
7899
+ function registerListMarketsTool() {
7900
+ registerContextAwareTool(
7901
+ {
7902
+ name: "list-markets",
7903
+ title: "List Markets",
7904
+ description: 'List all configured markets in the Shopify store with pagination. Markets enable regional targeting for international stores. Returns market id, name, handle, status, type, and currency settings. Response includes summary with totalReturned, hasMore, and pagination hint. **Pagination:** Use "after" cursor from pageInfo.endCursor to get next page. **Prerequisites:** Store must have Markets feature enabled. **Follow-ups:** get-market (for full details including web presences), create-market.',
7905
+ inputSchema: inputSchema30,
7906
+ outputSchema: outputSchema30,
7907
+ // AI Agent Optimization
7908
+ category: "market",
7909
+ relationships: {
7910
+ relatedTools: ["get-market"],
7911
+ followUps: ["get-market", "create-market", "update-market"]
7912
+ },
7913
+ // MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
7914
+ annotations: {
7915
+ readOnlyHint: true,
7916
+ destructiveHint: false,
7917
+ idempotentHint: true,
7918
+ openWorldHint: true
7919
+ }
7920
+ },
7921
+ handleListMarkets
7922
+ );
7923
+ }
7924
+
7388
7925
  // src/tools/list-pages.ts
7389
- import { z as z29 } from "zod";
7390
- var inputSchema27 = z29.object({
7391
- first: z29.number().int().min(1).max(50).optional().describe(
7926
+ import { z as z33 } from "zod";
7927
+ var inputSchema31 = z33.object({
7928
+ first: z33.number().int().min(1).max(50).optional().describe(
7392
7929
  "Number of pages to return per request. Default: 10, Maximum: 50. Use pagination (cursor) to retrieve more results."
7393
7930
  ),
7394
- cursor: z29.string().optional().describe(
7931
+ cursor: z33.string().optional().describe(
7395
7932
  "Pagination cursor from a previous list-pages response (endCursor). Use this to get the next page of results."
7396
7933
  ),
7397
- query: z29.string().optional().describe(
7934
+ query: z33.string().optional().describe(
7398
7935
  "Search query to filter pages. Supports Shopify search syntax. Examples: 'title:About', 'handle:contact', 'created_at:>2024-01-01'."
7399
7936
  ),
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.")
7937
+ sortKey: z33.enum(["ID", "TITLE", "UPDATED_AT", "PUBLISHED_AT"]).optional().describe("Field to sort pages by. Options: ID (default), TITLE, UPDATED_AT, PUBLISHED_AT.")
7401
7938
  });
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")
7939
+ var outputSchema31 = z33.object({
7940
+ pages: z33.array(
7941
+ z33.object({
7942
+ id: z33.string().describe("Page GID"),
7943
+ title: z33.string().describe("Page title"),
7944
+ handle: z33.string().describe("URL handle/slug"),
7945
+ isPublished: z33.boolean().describe("Whether page is visible"),
7946
+ updatedAt: z33.string().describe("ISO timestamp of last update")
7410
7947
  })
7411
7948
  ),
7412
- hasNextPage: z29.boolean().describe("Whether more pages exist"),
7413
- endCursor: z29.string().nullable().describe("Cursor for next page of results")
7949
+ hasNextPage: z33.boolean().describe("Whether more pages exist"),
7950
+ endCursor: z33.string().nullable().describe("Cursor for next page of results")
7414
7951
  });
7415
7952
  var handleListPages = async (context, params) => {
7416
7953
  log.debug(`Listing pages on shop: ${context.shopDomain}`);
@@ -7436,8 +7973,8 @@ function registerListPagesTool() {
7436
7973
  name: "list-pages",
7437
7974
  title: "List Pages",
7438
7975
  description: "List all pages with optional filtering and pagination. Use the query parameter to search pages by title or handle. Returns page ID, title, handle, and publish status. Use pagination (cursor) for stores with many pages. Useful for: finding pages to edit, auditing content, bulk operations. **Typical workflow:** list-pages \u2192 get-page. **Prerequisites:** None. **Follow-ups:** get-page, update-page, create-page. Returns a paginated list of page summaries with a response summary.",
7439
- inputSchema: inputSchema27,
7440
- outputSchema: outputSchema27,
7976
+ inputSchema: inputSchema31,
7977
+ outputSchema: outputSchema31,
7441
7978
  category: "content",
7442
7979
  relationships: {
7443
7980
  relatedTools: ["get-page", "create-page"],
@@ -7456,46 +7993,46 @@ function registerListPagesTool() {
7456
7993
  }
7457
7994
 
7458
7995
  // src/tools/list-products.ts
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(
7996
+ import { z as z34 } from "zod";
7997
+ var inputSchema32 = z34.object({
7998
+ first: z34.number().int().min(1).max(50).optional().describe("Number of products to return (1-50). Default: 10"),
7999
+ after: z34.string().optional().describe("Pagination cursor from previous response's endCursor. Omit for first page."),
8000
+ query: z34.string().optional().describe(
7464
8001
  'Search query for title, description, tags. Example: "summer dress", "organic cotton"'
7465
8002
  ),
7466
- status: z30.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe(
8003
+ status: z34.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe(
7467
8004
  "Filter by product status. ACTIVE = published, DRAFT = not published, ARCHIVED = hidden"
7468
8005
  ),
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")
8006
+ vendor: z34.string().optional().describe('Filter by exact vendor name. Example: "Acme Corp", "Nike"'),
8007
+ productType: z34.string().optional().describe('Filter by exact product type. Example: "T-Shirts", "Shoes"'),
8008
+ sortBy: z34.enum(["TITLE", "CREATED_AT", "UPDATED_AT", "INVENTORY_TOTAL"]).optional().describe("Sort field. TITLE, CREATED_AT (default), UPDATED_AT, or INVENTORY_TOTAL"),
8009
+ sortOrder: z34.enum(["ASC", "DESC"]).optional().describe("Sort direction. ASC (default) for ascending, DESC for descending")
7473
8010
  });
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")
8011
+ var outputSchema32 = z34.object({
8012
+ products: z34.array(
8013
+ z34.object({
8014
+ id: z34.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
8015
+ title: z34.string().describe("Product title"),
8016
+ handle: z34.string().describe("URL handle/slug"),
8017
+ status: z34.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
8018
+ vendor: z34.string().describe("Vendor/brand name"),
8019
+ productType: z34.string().describe("Product type"),
8020
+ totalInventory: z34.number().describe("Total inventory across all variants"),
8021
+ primaryVariantPrice: z34.string().describe('Price of the first variant (e.g., "29.99")'),
8022
+ imageUrl: z34.string().nullable().describe("First image URL or null if no images")
7486
8023
  })
7487
8024
  ).describe("Array of product summaries"),
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')
8025
+ pageInfo: z34.object({
8026
+ hasNextPage: z34.boolean().describe("Whether more products exist after this page"),
8027
+ hasPreviousPage: z34.boolean().describe("Whether products exist before this page"),
8028
+ startCursor: z34.string().nullable().describe("Cursor for the first item in this page"),
8029
+ endCursor: z34.string().nullable().describe('Cursor for the last item - use as "after" to get next page')
7493
8030
  }).describe("Pagination information"),
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")
8031
+ totalCount: z34.number().describe("Number of products returned in this page"),
8032
+ summary: z34.object({
8033
+ totalReturned: z34.number().describe("Number of products in this response"),
8034
+ hasMore: z34.boolean().describe("Whether more products are available"),
8035
+ hint: z34.string().describe("Suggestion for next action")
7499
8036
  }).describe("AI-friendly summary for context management")
7500
8037
  });
7501
8038
  var handleListProducts = async (context, params) => {
@@ -7529,8 +8066,8 @@ function registerListProductsTool() {
7529
8066
  name: "list-products",
7530
8067
  title: "List Products",
7531
8068
  description: 'List products from the Shopify store with pagination and filtering. Returns product summaries including title, status, vendor, type, price, and inventory. Supports filtering by status (ACTIVE/DRAFT/ARCHIVED), vendor, productType, and search query. Sort by TITLE, CREATED_AT, UPDATED_AT, or INVENTORY_TOTAL. Response includes summary with totalReturned, hasMore, and pagination hint. **Pagination:** Use "after" cursor from pageInfo.endCursor to get next page. **Follow-ups:** get-product (for full details), update-product, delete-product.',
7532
- inputSchema: inputSchema28,
7533
- outputSchema: outputSchema28,
8069
+ inputSchema: inputSchema32,
8070
+ outputSchema: outputSchema32,
7534
8071
  // AI Agent Optimization (Story 5.5)
7535
8072
  category: "product",
7536
8073
  relationships: {
@@ -7550,30 +8087,30 @@ function registerListProductsTool() {
7550
8087
  }
7551
8088
 
7552
8089
  // src/tools/list-redirects.ts
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(
8090
+ import { z as z35 } from "zod";
8091
+ var inputSchema33 = z35.object({
8092
+ first: z35.number().int().min(1).max(50).optional().default(10).describe("Number of redirects to return (1-50, default: 10)."),
8093
+ cursor: z35.string().optional().describe(
7557
8094
  "Pagination cursor from previous response (endCursor). Use this to fetch the next page of results."
7558
8095
  ),
7559
- query: z31.string().optional().describe(
8096
+ query: z35.string().optional().describe(
7560
8097
  "Search filter. Supports 'path:' and 'target:' prefixes. Examples: 'path:/old-' to find redirects starting with /old-, 'target:/products/' to find redirects pointing to /products/."
7561
8098
  )
7562
8099
  });
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")
8100
+ var outputSchema33 = z35.object({
8101
+ redirects: z35.array(
8102
+ z35.object({
8103
+ id: z35.string().describe("Redirect GID"),
8104
+ path: z35.string().describe("Source path"),
8105
+ target: z35.string().describe("Target URL")
7569
8106
  })
7570
8107
  ),
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")
8108
+ hasNextPage: z35.boolean().describe("Whether more results exist"),
8109
+ endCursor: z35.string().nullable().describe("Cursor for fetching next page"),
8110
+ summary: z35.object({
8111
+ totalReturned: z35.number().describe("Number of redirects in this response"),
8112
+ hasMore: z35.boolean().describe("Whether more redirects are available"),
8113
+ hint: z35.string().describe("Suggestion for next action")
7577
8114
  }).describe("AI-friendly summary for context management")
7578
8115
  });
7579
8116
  var handleListRedirects = async (context, params) => {
@@ -7611,8 +8148,8 @@ function registerListRedirectsTool() {
7611
8148
  name: "list-redirects",
7612
8149
  title: "List URL Redirects",
7613
8150
  description: `List all URL redirects with optional filtering and pagination. Use query parameter to search by path or target (supports 'path:' and 'target:' prefixes). Response includes summary with totalReturned, hasMore, and pagination hint. **Pagination:** Use "cursor" with endCursor value to get next page. **Follow-ups:** delete-redirect to remove unwanted redirects.`,
7614
- inputSchema: inputSchema29,
7615
- outputSchema: outputSchema29,
8151
+ inputSchema: inputSchema33,
8152
+ outputSchema: outputSchema33,
7616
8153
  // AI Agent Optimization (Story 5.5)
7617
8154
  category: "seo",
7618
8155
  relationships: {
@@ -7632,18 +8169,18 @@ function registerListRedirectsTool() {
7632
8169
  }
7633
8170
 
7634
8171
  // src/tools/remove-products-from-collection.ts
7635
- import { z as z32 } from "zod";
7636
- var inputSchema30 = z32.object({
8172
+ import { z as z36 } from "zod";
8173
+ var inputSchema34 = z36.object({
7637
8174
  collectionId: collectionIdSchema.describe(
7638
8175
  'Collection GID (e.g., "gid://shopify/Collection/123"). Use list-collections to find valid collection IDs.'
7639
8176
  ),
7640
- productIds: z32.array(productIdSchema).min(1).max(250).describe(
8177
+ productIds: z36.array(productIdSchema).min(1).max(250).describe(
7641
8178
  '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.'
7642
8179
  )
7643
8180
  });
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")
8181
+ var outputSchema34 = z36.object({
8182
+ success: z36.boolean().describe("Whether the operation succeeded"),
8183
+ removedCount: z36.number().describe("Number of products removed from the collection")
7647
8184
  });
7648
8185
  var handleRemoveProductsFromCollection = async (context, params) => {
7649
8186
  log.debug(
@@ -7673,8 +8210,8 @@ function registerRemoveProductsFromCollectionTool() {
7673
8210
  name: "remove-products-from-collection",
7674
8211
  title: "Remove Products from Collection",
7675
8212
  description: "Remove products from a collection by their product IDs. This does NOT delete the products - they remain in the catalog but are no longer part of this collection. Maximum 250 products per call. **Prerequisites:** get-collection to verify collection contents before removing.",
7676
- inputSchema: inputSchema30,
7677
- outputSchema: outputSchema30,
8213
+ inputSchema: inputSchema34,
8214
+ outputSchema: outputSchema34,
7678
8215
  // AI Agent Optimization (Story 5.5)
7679
8216
  category: "collection",
7680
8217
  relationships: {
@@ -7696,7 +8233,7 @@ function registerRemoveProductsFromCollectionTool() {
7696
8233
  }
7697
8234
 
7698
8235
  // src/tools/reorder-product-images.ts
7699
- import { z as z33 } from "zod";
8236
+ import { z as z37 } from "zod";
7700
8237
  var PRODUCT_REORDER_MEDIA_MUTATION = `
7701
8238
  mutation ProductReorderMedia($id: ID!, $moves: [MoveInput!]!) {
7702
8239
  productReorderMedia(id: $id, moves: $moves) {
@@ -7711,23 +8248,23 @@ var PRODUCT_REORDER_MEDIA_MUTATION = `
7711
8248
  }
7712
8249
  }
7713
8250
  `;
7714
- var moveSchema = z33.object({
8251
+ var moveSchema = z37.object({
7715
8252
  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.")
8253
+ newPosition: z37.number().int().min(0).describe("Zero-based target position. Position 0 is the featured image.")
7717
8254
  });
7718
- var inputSchema31 = z33.object({
8255
+ var inputSchema35 = z37.object({
7719
8256
  productId: productIdSchema.describe(
7720
8257
  'Shopify product GID (e.g., "gid://shopify/Product/123"). Required. Use list-products to find product IDs.'
7721
8258
  ),
7722
- moves: z33.array(moveSchema).min(1).describe(
8259
+ moves: z37.array(moveSchema).min(1).describe(
7723
8260
  "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)."
7724
8261
  )
7725
8262
  });
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")
8263
+ var outputSchema35 = z37.object({
8264
+ success: z37.boolean().describe("Whether the reorder operation was initiated successfully"),
8265
+ jobId: z37.string().nullable().describe("Background job ID for tracking (may be null if processed immediately)"),
8266
+ jobDone: z37.boolean().describe("Whether the job completed immediately"),
8267
+ message: z37.string().describe("Human-readable message describing the result")
7731
8268
  });
7732
8269
  var handleReorderProductImages = async (context, params) => {
7733
8270
  log.debug(`Reordering images on shop: ${context.shopDomain}`);
@@ -7795,8 +8332,8 @@ function registerReorderProductImagesTool() {
7795
8332
  name: "reorder-product-images",
7796
8333
  title: "Reorder Product Images",
7797
8334
  description: "Change the display order of product images. The first image (position 0) is typically the featured/hero image shown in product listings. Provide an array of move operations specifying which image IDs should move to which positions. Positions are 0-indexed. This operation runs asynchronously in Shopify's background. **Prerequisites:** get-product to obtain current image IDs and their positions.",
7798
- inputSchema: inputSchema31,
7799
- outputSchema: outputSchema31,
8335
+ inputSchema: inputSchema35,
8336
+ outputSchema: outputSchema35,
7800
8337
  // AI Agent Optimization (Story 5.5)
7801
8338
  category: "media",
7802
8339
  relationships: {
@@ -7817,45 +8354,45 @@ function registerReorderProductImagesTool() {
7817
8354
  }
7818
8355
 
7819
8356
  // src/tools/set-product-metafields.ts
7820
- import { z as z34 } from "zod";
8357
+ import { z as z38 } from "zod";
7821
8358
  var MAX_METAFIELDS_PER_CALL = 25;
7822
- var metafieldInputSchema = z34.object({
7823
- namespace: z34.string().min(1).describe(
8359
+ var metafieldInputSchema = z38.object({
8360
+ namespace: z38.string().min(1).describe(
7824
8361
  'Metafield namespace (grouping identifier, e.g., "custom", "seo", "my_app"). Use consistent namespaces to organize related metafields.'
7825
8362
  ),
7826
- key: z34.string().min(1).describe(
8363
+ key: z38.string().min(1).describe(
7827
8364
  'Metafield key (field name, e.g., "color_hex", "schema_markup"). Keys should be lowercase with underscores, unique within a namespace.'
7828
8365
  ),
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(
8366
+ value: z38.string().describe("The value to store. All values are stored as strings; JSON should be stringified."),
8367
+ type: z38.string().min(1).describe(
7831
8368
  '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".'
7832
8369
  )
7833
8370
  });
7834
- var inputSchema32 = z34.object({
8371
+ var inputSchema36 = z38.object({
7835
8372
  productId: productIdSchema.describe(
7836
8373
  'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Use get-product or list-products to find product IDs.'
7837
8374
  ),
7838
- metafields: z34.array(metafieldInputSchema).min(1, "At least one metafield is required").max(
8375
+ metafields: z38.array(metafieldInputSchema).min(1, "At least one metafield is required").max(
7839
8376
  MAX_METAFIELDS_PER_CALL,
7840
8377
  `Maximum ${MAX_METAFIELDS_PER_CALL} metafields per call. Split into multiple calls for larger batches.`
7841
8378
  ).describe(
7842
8379
  `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.`
7843
8380
  )
7844
8381
  });
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)")
8382
+ var outputSchema36 = z38.object({
8383
+ success: z38.boolean().describe("Whether the operation succeeded"),
8384
+ metafields: z38.array(
8385
+ z38.object({
8386
+ id: z38.string().describe("Metafield GID"),
8387
+ namespace: z38.string().describe("Metafield namespace"),
8388
+ key: z38.string().describe("Metafield key"),
8389
+ value: z38.string().describe("Metafield value"),
8390
+ type: z38.string().describe("Metafield type"),
8391
+ createdAt: z38.string().describe("Creation timestamp (ISO 8601)"),
8392
+ updatedAt: z38.string().describe("Last update timestamp (ISO 8601)")
7856
8393
  })
7857
8394
  ).describe("Created or updated metafields"),
7858
- count: z34.number().describe("Number of metafields processed")
8395
+ count: z38.number().describe("Number of metafields processed")
7859
8396
  });
7860
8397
  var handleSetProductMetafields = async (context, params) => {
7861
8398
  log.debug(
@@ -7891,8 +8428,8 @@ function registerSetProductMetafieldsTool() {
7891
8428
  name: "set-product-metafields",
7892
8429
  title: "Set Product Metafields",
7893
8430
  description: `Create or update up to ${MAX_METAFIELDS_PER_CALL} metafields on a product in a single operation. Each metafield requires: namespace (grouping identifier), key (field name), value (the data), and type. Existing metafields with matching namespace+key are updated; new ones are created. Common types: single_line_text_field, multi_line_text_field, number_integer, number_decimal, boolean, json. **Typical workflow:** create-product \u2192 set-product-metafields (add custom data). **Prerequisites:** get-product to obtain product ID, optionally get-product-metafields to check existing values. **Follow-ups:** get-product-metafields to verify changes.`,
7894
- inputSchema: inputSchema32,
7895
- outputSchema: outputSchema32,
8431
+ inputSchema: inputSchema36,
8432
+ outputSchema: outputSchema36,
7896
8433
  // AI Agent Optimization (Story 5.5)
7897
8434
  category: "seo",
7898
8435
  relationships: {
@@ -7914,47 +8451,47 @@ function registerSetProductMetafieldsTool() {
7914
8451
  }
7915
8452
 
7916
8453
  // src/tools/update-article.ts
7917
- import { z as z35 } from "zod";
7918
- var inputSchema33 = z35.object({
8454
+ import { z as z39 } from "zod";
8455
+ var inputSchema37 = z39.object({
7919
8456
  id: articleIdSchema.describe(
7920
8457
  '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.'
7921
8458
  ),
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(
8459
+ title: z39.string().optional().describe("The new title for the article. Only provided if changing the title."),
8460
+ authorName: z39.string().optional().describe("The new author name for the article."),
8461
+ body: z39.string().optional().describe("The new HTML body content of the article. Supports HTML markup for formatting."),
8462
+ summary: z39.string().optional().describe("A summary or excerpt of the article."),
8463
+ tags: z39.array(z39.string()).optional().describe(
7927
8464
  "New tags for categorization. This replaces all existing tags. Example: ['guide', 'tutorial', 'beginner']."
7928
8465
  ),
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")
8466
+ image: z39.object({
8467
+ url: z39.string().url().describe("The URL of the featured image"),
8468
+ altText: z39.string().optional().describe("Alt text for accessibility")
7932
8469
  }).optional().describe("Featured image for the article."),
7933
- isPublished: z35.boolean().optional().describe(
8470
+ isPublished: z39.boolean().optional().describe(
7934
8471
  "Whether the article should be visible on the storefront. Set to true to publish, false to unpublish."
7935
8472
  ),
7936
- publishDate: z35.string().optional().describe(
8473
+ publishDate: z39.string().optional().describe(
7937
8474
  "The date and time when the article should become visible (ISO 8601 format). Example: '2024-01-15T10:00:00Z'."
7938
8475
  ),
7939
- handle: z35.string().optional().describe(
8476
+ handle: z39.string().optional().describe(
7940
8477
  "The new URL handle/slug for the article. Set redirectNewHandle to true to automatically redirect the old URL to the new one."
7941
8478
  ),
7942
- templateSuffix: z35.string().optional().describe(
8479
+ templateSuffix: z39.string().optional().describe(
7943
8480
  "The suffix of the Liquid template used to render the article. For example, 'featured' would use the template 'article.featured.liquid'."
7944
8481
  ),
7945
- redirectNewHandle: z35.boolean().optional().describe(
8482
+ redirectNewHandle: z39.boolean().optional().describe(
7946
8483
  "Whether to automatically redirect the old URL to the new handle. Set to true when changing the handle to preserve SEO and existing links."
7947
8484
  )
7948
8485
  });
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")
8486
+ var outputSchema37 = z39.object({
8487
+ id: z39.string().describe("Updated article GID"),
8488
+ title: z39.string().describe("Article title"),
8489
+ handle: z39.string().describe("URL handle/slug"),
8490
+ blog: z39.object({
8491
+ id: z39.string().describe("Parent blog GID"),
8492
+ title: z39.string().describe("Parent blog title")
7956
8493
  }),
7957
- isPublished: z35.boolean().describe("Whether article is visible on storefront")
8494
+ isPublished: z39.boolean().describe("Whether article is visible on storefront")
7958
8495
  });
7959
8496
  var handleUpdateArticle = async (context, params) => {
7960
8497
  log.debug(`Updating article on shop: ${context.shopDomain}`);
@@ -8003,8 +8540,8 @@ function registerUpdateArticleTool() {
8003
8540
  name: "update-article",
8004
8541
  title: "Update Article",
8005
8542
  description: "Update an existing article's attributes. You can update title, body content (HTML), summary, author, tags, featured image, publish status, and template suffix. Only provided fields are updated; others remain unchanged. Use redirectNewHandle: true when changing handles. Useful for: editing content, adding tags, publishing/unpublishing articles. **Typical workflow:** list-articles \u2192 update-article. **Prerequisites:** Article ID. **Follow-ups:** list-articles. Returns the updated article object.",
8006
- inputSchema: inputSchema33,
8007
- outputSchema: outputSchema33,
8543
+ inputSchema: inputSchema37,
8544
+ outputSchema: outputSchema37,
8008
8545
  category: "content",
8009
8546
  relationships: {
8010
8547
  relatedTools: ["list-articles", "create-article", "delete-article"],
@@ -8024,30 +8561,30 @@ function registerUpdateArticleTool() {
8024
8561
  }
8025
8562
 
8026
8563
  // src/tools/update-blog.ts
8027
- import { z as z36 } from "zod";
8028
- var inputSchema34 = z36.object({
8564
+ import { z as z40 } from "zod";
8565
+ var inputSchema38 = z40.object({
8029
8566
  id: blogIdSchema.describe(
8030
8567
  '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.'
8031
8568
  ),
8032
- title: z36.string().optional().describe("The new title for the blog. Only provided if changing the title."),
8033
- handle: z36.string().optional().describe(
8569
+ title: z40.string().optional().describe("The new title for the blog. Only provided if changing the title."),
8570
+ handle: z40.string().optional().describe(
8034
8571
  "The new URL handle/slug for the blog. Set redirectNewHandle to true to automatically redirect the old URL to the new one."
8035
8572
  ),
8036
- commentPolicy: z36.enum(["CLOSED", "MODERATE", "OPEN"]).optional().describe(
8573
+ commentPolicy: z40.enum(["CLOSED", "MODERATE", "OPEN"]).optional().describe(
8037
8574
  "Comment moderation policy for the blog. CLOSED: Comments disabled. MODERATE: Comments require approval. OPEN: Comments appear immediately."
8038
8575
  ),
8039
- templateSuffix: z36.string().optional().describe(
8576
+ templateSuffix: z40.string().optional().describe(
8040
8577
  "The suffix of the Liquid template used to render the blog. For example, 'news' would use the template 'blog.news.liquid'."
8041
8578
  ),
8042
- redirectNewHandle: z36.boolean().optional().describe(
8579
+ redirectNewHandle: z40.boolean().optional().describe(
8043
8580
  "Whether to automatically redirect the old URL to the new handle. Set to true when changing the handle to preserve SEO and existing links."
8044
8581
  )
8045
8582
  });
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")
8583
+ var outputSchema38 = z40.object({
8584
+ id: z40.string().describe("Updated blog GID"),
8585
+ title: z40.string().describe("Blog title"),
8586
+ handle: z40.string().describe("URL handle/slug"),
8587
+ commentPolicy: z40.enum(["CLOSED", "MODERATE", "OPEN"]).describe("Comment moderation policy")
8051
8588
  });
8052
8589
  var handleUpdateBlog = async (context, params) => {
8053
8590
  log.debug(`Updating blog on shop: ${context.shopDomain}`);
@@ -8090,8 +8627,8 @@ function registerUpdateBlogTool() {
8090
8627
  name: "update-blog",
8091
8628
  title: "Update Blog",
8092
8629
  description: "Update an existing blog's attributes. You can update title, handle (URL slug), comment policy (CLOSED, MODERATE, OPEN), and template suffix. Only provided fields are updated; others remain unchanged. Use redirectNewHandle: true when changing handles. Useful for: renaming blogs, changing comment settings, updating templates. **Typical workflow:** list-blogs \u2192 update-blog. **Prerequisites:** Blog ID. **Follow-ups:** list-blogs. Returns the updated blog object.",
8093
- inputSchema: inputSchema34,
8094
- outputSchema: outputSchema34,
8630
+ inputSchema: inputSchema38,
8631
+ outputSchema: outputSchema38,
8095
8632
  category: "content",
8096
8633
  relationships: {
8097
8634
  relatedTools: ["list-blogs", "create-blog", "delete-blog"],
@@ -8111,21 +8648,21 @@ function registerUpdateBlogTool() {
8111
8648
  }
8112
8649
 
8113
8650
  // src/tools/update-collection.ts
8114
- import { z as z37 } from "zod";
8115
- var inputSchema35 = z37.object({
8651
+ import { z as z41 } from "zod";
8652
+ var inputSchema39 = z41.object({
8116
8653
  collectionId: collectionIdSchema.describe(
8117
8654
  'Collection ID to update (GID format, e.g., "gid://shopify/Collection/123"). Use list-collections or get-collection to find collection IDs.'
8118
8655
  ),
8119
- title: z37.string().min(1).optional().describe("New collection title"),
8120
- handle: z37.string().optional().describe(
8656
+ title: z41.string().min(1).optional().describe("New collection title"),
8657
+ handle: z41.string().optional().describe(
8121
8658
  "New URL handle/slug. When changing, set redirectNewHandle: true to create automatic redirect from the old URL."
8122
8659
  ),
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")
8660
+ descriptionHtml: z41.string().optional().describe("New HTML description"),
8661
+ seo: z41.object({
8662
+ title: z41.string().optional().describe("New SEO title"),
8663
+ description: z41.string().optional().describe("New SEO meta description")
8127
8664
  }).optional().describe("Updated SEO metadata"),
8128
- sortOrder: z37.enum([
8665
+ sortOrder: z41.enum([
8129
8666
  "ALPHA_ASC",
8130
8667
  "ALPHA_DESC",
8131
8668
  "BEST_SELLING",
@@ -8135,19 +8672,19 @@ var inputSchema35 = z37.object({
8135
8672
  "PRICE_ASC",
8136
8673
  "PRICE_DESC"
8137
8674
  ]).optional().describe("New product sort order within collection"),
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")
8675
+ image: z41.object({
8676
+ src: z41.string().url().describe("New image URL (publicly accessible)"),
8677
+ altText: z41.string().optional().describe("New alt text for accessibility")
8141
8678
  }).optional().describe("New collection image"),
8142
- templateSuffix: z37.string().optional().describe("New Liquid template suffix"),
8143
- redirectNewHandle: z37.boolean().optional().describe(
8679
+ templateSuffix: z41.string().optional().describe("New Liquid template suffix"),
8680
+ redirectNewHandle: z41.boolean().optional().describe(
8144
8681
  "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."
8145
8682
  )
8146
8683
  });
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")
8684
+ var outputSchema39 = z41.object({
8685
+ id: z41.string().describe("Updated collection GID"),
8686
+ title: z41.string().describe("Collection title"),
8687
+ handle: z41.string().describe("Collection URL handle")
8151
8688
  });
8152
8689
  var handleUpdateCollection = async (context, params) => {
8153
8690
  log.debug(`Updating collection on shop: ${context.shopDomain}`);
@@ -8186,8 +8723,8 @@ function registerUpdateCollectionTool() {
8186
8723
  name: "update-collection",
8187
8724
  title: "Update Collection",
8188
8725
  description: "Update an existing collection's attributes. Any field can be updated including title, handle, description, SEO fields, sort order, and image. When changing the handle, set redirectNewHandle: true for automatic URL redirect (important for SEO). Only provided fields are modified; omitted fields remain unchanged. **Prerequisites:** get-collection to view current values before updating. **Follow-ups:** add-products-to-collection, remove-products-from-collection.",
8189
- inputSchema: inputSchema35,
8190
- outputSchema: outputSchema35,
8726
+ inputSchema: inputSchema39,
8727
+ outputSchema: outputSchema39,
8191
8728
  // AI Agent Optimization (Story 5.5)
8192
8729
  category: "collection",
8193
8730
  relationships: {
@@ -8209,8 +8746,8 @@ function registerUpdateCollectionTool() {
8209
8746
  }
8210
8747
 
8211
8748
  // src/tools/update-inventory.ts
8212
- import { z as z38 } from "zod";
8213
- var inventoryReasonEnum = z38.enum([
8749
+ import { z as z42 } from "zod";
8750
+ var inventoryReasonEnum = z42.enum([
8214
8751
  "correction",
8215
8752
  "cycle_count_available",
8216
8753
  "damaged",
@@ -8229,20 +8766,20 @@ var inventoryReasonEnum = z38.enum([
8229
8766
  "safety_stock",
8230
8767
  "shrinkage"
8231
8768
  ]);
8232
- var inputSchema36 = z38.object({
8769
+ var inputSchema40 = z42.object({
8233
8770
  inventoryItemId: inventoryItemIdSchema.describe(
8234
8771
  'The Shopify inventory item ID (e.g., "gid://shopify/InventoryItem/456"). Get this from get-inventory tool or from get-product response.'
8235
8772
  ),
8236
8773
  locationId: locationIdSchema.describe(
8237
8774
  'The location ID where inventory should be updated (e.g., "gid://shopify/Location/789"). Get this from get-inventory tool.'
8238
8775
  ),
8239
- setQuantity: z38.number().int().min(0).optional().describe(
8776
+ setQuantity: z42.number().int().min(0).optional().describe(
8240
8777
  "Set inventory to this exact quantity. Example: 100. Use for absolute stock counts after physical inventory. Cannot be used together with adjustQuantity."
8241
8778
  ),
8242
- adjustQuantity: z38.number().int().optional().describe(
8779
+ adjustQuantity: z42.number().int().optional().describe(
8243
8780
  "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."
8244
8781
  ),
8245
- name: z38.enum(["available", "on_hand"]).optional().default("available").describe(
8782
+ name: z42.enum(["available", "on_hand"]).optional().default("available").describe(
8246
8783
  'Which quantity to modify. "available" (default) for sellable stock, "on_hand" for physical count.'
8247
8784
  ),
8248
8785
  reason: inventoryReasonEnum.optional().default("correction").describe(
@@ -8253,16 +8790,16 @@ var inputSchema36 = z38.object({
8253
8790
  }).refine((data) => !(data.setQuantity !== void 0 && data.adjustQuantity !== void 0), {
8254
8791
  message: "Cannot provide both setQuantity and adjustQuantity. Choose one."
8255
8792
  });
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")
8793
+ var outputSchema40 = z42.object({
8794
+ success: z42.boolean().describe("Whether the operation succeeded"),
8795
+ inventoryItemId: z42.string().describe("Inventory item GID"),
8796
+ locationId: z42.string().describe("Location GID where inventory was updated"),
8797
+ locationName: z42.string().describe("Human-readable location name"),
8798
+ previousQuantity: z42.number().describe("Quantity before the change"),
8799
+ newQuantity: z42.number().describe("Quantity after the change"),
8800
+ delta: z42.number().describe("The actual change applied (positive or negative)"),
8801
+ name: z42.string().describe("Which quantity was modified (available or on_hand)"),
8802
+ reason: z42.string().describe("Reason for the adjustment")
8266
8803
  });
8267
8804
  var handleUpdateInventory = async (context, params) => {
8268
8805
  log.debug(`Updating inventory on shop: ${context.shopDomain}`);
@@ -8336,8 +8873,8 @@ function registerUpdateInventoryTool() {
8336
8873
  name: "update-inventory",
8337
8874
  title: "Update Inventory",
8338
8875
  description: 'Update inventory quantity at a specific location. Use setQuantity for absolute values (e.g., setting stock to 50 after a count) or adjustQuantity for relative changes (e.g., +10 for received stock, -5 for shrinkage). Requires inventoryItemId (from get-inventory or get-product) and locationId (from get-inventory). Common reasons: "correction" (default), "received" (new stock), "shrinkage" (loss/theft), "damaged", "cycle_count_available" (stock take). Pre-validates that adjustments will not result in negative inventory. **Typical workflow:** get-inventory \u2192 update-inventory \u2192 get-inventory (to verify). **Prerequisites:** Inventory Item ID, Location ID. **Follow-ups:** get-inventory. Returns before/after quantities with delta.',
8339
- inputSchema: inputSchema36,
8340
- outputSchema: outputSchema36,
8876
+ inputSchema: inputSchema40,
8877
+ outputSchema: outputSchema40,
8341
8878
  category: "inventory",
8342
8879
  relationships: {
8343
8880
  relatedTools: ["get-inventory", "get-bulk-inventory", "list-low-inventory"],
@@ -8356,34 +8893,93 @@ function registerUpdateInventoryTool() {
8356
8893
  );
8357
8894
  }
8358
8895
 
8896
+ // src/tools/update-market.ts
8897
+ import { z as z43 } from "zod";
8898
+ var inputSchema41 = z43.object({
8899
+ id: marketIdSchema.describe(
8900
+ 'Shopify Market GID to update (e.g., "gid://shopify/Market/123"). Use list-markets to find market IDs.'
8901
+ ),
8902
+ name: z43.string().min(1).optional().describe("New market name"),
8903
+ handle: z43.string().optional().describe("New URL handle/slug for the market"),
8904
+ enabled: z43.boolean().optional().describe("Whether the market should be enabled")
8905
+ });
8906
+ var outputSchema41 = z43.object({
8907
+ id: z43.string().describe("Updated market GID"),
8908
+ name: z43.string().describe("Market name"),
8909
+ handle: z43.string().describe("URL handle"),
8910
+ enabled: z43.boolean().describe("Whether the market is enabled")
8911
+ });
8912
+ var handleUpdateMarket = async (context, params) => {
8913
+ log.debug(`Updating market ${params.id} on shop: ${context.shopDomain}`);
8914
+ const input = {};
8915
+ if (params.name !== void 0) {
8916
+ input.name = params.name;
8917
+ }
8918
+ if (params.handle !== void 0) {
8919
+ input.handle = params.handle;
8920
+ }
8921
+ if (params.enabled !== void 0) {
8922
+ input.enabled = params.enabled;
8923
+ }
8924
+ const market = await updateMarket(params.id, input);
8925
+ log.debug(`Updated market: ${market.id}`);
8926
+ return market;
8927
+ };
8928
+ function registerUpdateMarketTool() {
8929
+ registerContextAwareTool(
8930
+ {
8931
+ name: "update-market",
8932
+ title: "Update Market",
8933
+ description: "Update an existing market's properties. Only provided fields are updated; omitted fields remain unchanged. Can update: name, handle, enabled status. **Prerequisites:** get-market to view current values before updating. **Follow-ups:** get-market (to verify), delete-market.",
8934
+ inputSchema: inputSchema41,
8935
+ outputSchema: outputSchema41,
8936
+ // AI Agent Optimization
8937
+ category: "market",
8938
+ relationships: {
8939
+ relatedTools: ["get-market", "list-markets"],
8940
+ followUps: ["get-market", "delete-market"]
8941
+ },
8942
+ // MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
8943
+ annotations: {
8944
+ readOnlyHint: false,
8945
+ destructiveHint: false,
8946
+ idempotentHint: true,
8947
+ // Same update with same values produces same result
8948
+ openWorldHint: true
8949
+ }
8950
+ },
8951
+ handleUpdateMarket
8952
+ );
8953
+ }
8954
+
8359
8955
  // src/tools/update-page.ts
8360
- import { z as z39 } from "zod";
8361
- var inputSchema37 = z39.object({
8956
+ import { z as z44 } from "zod";
8957
+ var inputSchema42 = z44.object({
8362
8958
  id: pageIdSchema.describe(
8363
8959
  '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.'
8364
8960
  ),
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(
8961
+ title: z44.string().optional().describe("The new title for the page. Only provide if you want to change it."),
8962
+ body: z44.string().optional().describe(
8367
8963
  "The new HTML body content for the page. Supports HTML markup. Only provide if you want to change it."
8368
8964
  ),
8369
- handle: z39.string().optional().describe(
8965
+ handle: z44.string().optional().describe(
8370
8966
  "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."
8371
8967
  ),
8372
- isPublished: z39.boolean().optional().describe(
8968
+ isPublished: z44.boolean().optional().describe(
8373
8969
  "Whether the page should be visible on the storefront. Set to true to publish, false to unpublish."
8374
8970
  ),
8375
- templateSuffix: z39.string().optional().describe(
8971
+ templateSuffix: z44.string().optional().describe(
8376
8972
  "The suffix of the Liquid template used to render the page. Set to empty string to use the default template."
8377
8973
  ),
8378
- redirectNewHandle: z39.boolean().optional().describe(
8974
+ redirectNewHandle: z44.boolean().optional().describe(
8379
8975
  "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."
8380
8976
  )
8381
8977
  });
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")
8978
+ var outputSchema42 = z44.object({
8979
+ id: z44.string().describe("Updated page GID"),
8980
+ title: z44.string().describe("Page title"),
8981
+ handle: z44.string().describe("URL handle/slug"),
8982
+ isPublished: z44.boolean().describe("Whether page is visible on storefront")
8387
8983
  });
8388
8984
  var handleUpdatePage = async (context, params) => {
8389
8985
  log.debug(`Updating page on shop: ${context.shopDomain}`);
@@ -8427,8 +9023,8 @@ function registerUpdatePageTool() {
8427
9023
  name: "update-page",
8428
9024
  title: "Update Page",
8429
9025
  description: "Update an existing page's attributes. You can update title, body content (HTML), handle (URL slug), publish status, and template suffix. Only provided fields are updated; others remain unchanged. Use redirectNewHandle: true when changing handles to preserve SEO. Useful for: editing content, publishing/unpublishing pages, changing URLs. **Typical workflow:** get-page \u2192 update-page. **Prerequisites:** Page ID. **Follow-ups:** get-page. Returns the updated page object.",
8430
- inputSchema: inputSchema37,
8431
- outputSchema: outputSchema37,
9026
+ inputSchema: inputSchema42,
9027
+ outputSchema: outputSchema42,
8432
9028
  category: "content",
8433
9029
  relationships: {
8434
9030
  relatedTools: ["get-page", "list-pages"],
@@ -8448,7 +9044,7 @@ function registerUpdatePageTool() {
8448
9044
  }
8449
9045
 
8450
9046
  // src/tools/update-product-image.ts
8451
- import { z as z40 } from "zod";
9047
+ import { z as z45 } from "zod";
8452
9048
  var FILE_UPDATE_MUTATION = `
8453
9049
  mutation FileUpdate($files: [FileUpdateInput!]!) {
8454
9050
  fileUpdate(files: $files) {
@@ -8468,18 +9064,18 @@ var FILE_UPDATE_MUTATION = `
8468
9064
  }
8469
9065
  }
8470
9066
  `;
8471
- var inputSchema38 = z40.object({
9067
+ var inputSchema43 = z45.object({
8472
9068
  imageId: imageIdSchema.describe(
8473
9069
  'Shopify image GID (e.g., "gid://shopify/MediaImage/123"). Required. Use get-product to find image IDs in the media field.'
8474
9070
  ),
8475
- altText: z40.string().describe(
9071
+ altText: z45.string().describe(
8476
9072
  "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."
8477
9073
  )
8478
9074
  });
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")
9075
+ var outputSchema43 = z45.object({
9076
+ id: z45.string().describe('Image GID (e.g., "gid://shopify/MediaImage/123")'),
9077
+ url: z45.string().nullable().describe("Shopify CDN URL for the image"),
9078
+ altText: z45.string().nullable().describe("Updated alt text for the image")
8483
9079
  });
8484
9080
  var handleUpdateProductImage = async (context, params) => {
8485
9081
  log.debug(`Updating image alt text on shop: ${context.shopDomain}`);
@@ -8538,8 +9134,8 @@ function registerUpdateProductImageTool() {
8538
9134
  name: "update-product-image",
8539
9135
  title: "Update Product Image",
8540
9136
  description: "Update the alt text of a product image for SEO and accessibility. Alt text describes the image for screen readers and helps search engines understand image content. Pass an empty string to clear alt text. **Typical workflow:** add-product-image \u2192 update-product-image (set alt text). **Prerequisites:** add-product-image or get-product to find image IDs. **Follow-ups:** reorder-product-images.",
8541
- inputSchema: inputSchema38,
8542
- outputSchema: outputSchema38,
9137
+ inputSchema: inputSchema43,
9138
+ outputSchema: outputSchema43,
8543
9139
  // AI Agent Optimization (Story 5.5)
8544
9140
  category: "media",
8545
9141
  relationships: {
@@ -8561,19 +9157,19 @@ function registerUpdateProductImageTool() {
8561
9157
  }
8562
9158
 
8563
9159
  // src/tools/update-product-variant.ts
8564
- import { z as z41 } from "zod";
8565
- var inputSchema39 = z41.object({
9160
+ import { z as z46 } from "zod";
9161
+ var inputSchema44 = z46.object({
8566
9162
  productId: productIdSchema.describe(
8567
9163
  'Shopify product GID (e.g., "gid://shopify/Product/123"). Required. Use get-product to find the product ID containing the variant.'
8568
9164
  ),
8569
9165
  id: variantIdSchema.describe(
8570
9166
  'Shopify variant GID (e.g., "gid://shopify/ProductVariant/456"). Required. Use get-product to find variant IDs for a product.'
8571
9167
  ),
8572
- price: z41.string().optional().describe('New variant price as decimal string (e.g., "29.99")'),
8573
- compareAtPrice: z41.string().nullable().optional().describe(
9168
+ price: z46.string().optional().describe('New variant price as decimal string (e.g., "29.99")'),
9169
+ compareAtPrice: z46.string().nullable().optional().describe(
8574
9170
  '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.'
8575
9171
  ),
8576
- barcode: z41.string().nullable().optional().describe("Barcode (UPC, EAN, ISBN, etc.). Set to null to remove.")
9172
+ barcode: z46.string().nullable().optional().describe("Barcode (UPC, EAN, ISBN, etc.). Set to null to remove.")
8577
9173
  }).refine(
8578
9174
  (data) => {
8579
9175
  const { productId: _productId, id: _id, ...updateFields } = data;
@@ -8581,14 +9177,14 @@ var inputSchema39 = z41.object({
8581
9177
  },
8582
9178
  { message: "At least one field to update must be provided (price, compareAtPrice, or barcode)" }
8583
9179
  );
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")
9180
+ var outputSchema44 = z46.object({
9181
+ id: z46.string().describe('Variant GID (e.g., "gid://shopify/ProductVariant/123")'),
9182
+ title: z46.string().describe('Variant title (e.g., "Red / Medium")'),
9183
+ price: z46.string().describe("Current price as decimal string"),
9184
+ compareAtPrice: z46.string().nullable().describe("Compare at price for sale display"),
9185
+ sku: z46.string().nullable().describe("Stock keeping unit"),
9186
+ barcode: z46.string().nullable().describe("Barcode (UPC, EAN, etc.)"),
9187
+ inventoryQuantity: z46.number().nullable().describe("Available inventory quantity")
8592
9188
  });
8593
9189
  var handleUpdateProductVariant = async (context, params) => {
8594
9190
  log.debug(`Updating product variant on shop: ${context.shopDomain}`);
@@ -8617,8 +9213,8 @@ function registerUpdateProductVariantTool() {
8617
9213
  name: "update-product-variant",
8618
9214
  title: "Update Product Variant",
8619
9215
  description: 'Update an existing product variant in the Shopify store. Requires BOTH productId and variant id (GID format). Only provided fields are updated; others remain unchanged. Supports updating: price, compareAtPrice (for sale pricing), barcode. Returns the updated variant with all current details. To show sale pricing, set compareAtPrice higher than price (e.g., compareAtPrice: "39.99", price: "24.99"). **Prerequisites:** get-product to obtain productId and variant IDs. **Note:** SKU updates require separate inventory API calls.',
8620
- inputSchema: inputSchema39,
8621
- outputSchema: outputSchema39,
9216
+ inputSchema: inputSchema44,
9217
+ outputSchema: outputSchema44,
8622
9218
  // AI Agent Optimization (Story 5.5)
8623
9219
  category: "product",
8624
9220
  relationships: {
@@ -8639,25 +9235,25 @@ function registerUpdateProductVariantTool() {
8639
9235
  }
8640
9236
 
8641
9237
  // src/tools/update-product.ts
8642
- import { z as z42 } from "zod";
8643
- var inputSchema40 = z42.object({
9238
+ import { z as z47 } from "zod";
9239
+ var inputSchema45 = z47.object({
8644
9240
  id: productIdSchema.describe(
8645
9241
  'Shopify product ID (GID format, e.g., "gid://shopify/Product/123"). Required.'
8646
9242
  ),
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")
9243
+ title: z47.string().min(1).optional().describe("New product title"),
9244
+ description: z47.string().optional().describe("New product description (HTML supported)"),
9245
+ vendor: z47.string().optional().describe("New product vendor/brand name"),
9246
+ productType: z47.string().optional().describe("New product type for categorization"),
9247
+ tags: z47.array(z47.string()).optional().describe("New tags (replaces existing tags)"),
9248
+ status: z47.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).optional().describe("New product status: ACTIVE (visible), DRAFT (hidden), ARCHIVED (hidden)"),
9249
+ seo: z47.object({
9250
+ title: z47.string().optional().describe("SEO title for search engine results"),
9251
+ description: z47.string().optional().describe("SEO meta description for search engines")
8656
9252
  }).optional().describe("SEO metadata for search engine optimization"),
8657
- handle: z42.string().optional().describe(
9253
+ handle: z47.string().optional().describe(
8658
9254
  'New URL handle/slug (e.g., "my-product"). Use with redirectNewHandle for URL changes.'
8659
9255
  ),
8660
- redirectNewHandle: z42.boolean().optional().describe(
9256
+ redirectNewHandle: z47.boolean().optional().describe(
8661
9257
  "If true, creates automatic redirect from old handle to new handle. Use when changing handle."
8662
9258
  )
8663
9259
  }).refine(
@@ -8667,40 +9263,40 @@ var inputSchema40 = z42.object({
8667
9263
  },
8668
9264
  { message: "At least one field to update must be provided" }
8669
9265
  );
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")
9266
+ var outputSchema45 = z47.object({
9267
+ id: z47.string().describe('Product GID (e.g., "gid://shopify/Product/123")'),
9268
+ title: z47.string().describe("Product title"),
9269
+ handle: z47.string().describe("URL handle/slug"),
9270
+ description: z47.string().nullable().describe("Product description (HTML)"),
9271
+ vendor: z47.string().describe("Vendor/brand name"),
9272
+ productType: z47.string().describe("Product type"),
9273
+ status: z47.enum(["ACTIVE", "DRAFT", "ARCHIVED"]).describe("Product status"),
9274
+ tags: z47.array(z47.string()).describe("Product tags"),
9275
+ seo: z47.object({
9276
+ title: z47.string().nullable().describe("SEO title for search engines"),
9277
+ description: z47.string().nullable().describe("SEO description/meta description")
8682
9278
  }).describe("SEO metadata for search engine optimization"),
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")
9279
+ createdAt: z47.string().describe("Creation timestamp (ISO 8601)"),
9280
+ updatedAt: z47.string().describe("Last update timestamp (ISO 8601)"),
9281
+ variants: z47.array(
9282
+ z47.object({
9283
+ id: z47.string().describe("Variant GID"),
9284
+ title: z47.string().describe("Variant title"),
9285
+ price: z47.string().describe("Price"),
9286
+ compareAtPrice: z47.string().nullable().describe("Compare at price"),
9287
+ sku: z47.string().nullable().describe("SKU"),
9288
+ barcode: z47.string().nullable().describe("Barcode"),
9289
+ inventoryQuantity: z47.number().nullable().describe("Inventory quantity")
8694
9290
  })
8695
9291
  ).describe("Product variants"),
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")
9292
+ images: z47.array(
9293
+ z47.object({
9294
+ id: z47.string().describe("Image GID"),
9295
+ url: z47.string().describe("Image URL"),
9296
+ altText: z47.string().nullable().describe("Alt text")
8701
9297
  })
8702
9298
  ).describe("Product images"),
8703
- totalInventory: z42.number().describe("Total inventory across variants")
9299
+ totalInventory: z47.number().describe("Total inventory across variants")
8704
9300
  });
8705
9301
  var handleUpdateProduct = async (context, params) => {
8706
9302
  log.debug(`Updating product on shop: ${context.shopDomain}`);
@@ -8729,8 +9325,8 @@ function registerUpdateProductTool() {
8729
9325
  name: "update-product",
8730
9326
  title: "Update Product",
8731
9327
  description: "Update an existing product in the Shopify store. Requires the product ID (GID format). Only provided fields are updated; others remain unchanged. Supports updating: title, description, vendor, productType, tags, status, SEO metadata (seo.title, seo.description), and URL handle. Use handle + redirectNewHandle=true to change URLs with automatic redirect. Returns the updated product with all current details including SEO. **Prerequisites:** get-product to view current values before updating. **Follow-ups:** add-product-image, set-product-metafields.",
8732
- inputSchema: inputSchema40,
8733
- outputSchema: outputSchema40,
9328
+ inputSchema: inputSchema45,
9329
+ outputSchema: outputSchema45,
8734
9330
  // AI Agent Optimization (Story 5.5)
8735
9331
  category: "product",
8736
9332
  relationships: {
@@ -8847,6 +9443,11 @@ function registerAllTools(server) {
8847
9443
  registerGetInventoryTool();
8848
9444
  registerListLowInventoryTool();
8849
9445
  registerUpdateInventoryTool();
9446
+ registerCreateMarketTool();
9447
+ registerDeleteMarketTool();
9448
+ registerGetMarketTool();
9449
+ registerListMarketsTool();
9450
+ registerUpdateMarketTool();
8850
9451
  const toolCount = getRegisteredTools().length;
8851
9452
  log.debug(`Tool registration complete: ${toolCount} tools registered`);
8852
9453
  }