@anton.andrusenko/shopify-mcp-admin 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -6
- package/dist/index.js +1159 -13
- package/package.json +2 -2
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:
|
|
2454
|
+
const { name, title, description, inputSchema: inputSchema55, 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(
|
|
2463
|
-
const wrappedHandler = wrapToolHandler(name,
|
|
2462
|
+
const jsonSchema = convertZodToJsonSchema(inputSchema55);
|
|
2463
|
+
const wrappedHandler = wrapToolHandler(name, inputSchema55, 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:
|
|
2475
|
+
zodSchema: inputSchema55,
|
|
2476
2476
|
handler: wrappedHandler,
|
|
2477
2477
|
annotations: finalAnnotations
|
|
2478
2478
|
};
|
|
@@ -3173,6 +3173,12 @@ var redirectIdSchema = gidSchema("UrlRedirect");
|
|
|
3173
3173
|
var inventoryItemIdSchema = gidSchema("InventoryItem");
|
|
3174
3174
|
var locationIdSchema = gidSchema("Location");
|
|
3175
3175
|
var marketIdSchema = gidSchema("Market");
|
|
3176
|
+
var webPresenceIdSchema = gidSchema("MarketWebPresence");
|
|
3177
|
+
var LOCALE_CODE_PATTERN = /^[a-z]{2}(-[A-Za-z]{2})?$/;
|
|
3178
|
+
var localeCodeSchema = z2.string().regex(
|
|
3179
|
+
LOCALE_CODE_PATTERN,
|
|
3180
|
+
'Must be a valid locale code (e.g., "en", "fr", "de", "fr-CA", "pt-BR"). Use lowercase 2-letter language code, optionally followed by hyphen and region code.'
|
|
3181
|
+
).describe('Locale code (e.g., "en", "fr", "de", "fr-CA", "pt-BR")');
|
|
3176
3182
|
|
|
3177
3183
|
// src/tools/add-product-image.ts
|
|
3178
3184
|
var inputSchema = z3.object({
|
|
@@ -4533,6 +4539,19 @@ query Markets($first: Int!, $after: String) {
|
|
|
4533
4539
|
enabled
|
|
4534
4540
|
status
|
|
4535
4541
|
type
|
|
4542
|
+
conditions {
|
|
4543
|
+
regionsCondition {
|
|
4544
|
+
regions(first: 50) {
|
|
4545
|
+
nodes {
|
|
4546
|
+
id
|
|
4547
|
+
name
|
|
4548
|
+
... on MarketRegionCountry {
|
|
4549
|
+
countryCode: code
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4536
4555
|
currencySettings {
|
|
4537
4556
|
baseCurrency { currencyCode }
|
|
4538
4557
|
localCurrencies
|
|
@@ -4554,6 +4573,19 @@ query Market($id: ID!) {
|
|
|
4554
4573
|
enabled
|
|
4555
4574
|
status
|
|
4556
4575
|
type
|
|
4576
|
+
conditions {
|
|
4577
|
+
regionsCondition {
|
|
4578
|
+
regions(first: 50) {
|
|
4579
|
+
nodes {
|
|
4580
|
+
id
|
|
4581
|
+
name
|
|
4582
|
+
... on MarketRegionCountry {
|
|
4583
|
+
countryCode: code
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4557
4589
|
currencySettings {
|
|
4558
4590
|
baseCurrency { currencyCode currencyName }
|
|
4559
4591
|
localCurrencies
|
|
@@ -4595,6 +4627,60 @@ mutation MarketDelete($id: ID!) {
|
|
|
4595
4627
|
}
|
|
4596
4628
|
}
|
|
4597
4629
|
`;
|
|
4630
|
+
var LIST_WEB_PRESENCES_QUERY = `
|
|
4631
|
+
query WebPresences($first: Int!, $after: String) {
|
|
4632
|
+
webPresences(first: $first, after: $after) {
|
|
4633
|
+
nodes {
|
|
4634
|
+
id
|
|
4635
|
+
defaultLocale { locale name }
|
|
4636
|
+
alternateLocales { locale name }
|
|
4637
|
+
subfolderSuffix
|
|
4638
|
+
domain { host }
|
|
4639
|
+
rootUrls { locale url }
|
|
4640
|
+
}
|
|
4641
|
+
pageInfo {
|
|
4642
|
+
hasNextPage
|
|
4643
|
+
endCursor
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
`;
|
|
4648
|
+
var WEB_PRESENCE_CREATE_MUTATION = `
|
|
4649
|
+
mutation WebPresenceCreate($input: WebPresenceCreateInput!) {
|
|
4650
|
+
webPresenceCreate(input: $input) {
|
|
4651
|
+
webPresence {
|
|
4652
|
+
id
|
|
4653
|
+
defaultLocale { locale name }
|
|
4654
|
+
alternateLocales { locale name }
|
|
4655
|
+
subfolderSuffix
|
|
4656
|
+
domain { host }
|
|
4657
|
+
}
|
|
4658
|
+
userErrors { field message }
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
`;
|
|
4662
|
+
var WEB_PRESENCE_UPDATE_MUTATION = `
|
|
4663
|
+
mutation WebPresenceUpdate($id: ID!, $input: WebPresenceUpdateInput!) {
|
|
4664
|
+
webPresenceUpdate(id: $id, input: $input) {
|
|
4665
|
+
webPresence {
|
|
4666
|
+
id
|
|
4667
|
+
defaultLocale { locale name }
|
|
4668
|
+
alternateLocales { locale name }
|
|
4669
|
+
subfolderSuffix
|
|
4670
|
+
domain { host }
|
|
4671
|
+
}
|
|
4672
|
+
userErrors { field message }
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
`;
|
|
4676
|
+
var WEB_PRESENCE_DELETE_MUTATION = `
|
|
4677
|
+
mutation WebPresenceDelete($id: ID!) {
|
|
4678
|
+
webPresenceDelete(id: $id) {
|
|
4679
|
+
deletedId
|
|
4680
|
+
userErrors { field message }
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
`;
|
|
4598
4684
|
async function listMarkets(first = 50, after) {
|
|
4599
4685
|
const client = await getShopifyClient();
|
|
4600
4686
|
log.debug(`Listing markets: first=${first}, after=${after}`);
|
|
@@ -4619,6 +4705,13 @@ async function listMarkets(first = 50, after) {
|
|
|
4619
4705
|
enabled: node.enabled,
|
|
4620
4706
|
status: node.status,
|
|
4621
4707
|
type: node.type,
|
|
4708
|
+
regions: node.conditions?.regionsCondition?.regions?.nodes?.map(
|
|
4709
|
+
(region) => ({
|
|
4710
|
+
id: region.id,
|
|
4711
|
+
name: region.name,
|
|
4712
|
+
countryCode: region.countryCode
|
|
4713
|
+
})
|
|
4714
|
+
),
|
|
4622
4715
|
currencySettings: node.currencySettings ? {
|
|
4623
4716
|
baseCurrency: {
|
|
4624
4717
|
currencyCode: node.currencySettings.baseCurrency.currencyCode
|
|
@@ -4660,6 +4753,13 @@ async function getMarket(marketId) {
|
|
|
4660
4753
|
enabled: market.enabled,
|
|
4661
4754
|
status: market.status,
|
|
4662
4755
|
type: market.type,
|
|
4756
|
+
regions: market.conditions?.regionsCondition?.regions?.nodes?.map(
|
|
4757
|
+
(region) => ({
|
|
4758
|
+
id: region.id,
|
|
4759
|
+
name: region.name,
|
|
4760
|
+
countryCode: region.countryCode
|
|
4761
|
+
})
|
|
4762
|
+
),
|
|
4663
4763
|
currencySettings: market.currencySettings ? {
|
|
4664
4764
|
baseCurrency: {
|
|
4665
4765
|
currencyCode: market.currencySettings.baseCurrency.currencyCode,
|
|
@@ -4695,11 +4795,27 @@ async function createMarket(input) {
|
|
|
4695
4795
|
if (input.handle !== void 0) {
|
|
4696
4796
|
graphqlInput.handle = input.handle;
|
|
4697
4797
|
}
|
|
4698
|
-
if (input.
|
|
4699
|
-
graphqlInput.
|
|
4798
|
+
if (input.countryCodes !== void 0 && input.countryCodes.length > 0) {
|
|
4799
|
+
graphqlInput.conditions = {
|
|
4800
|
+
regionsCondition: {
|
|
4801
|
+
regions: input.countryCodes.map((code) => ({ countryCode: code }))
|
|
4802
|
+
}
|
|
4803
|
+
};
|
|
4700
4804
|
}
|
|
4701
4805
|
if (input.enabled !== void 0) {
|
|
4702
|
-
graphqlInput.
|
|
4806
|
+
graphqlInput.status = input.enabled ? "ACTIVE" : "INACTIVE";
|
|
4807
|
+
}
|
|
4808
|
+
if (input.currencySettings) {
|
|
4809
|
+
const currencyInput = {};
|
|
4810
|
+
if (input.currencySettings.baseCurrency !== void 0) {
|
|
4811
|
+
currencyInput.baseCurrency = input.currencySettings.baseCurrency;
|
|
4812
|
+
}
|
|
4813
|
+
if (input.currencySettings.localCurrencies !== void 0) {
|
|
4814
|
+
currencyInput.localCurrencies = input.currencySettings.localCurrencies;
|
|
4815
|
+
}
|
|
4816
|
+
if (Object.keys(currencyInput).length > 0) {
|
|
4817
|
+
graphqlInput.currencySettings = currencyInput;
|
|
4818
|
+
}
|
|
4703
4819
|
}
|
|
4704
4820
|
const response = await withRateLimit(
|
|
4705
4821
|
() => client.graphql.request(MARKET_CREATE_MUTATION, {
|
|
@@ -4735,7 +4851,35 @@ async function updateMarket(marketId, input) {
|
|
|
4735
4851
|
graphqlInput.handle = input.handle;
|
|
4736
4852
|
}
|
|
4737
4853
|
if (input.enabled !== void 0) {
|
|
4738
|
-
graphqlInput.
|
|
4854
|
+
graphqlInput.status = input.enabled ? "ACTIVE" : "INACTIVE";
|
|
4855
|
+
}
|
|
4856
|
+
if (input.countryCodesToAdd && input.countryCodesToAdd.length > 0) {
|
|
4857
|
+
graphqlInput.conditions = graphqlInput.conditions || {};
|
|
4858
|
+
graphqlInput.conditions.regionsCondition = {
|
|
4859
|
+
regionsToAdd: input.countryCodesToAdd.map((code) => ({ countryCode: code }))
|
|
4860
|
+
};
|
|
4861
|
+
}
|
|
4862
|
+
if (input.countryCodesToRemove && input.countryCodesToRemove.length > 0) {
|
|
4863
|
+
graphqlInput.conditions = graphqlInput.conditions || {};
|
|
4864
|
+
const regionsCondition = graphqlInput.conditions.regionsCondition || {};
|
|
4865
|
+
regionsCondition.regionsToRemove = input.countryCodesToRemove.map((code) => ({
|
|
4866
|
+
countryCode: code
|
|
4867
|
+
}));
|
|
4868
|
+
graphqlInput.conditions.regionsCondition = regionsCondition;
|
|
4869
|
+
}
|
|
4870
|
+
if (input.removeCurrencySettings) {
|
|
4871
|
+
graphqlInput.removeCurrencySettings = true;
|
|
4872
|
+
} else if (input.currencySettings) {
|
|
4873
|
+
const currencyInput = {};
|
|
4874
|
+
if (input.currencySettings.baseCurrency !== void 0) {
|
|
4875
|
+
currencyInput.baseCurrency = input.currencySettings.baseCurrency;
|
|
4876
|
+
}
|
|
4877
|
+
if (input.currencySettings.localCurrencies !== void 0) {
|
|
4878
|
+
currencyInput.localCurrencies = input.currencySettings.localCurrencies;
|
|
4879
|
+
}
|
|
4880
|
+
if (Object.keys(currencyInput).length > 0) {
|
|
4881
|
+
graphqlInput.currencySettings = currencyInput;
|
|
4882
|
+
}
|
|
4739
4883
|
}
|
|
4740
4884
|
const response = await withRateLimit(
|
|
4741
4885
|
() => client.graphql.request(MARKET_UPDATE_MUTATION, {
|
|
@@ -4786,6 +4930,152 @@ async function deleteMarket(marketId) {
|
|
|
4786
4930
|
log.debug(`Deleted market: ${result.deletedId}`);
|
|
4787
4931
|
return result.deletedId;
|
|
4788
4932
|
}
|
|
4933
|
+
function transformWebPresenceNode(node) {
|
|
4934
|
+
return {
|
|
4935
|
+
id: node.id,
|
|
4936
|
+
defaultLocale: {
|
|
4937
|
+
locale: node.defaultLocale.locale,
|
|
4938
|
+
name: node.defaultLocale.name
|
|
4939
|
+
},
|
|
4940
|
+
alternateLocales: node.alternateLocales?.map((al) => ({
|
|
4941
|
+
locale: al.locale,
|
|
4942
|
+
name: al.name
|
|
4943
|
+
})),
|
|
4944
|
+
subfolderSuffix: node.subfolderSuffix ?? void 0,
|
|
4945
|
+
domain: node.domain ? { host: node.domain.host } : void 0,
|
|
4946
|
+
rootUrls: node.rootUrls?.map((ru) => ({
|
|
4947
|
+
locale: ru.locale,
|
|
4948
|
+
url: ru.url
|
|
4949
|
+
}))
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
async function listWebPresences(first = 50, after) {
|
|
4953
|
+
const client = await getShopifyClient();
|
|
4954
|
+
log.debug(`Listing web presences: first=${first}, after=${after}`);
|
|
4955
|
+
const response = await withRateLimit(
|
|
4956
|
+
() => client.graphql.request(LIST_WEB_PRESENCES_QUERY, {
|
|
4957
|
+
variables: { first, after }
|
|
4958
|
+
}),
|
|
4959
|
+
"list-web-presences"
|
|
4960
|
+
);
|
|
4961
|
+
if (response.errors && response.errors.length > 0) {
|
|
4962
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
4963
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
4964
|
+
}
|
|
4965
|
+
const webPresencesData = response.data?.webPresences;
|
|
4966
|
+
if (!webPresencesData) {
|
|
4967
|
+
throw new Error("No response data from webPresences query");
|
|
4968
|
+
}
|
|
4969
|
+
const webPresences = webPresencesData.nodes.map(transformWebPresenceNode);
|
|
4970
|
+
log.debug(`Listed ${webPresences.length} web presences`);
|
|
4971
|
+
return {
|
|
4972
|
+
webPresences,
|
|
4973
|
+
pageInfo: {
|
|
4974
|
+
hasNextPage: webPresencesData.pageInfo.hasNextPage,
|
|
4975
|
+
endCursor: webPresencesData.pageInfo.endCursor
|
|
4976
|
+
}
|
|
4977
|
+
};
|
|
4978
|
+
}
|
|
4979
|
+
async function createWebPresence(input) {
|
|
4980
|
+
const client = await getShopifyClient();
|
|
4981
|
+
log.debug(`Creating web presence with defaultLocale: ${input.defaultLocale}`);
|
|
4982
|
+
const graphqlInput = {
|
|
4983
|
+
defaultLocale: input.defaultLocale
|
|
4984
|
+
};
|
|
4985
|
+
if (input.domainId !== void 0) {
|
|
4986
|
+
graphqlInput.domainId = input.domainId;
|
|
4987
|
+
}
|
|
4988
|
+
if (input.subfolderSuffix !== void 0) {
|
|
4989
|
+
graphqlInput.subfolderSuffix = input.subfolderSuffix;
|
|
4990
|
+
}
|
|
4991
|
+
if (input.alternateLocales !== void 0) {
|
|
4992
|
+
graphqlInput.alternateLocales = input.alternateLocales;
|
|
4993
|
+
}
|
|
4994
|
+
const response = await withRateLimit(
|
|
4995
|
+
() => client.graphql.request(WEB_PRESENCE_CREATE_MUTATION, {
|
|
4996
|
+
variables: { input: graphqlInput }
|
|
4997
|
+
}),
|
|
4998
|
+
"create-web-presence"
|
|
4999
|
+
);
|
|
5000
|
+
if (response.errors && response.errors.length > 0) {
|
|
5001
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
5002
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
5003
|
+
}
|
|
5004
|
+
const result = response.data?.webPresenceCreate;
|
|
5005
|
+
if (!result) {
|
|
5006
|
+
throw new Error("No response data from webPresenceCreate mutation");
|
|
5007
|
+
}
|
|
5008
|
+
if (result.userErrors && result.userErrors.length > 0) {
|
|
5009
|
+
throw new ShopifyUserErrorException(result.userErrors);
|
|
5010
|
+
}
|
|
5011
|
+
if (!result.webPresence) {
|
|
5012
|
+
throw new Error("Web presence creation succeeded but no web presence was returned");
|
|
5013
|
+
}
|
|
5014
|
+
log.debug(`Created web presence: ${result.webPresence.id}`);
|
|
5015
|
+
return transformWebPresenceNode(result.webPresence);
|
|
5016
|
+
}
|
|
5017
|
+
async function updateWebPresence(webPresenceId, input) {
|
|
5018
|
+
const client = await getShopifyClient();
|
|
5019
|
+
log.debug(`Updating web presence: ${webPresenceId}`);
|
|
5020
|
+
const graphqlInput = {};
|
|
5021
|
+
if (input.defaultLocale !== void 0) {
|
|
5022
|
+
graphqlInput.defaultLocale = input.defaultLocale;
|
|
5023
|
+
}
|
|
5024
|
+
if (input.alternateLocales !== void 0) {
|
|
5025
|
+
graphqlInput.alternateLocales = input.alternateLocales;
|
|
5026
|
+
}
|
|
5027
|
+
if (input.subfolderSuffix !== void 0) {
|
|
5028
|
+
graphqlInput.subfolderSuffix = input.subfolderSuffix;
|
|
5029
|
+
}
|
|
5030
|
+
const response = await withRateLimit(
|
|
5031
|
+
() => client.graphql.request(WEB_PRESENCE_UPDATE_MUTATION, {
|
|
5032
|
+
variables: { id: webPresenceId, input: graphqlInput }
|
|
5033
|
+
}),
|
|
5034
|
+
"update-web-presence"
|
|
5035
|
+
);
|
|
5036
|
+
if (response.errors && response.errors.length > 0) {
|
|
5037
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
5038
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
5039
|
+
}
|
|
5040
|
+
const result = response.data?.webPresenceUpdate;
|
|
5041
|
+
if (!result) {
|
|
5042
|
+
throw new Error("No response data from webPresenceUpdate mutation");
|
|
5043
|
+
}
|
|
5044
|
+
if (result.userErrors && result.userErrors.length > 0) {
|
|
5045
|
+
throw new ShopifyUserErrorException(result.userErrors);
|
|
5046
|
+
}
|
|
5047
|
+
if (!result.webPresence) {
|
|
5048
|
+
throw new Error("Web presence update succeeded but no web presence was returned");
|
|
5049
|
+
}
|
|
5050
|
+
log.debug(`Updated web presence: ${result.webPresence.id}`);
|
|
5051
|
+
return transformWebPresenceNode(result.webPresence);
|
|
5052
|
+
}
|
|
5053
|
+
async function deleteWebPresence(webPresenceId) {
|
|
5054
|
+
const client = await getShopifyClient();
|
|
5055
|
+
log.debug(`Deleting web presence: ${webPresenceId}`);
|
|
5056
|
+
const response = await withRateLimit(
|
|
5057
|
+
() => client.graphql.request(WEB_PRESENCE_DELETE_MUTATION, {
|
|
5058
|
+
variables: { id: webPresenceId }
|
|
5059
|
+
}),
|
|
5060
|
+
"delete-web-presence"
|
|
5061
|
+
);
|
|
5062
|
+
if (response.errors && response.errors.length > 0) {
|
|
5063
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
5064
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
5065
|
+
}
|
|
5066
|
+
const result = response.data?.webPresenceDelete;
|
|
5067
|
+
if (!result) {
|
|
5068
|
+
throw new Error("No response data from webPresenceDelete mutation");
|
|
5069
|
+
}
|
|
5070
|
+
if (result.userErrors && result.userErrors.length > 0) {
|
|
5071
|
+
throw new ShopifyUserErrorException(result.userErrors);
|
|
5072
|
+
}
|
|
5073
|
+
if (!result.deletedId) {
|
|
5074
|
+
throw new Error("Web presence deletion succeeded but no deletedId was returned");
|
|
5075
|
+
}
|
|
5076
|
+
log.debug(`Deleted web presence: ${result.deletedId}`);
|
|
5077
|
+
return result.deletedId;
|
|
5078
|
+
}
|
|
4789
5079
|
|
|
4790
5080
|
// src/tools/create-market.ts
|
|
4791
5081
|
var inputSchema6 = z8.object({
|
|
@@ -4796,7 +5086,15 @@ var inputSchema6 = z8.object({
|
|
|
4796
5086
|
countryCodes: z8.array(z8.string().length(2)).optional().describe(
|
|
4797
5087
|
'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
5088
|
),
|
|
4799
|
-
enabled: z8.boolean().optional().default(
|
|
5089
|
+
enabled: z8.boolean().optional().default(false).describe(
|
|
5090
|
+
"Whether the market should be enabled (status: ACTIVE). Default: false (creates market as INACTIVE/draft). Set to true only when ready to make the market live. \u26A0\uFE0F ACTIVE markets are immediately visible to customers in those regions."
|
|
5091
|
+
),
|
|
5092
|
+
baseCurrency: z8.string().length(3).optional().describe(
|
|
5093
|
+
'Base currency code for the market (ISO 4217). Example: "USD", "EUR", "CAD", "GBP". If not set, uses store default currency.'
|
|
5094
|
+
),
|
|
5095
|
+
localCurrencies: z8.boolean().optional().describe(
|
|
5096
|
+
"Whether to enable local currencies for better customer experience. When true, prices are converted to customer local currency. When false, all customers see prices in baseCurrency."
|
|
5097
|
+
)
|
|
4800
5098
|
});
|
|
4801
5099
|
var outputSchema6 = z8.object({
|
|
4802
5100
|
id: z8.string().describe("Created market GID"),
|
|
@@ -4813,11 +5111,20 @@ var handleCreateMarket = async (context, params) => {
|
|
|
4813
5111
|
input.handle = params.handle;
|
|
4814
5112
|
}
|
|
4815
5113
|
if (params.countryCodes !== void 0 && params.countryCodes.length > 0) {
|
|
4816
|
-
input.
|
|
5114
|
+
input.countryCodes = params.countryCodes;
|
|
4817
5115
|
}
|
|
4818
5116
|
if (params.enabled !== void 0) {
|
|
4819
5117
|
input.enabled = params.enabled;
|
|
4820
5118
|
}
|
|
5119
|
+
if (params.baseCurrency !== void 0 || params.localCurrencies !== void 0) {
|
|
5120
|
+
input.currencySettings = {};
|
|
5121
|
+
if (params.baseCurrency !== void 0) {
|
|
5122
|
+
input.currencySettings.baseCurrency = params.baseCurrency;
|
|
5123
|
+
}
|
|
5124
|
+
if (params.localCurrencies !== void 0) {
|
|
5125
|
+
input.currencySettings.localCurrencies = params.localCurrencies;
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
4821
5128
|
const market = await createMarket(input);
|
|
4822
5129
|
log.debug(`Created market: ${market.id}`);
|
|
4823
5130
|
return market;
|
|
@@ -4827,7 +5134,7 @@ function registerCreateMarketTool() {
|
|
|
4827
5134
|
{
|
|
4828
5135
|
name: "create-market",
|
|
4829
5136
|
title: "Create Market",
|
|
4830
|
-
description:
|
|
5137
|
+
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, currency settings, and enabled status. \u26A0\uFE0F **Note:** Markets are created as INACTIVE by default (enabled=false). Use update-market to activate when ready, or set enabled=true if you want it live immediately. **Currency:** Set baseCurrency (e.g., "CAD") and localCurrencies for multi-currency support. **Typical workflow:** create-market \u2192 create-web-presence (configure SEO URLs) \u2192 enable-shop-locale (add languages) \u2192 update-market (enable when ready). **Prerequisites:** None. Store must have Markets feature enabled on their plan. **Follow-ups:** get-market, update-market, create-web-presence.',
|
|
4831
5138
|
inputSchema: inputSchema6,
|
|
4832
5139
|
outputSchema: outputSchema6,
|
|
4833
5140
|
// AI Agent Optimization
|
|
@@ -7209,6 +7516,13 @@ var outputSchema22 = z24.object({
|
|
|
7209
7516
|
enabled: z24.boolean().describe("Whether the market is enabled"),
|
|
7210
7517
|
status: z24.enum(["ACTIVE", "INACTIVE"]).describe("Market status"),
|
|
7211
7518
|
type: z24.string().describe("Market type (PRIMARY, SINGLE_COUNTRY, MULTI_COUNTRY, CONTINENT)"),
|
|
7519
|
+
regions: z24.array(
|
|
7520
|
+
z24.object({
|
|
7521
|
+
id: z24.string().describe("Region GID"),
|
|
7522
|
+
name: z24.string().describe('Region name (e.g., "Canada", "France")'),
|
|
7523
|
+
countryCode: z24.string().optional().describe("ISO 3166-1 alpha-2 country code")
|
|
7524
|
+
})
|
|
7525
|
+
).optional().describe("Countries/regions included in this market"),
|
|
7212
7526
|
currencySettings: z24.object({
|
|
7213
7527
|
baseCurrency: z24.object({
|
|
7214
7528
|
currencyCode: z24.string().describe("Base currency code (e.g., USD, EUR)"),
|
|
@@ -7865,6 +8179,13 @@ var outputSchema30 = z32.object({
|
|
|
7865
8179
|
enabled: z32.boolean().describe("Whether the market is enabled"),
|
|
7866
8180
|
status: z32.enum(["ACTIVE", "INACTIVE"]).describe("Market status"),
|
|
7867
8181
|
type: z32.string().describe("Market type (PRIMARY, SINGLE_COUNTRY, MULTI_COUNTRY, CONTINENT)"),
|
|
8182
|
+
regions: z32.array(
|
|
8183
|
+
z32.object({
|
|
8184
|
+
id: z32.string().describe("Region GID"),
|
|
8185
|
+
name: z32.string().describe("Region name"),
|
|
8186
|
+
countryCode: z32.string().optional().describe("ISO country code")
|
|
8187
|
+
})
|
|
8188
|
+
).optional().describe("Countries in this market"),
|
|
7868
8189
|
currencySettings: z32.object({
|
|
7869
8190
|
baseCurrency: z32.object({
|
|
7870
8191
|
currencyCode: z32.string().describe("Base currency code (e.g., USD, EUR)")
|
|
@@ -8901,7 +9222,22 @@ var inputSchema41 = z43.object({
|
|
|
8901
9222
|
),
|
|
8902
9223
|
name: z43.string().min(1).optional().describe("New market name"),
|
|
8903
9224
|
handle: z43.string().optional().describe("New URL handle/slug for the market"),
|
|
8904
|
-
enabled: z43.boolean().optional().describe(
|
|
9225
|
+
enabled: z43.boolean().optional().describe(
|
|
9226
|
+
"Whether the market should be enabled (status: ACTIVE). Set to true to activate the market, false to deactivate."
|
|
9227
|
+
),
|
|
9228
|
+
countryCodesToAdd: z43.array(z43.string().length(2)).optional().describe(
|
|
9229
|
+
'Country codes to ADD to this market (ISO 3166-1 alpha-2). Example: ["CA", "MX"] to add Canada and Mexico'
|
|
9230
|
+
),
|
|
9231
|
+
countryCodesToRemove: z43.array(z43.string().length(2)).optional().describe(
|
|
9232
|
+
'Country codes to REMOVE from this market (ISO 3166-1 alpha-2). Example: ["US"] to remove United States'
|
|
9233
|
+
),
|
|
9234
|
+
baseCurrency: z43.string().length(3).optional().describe(
|
|
9235
|
+
'Base currency code for the market (ISO 4217). Example: "USD", "EUR", "CAD", "GBP".'
|
|
9236
|
+
),
|
|
9237
|
+
localCurrencies: z43.boolean().optional().describe(
|
|
9238
|
+
"Whether to enable local currencies for better customer experience. When true, prices are converted to customer local currency."
|
|
9239
|
+
),
|
|
9240
|
+
removeCurrencySettings: z43.boolean().optional().describe("Set to true to remove currency settings and use store defaults.")
|
|
8905
9241
|
});
|
|
8906
9242
|
var outputSchema41 = z43.object({
|
|
8907
9243
|
id: z43.string().describe("Updated market GID"),
|
|
@@ -8921,6 +9257,23 @@ var handleUpdateMarket = async (context, params) => {
|
|
|
8921
9257
|
if (params.enabled !== void 0) {
|
|
8922
9258
|
input.enabled = params.enabled;
|
|
8923
9259
|
}
|
|
9260
|
+
if (params.countryCodesToAdd !== void 0 && params.countryCodesToAdd.length > 0) {
|
|
9261
|
+
input.countryCodesToAdd = params.countryCodesToAdd;
|
|
9262
|
+
}
|
|
9263
|
+
if (params.countryCodesToRemove !== void 0 && params.countryCodesToRemove.length > 0) {
|
|
9264
|
+
input.countryCodesToRemove = params.countryCodesToRemove;
|
|
9265
|
+
}
|
|
9266
|
+
if (params.removeCurrencySettings) {
|
|
9267
|
+
input.removeCurrencySettings = true;
|
|
9268
|
+
} else if (params.baseCurrency !== void 0 || params.localCurrencies !== void 0) {
|
|
9269
|
+
input.currencySettings = {};
|
|
9270
|
+
if (params.baseCurrency !== void 0) {
|
|
9271
|
+
input.currencySettings.baseCurrency = params.baseCurrency;
|
|
9272
|
+
}
|
|
9273
|
+
if (params.localCurrencies !== void 0) {
|
|
9274
|
+
input.currencySettings.localCurrencies = params.localCurrencies;
|
|
9275
|
+
}
|
|
9276
|
+
}
|
|
8924
9277
|
const market = await updateMarket(params.id, input);
|
|
8925
9278
|
log.debug(`Updated market: ${market.id}`);
|
|
8926
9279
|
return market;
|
|
@@ -8930,7 +9283,7 @@ function registerUpdateMarketTool() {
|
|
|
8930
9283
|
{
|
|
8931
9284
|
name: "update-market",
|
|
8932
9285
|
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.",
|
|
9286
|
+
description: "Update an existing market's properties. Only provided fields are updated; omitted fields remain unchanged. Can update: name, handle, enabled status, regions (add/remove countries), and currency settings. **Regions:** Use countryCodesToAdd/countryCodesToRemove to manage countries. **Currency:** Set baseCurrency and localCurrencies, or use removeCurrencySettings to reset. **Prerequisites:** get-market to view current values before updating. **Follow-ups:** get-market (to verify), delete-market.",
|
|
8934
9287
|
inputSchema: inputSchema41,
|
|
8935
9288
|
outputSchema: outputSchema41,
|
|
8936
9289
|
// AI Agent Optimization
|
|
@@ -9347,6 +9700,790 @@ function registerUpdateProductTool() {
|
|
|
9347
9700
|
);
|
|
9348
9701
|
}
|
|
9349
9702
|
|
|
9703
|
+
// src/tools/create-web-presence.ts
|
|
9704
|
+
import { z as z48 } from "zod";
|
|
9705
|
+
var inputSchema46 = z48.object({
|
|
9706
|
+
defaultLocale: z48.string().min(1).describe(
|
|
9707
|
+
'Default locale for the web presence (required). Use locale codes like "en-CA", "fr-FR", "de-DE". This locale will be served when the domain root or subfolder is accessed.'
|
|
9708
|
+
),
|
|
9709
|
+
domainId: z48.string().optional().describe(
|
|
9710
|
+
'Domain GID for custom domain configuration (e.g., "gid://shopify/Domain/123"). Mutually exclusive with subfolderSuffix - provide one or the other, not both. Use get-market to find available domains.'
|
|
9711
|
+
),
|
|
9712
|
+
subfolderSuffix: z48.string().optional().describe(
|
|
9713
|
+
'Subfolder suffix for URL structure (e.g., "ca" for /en-ca, "us" for /en-us). Only ASCII characters allowed. Mutually exclusive with domainId - provide one or the other, not both.'
|
|
9714
|
+
),
|
|
9715
|
+
alternateLocales: z48.array(z48.string()).optional().describe(
|
|
9716
|
+
'Additional locales for this web presence. Example: ["fr-CA", "es-CA"] for Canadian French and Spanish support. These will be available as language-specific subfolders.'
|
|
9717
|
+
)
|
|
9718
|
+
});
|
|
9719
|
+
var outputSchema46 = z48.object({
|
|
9720
|
+
id: z48.string().describe("Created web presence GID"),
|
|
9721
|
+
defaultLocale: z48.object({
|
|
9722
|
+
locale: z48.string().describe("Locale code"),
|
|
9723
|
+
name: z48.string().describe("Locale display name")
|
|
9724
|
+
}),
|
|
9725
|
+
alternateLocales: z48.array(
|
|
9726
|
+
z48.object({
|
|
9727
|
+
locale: z48.string(),
|
|
9728
|
+
name: z48.string()
|
|
9729
|
+
})
|
|
9730
|
+
).optional(),
|
|
9731
|
+
subfolderSuffix: z48.string().optional().describe("Subfolder suffix if configured"),
|
|
9732
|
+
domain: z48.object({
|
|
9733
|
+
host: z48.string().describe("Domain hostname")
|
|
9734
|
+
}).optional().describe("Domain if configured")
|
|
9735
|
+
});
|
|
9736
|
+
var handleCreateWebPresence = async (context, params) => {
|
|
9737
|
+
log.debug(`Creating web presence on shop: ${context.shopDomain}`);
|
|
9738
|
+
const input = {
|
|
9739
|
+
defaultLocale: params.defaultLocale
|
|
9740
|
+
};
|
|
9741
|
+
if (params.domainId !== void 0) {
|
|
9742
|
+
input.domainId = params.domainId;
|
|
9743
|
+
}
|
|
9744
|
+
if (params.subfolderSuffix !== void 0) {
|
|
9745
|
+
input.subfolderSuffix = params.subfolderSuffix;
|
|
9746
|
+
}
|
|
9747
|
+
if (params.alternateLocales !== void 0) {
|
|
9748
|
+
input.alternateLocales = params.alternateLocales;
|
|
9749
|
+
}
|
|
9750
|
+
const webPresence = await createWebPresence(input);
|
|
9751
|
+
log.debug(`Created web presence: ${webPresence.id}`);
|
|
9752
|
+
return webPresence;
|
|
9753
|
+
};
|
|
9754
|
+
function registerCreateWebPresenceTool() {
|
|
9755
|
+
registerContextAwareTool(
|
|
9756
|
+
{
|
|
9757
|
+
name: "create-web-presence",
|
|
9758
|
+
title: "Create Web Presence",
|
|
9759
|
+
description: "Create a new web presence for SEO URL configuration. Web presences define how URLs are structured for markets and locales. Provide defaultLocale (required) and either domainId (for custom domains like example.ca) OR subfolderSuffix (for subfolders like /en-ca) - these are mutually exclusive. Optionally add alternateLocales for multi-language support. **Typical workflow:** create-market \u2192 create-web-presence \u2192 enable-shop-locale. **Prerequisites:** Store must have Markets feature enabled. Locale must be enabled in shop settings. **Follow-ups:** list-web-presences, update-web-presence, get-market.",
|
|
9760
|
+
inputSchema: inputSchema46,
|
|
9761
|
+
outputSchema: outputSchema46,
|
|
9762
|
+
// AI Agent Optimization
|
|
9763
|
+
category: "market",
|
|
9764
|
+
relationships: {
|
|
9765
|
+
relatedTools: ["list-web-presences", "create-market", "get-market"],
|
|
9766
|
+
followUps: ["update-web-presence", "list-web-presences", "get-market"]
|
|
9767
|
+
},
|
|
9768
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
9769
|
+
annotations: {
|
|
9770
|
+
readOnlyHint: false,
|
|
9771
|
+
destructiveHint: false,
|
|
9772
|
+
idempotentHint: false,
|
|
9773
|
+
// Creating web presences is not idempotent
|
|
9774
|
+
openWorldHint: true
|
|
9775
|
+
}
|
|
9776
|
+
},
|
|
9777
|
+
handleCreateWebPresence
|
|
9778
|
+
);
|
|
9779
|
+
}
|
|
9780
|
+
|
|
9781
|
+
// src/tools/delete-web-presence.ts
|
|
9782
|
+
import { z as z49 } from "zod";
|
|
9783
|
+
var inputSchema47 = z49.object({
|
|
9784
|
+
id: webPresenceIdSchema.describe(
|
|
9785
|
+
'Web presence GID to delete (e.g., "gid://shopify/MarketWebPresence/123"). Use list-web-presences or get-market to find web presence IDs.'
|
|
9786
|
+
)
|
|
9787
|
+
});
|
|
9788
|
+
var outputSchema47 = z49.object({
|
|
9789
|
+
deletedId: z49.string().describe("The GID of the deleted web presence"),
|
|
9790
|
+
success: z49.boolean().describe("Whether the deletion was successful"),
|
|
9791
|
+
message: z49.string().describe("Confirmation message")
|
|
9792
|
+
});
|
|
9793
|
+
var handleDeleteWebPresence = async (context, params) => {
|
|
9794
|
+
log.debug(`Deleting web presence ${params.id} on shop: ${context.shopDomain}`);
|
|
9795
|
+
const deletedId = await deleteWebPresence(params.id);
|
|
9796
|
+
log.debug(`Deleted web presence: ${deletedId}`);
|
|
9797
|
+
return {
|
|
9798
|
+
deletedId,
|
|
9799
|
+
success: true,
|
|
9800
|
+
message: `Web presence ${deletedId} has been deleted. Note: URLs previously served by this web presence may no longer resolve correctly. Consider setting up URL redirects if needed.`
|
|
9801
|
+
};
|
|
9802
|
+
};
|
|
9803
|
+
function registerDeleteWebPresenceTool() {
|
|
9804
|
+
registerContextAwareTool(
|
|
9805
|
+
{
|
|
9806
|
+
name: "delete-web-presence",
|
|
9807
|
+
title: "Delete Web Presence",
|
|
9808
|
+
description: "\u26A0\uFE0F **WARNING: SEO IMPACT** - Delete a web presence (SEO URL configuration). Deleting a web presence removes the URL structure for a market. URLs that were previously served by this web presence will no longer resolve, which can negatively impact SEO and break existing links. Consider setting up URL redirects before or after deletion to preserve SEO value. **Prerequisites:** list-web-presences or get-market to find web presence IDs. **Follow-ups:** create-redirect (to preserve SEO), list-web-presences.",
|
|
9809
|
+
inputSchema: inputSchema47,
|
|
9810
|
+
outputSchema: outputSchema47,
|
|
9811
|
+
// AI Agent Optimization
|
|
9812
|
+
category: "market",
|
|
9813
|
+
relationships: {
|
|
9814
|
+
relatedTools: ["list-web-presences", "get-market", "create-redirect"],
|
|
9815
|
+
followUps: ["create-redirect", "list-web-presences"]
|
|
9816
|
+
},
|
|
9817
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
9818
|
+
annotations: {
|
|
9819
|
+
readOnlyHint: false,
|
|
9820
|
+
destructiveHint: true,
|
|
9821
|
+
// This is a destructive operation
|
|
9822
|
+
idempotentHint: false,
|
|
9823
|
+
// Deleting twice fails on second attempt
|
|
9824
|
+
openWorldHint: true
|
|
9825
|
+
}
|
|
9826
|
+
},
|
|
9827
|
+
handleDeleteWebPresence
|
|
9828
|
+
);
|
|
9829
|
+
}
|
|
9830
|
+
|
|
9831
|
+
// src/tools/list-web-presences.ts
|
|
9832
|
+
import { z as z50 } from "zod";
|
|
9833
|
+
var inputSchema48 = z50.object({
|
|
9834
|
+
first: z50.number().int().min(1).max(50).optional().default(10).describe("Number of web presences to return (1-50). Default: 10"),
|
|
9835
|
+
after: z50.string().optional().describe(
|
|
9836
|
+
"Pagination cursor from previous response. Use the endCursor value from the previous page to get the next page of results."
|
|
9837
|
+
)
|
|
9838
|
+
});
|
|
9839
|
+
var outputSchema48 = z50.object({
|
|
9840
|
+
webPresences: z50.array(
|
|
9841
|
+
z50.object({
|
|
9842
|
+
id: z50.string().describe("Web presence GID"),
|
|
9843
|
+
defaultLocale: z50.object({
|
|
9844
|
+
locale: z50.string().describe("Locale code (e.g., en-CA)"),
|
|
9845
|
+
name: z50.string().describe("Locale display name")
|
|
9846
|
+
}),
|
|
9847
|
+
alternateLocales: z50.array(
|
|
9848
|
+
z50.object({
|
|
9849
|
+
locale: z50.string().describe("Locale code"),
|
|
9850
|
+
name: z50.string().describe("Locale display name")
|
|
9851
|
+
})
|
|
9852
|
+
).optional().describe("Additional locales supported by this web presence"),
|
|
9853
|
+
subfolderSuffix: z50.string().optional().describe('Subfolder suffix (e.g., "ca" for /en-ca). Null if using a domain.'),
|
|
9854
|
+
domain: z50.object({
|
|
9855
|
+
host: z50.string().describe("Custom domain hostname")
|
|
9856
|
+
}).optional().describe("Custom domain configuration. Null if using subfolders."),
|
|
9857
|
+
rootUrls: z50.array(
|
|
9858
|
+
z50.object({
|
|
9859
|
+
locale: z50.string().describe("Locale code"),
|
|
9860
|
+
url: z50.string().describe("Full URL for this locale")
|
|
9861
|
+
})
|
|
9862
|
+
).optional().describe("Root URLs for each locale")
|
|
9863
|
+
})
|
|
9864
|
+
),
|
|
9865
|
+
pageInfo: z50.object({
|
|
9866
|
+
hasNextPage: z50.boolean().describe("Whether more pages exist"),
|
|
9867
|
+
endCursor: z50.string().nullable().describe("Cursor for next page")
|
|
9868
|
+
}),
|
|
9869
|
+
summary: z50.object({
|
|
9870
|
+
totalReturned: z50.number().describe("Number of web presences in this response"),
|
|
9871
|
+
hasMore: z50.boolean().describe("Whether more web presences are available"),
|
|
9872
|
+
hint: z50.string().describe("Suggestion for next action")
|
|
9873
|
+
}).describe("AI-friendly summary for context management")
|
|
9874
|
+
});
|
|
9875
|
+
var handleListWebPresences = async (context, params) => {
|
|
9876
|
+
log.debug(`Listing web presences on shop: ${context.shopDomain}`);
|
|
9877
|
+
const result = await listWebPresences(params.first, params.after);
|
|
9878
|
+
log.debug(`Listed ${result.webPresences.length} web presences via tool`);
|
|
9879
|
+
return {
|
|
9880
|
+
...result,
|
|
9881
|
+
summary: {
|
|
9882
|
+
totalReturned: result.webPresences.length,
|
|
9883
|
+
hasMore: result.pageInfo.hasNextPage,
|
|
9884
|
+
hint: result.pageInfo.hasNextPage ? `Use "after" parameter with value "${result.pageInfo.endCursor}" to fetch next page` : "All web presences returned"
|
|
9885
|
+
}
|
|
9886
|
+
};
|
|
9887
|
+
};
|
|
9888
|
+
function registerListWebPresencesTool() {
|
|
9889
|
+
registerContextAwareTool(
|
|
9890
|
+
{
|
|
9891
|
+
name: "list-web-presences",
|
|
9892
|
+
title: "List Web Presences",
|
|
9893
|
+
description: 'List all web presences (SEO URL configurations) in the Shopify store with pagination. Web presences define how URLs are structured for different markets and locales. Each web presence can use either a custom domain (e.g., example.ca) or subfolders (e.g., /en-ca). Returns id, defaultLocale, alternateLocales, subfolderSuffix, domain, and rootUrls. 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:** create-web-presence, update-web-presence, get-market (to see web presences by market).',
|
|
9894
|
+
inputSchema: inputSchema48,
|
|
9895
|
+
outputSchema: outputSchema48,
|
|
9896
|
+
// AI Agent Optimization
|
|
9897
|
+
category: "market",
|
|
9898
|
+
relationships: {
|
|
9899
|
+
relatedTools: ["list-markets", "get-market"],
|
|
9900
|
+
followUps: ["create-web-presence", "update-web-presence", "delete-web-presence"]
|
|
9901
|
+
},
|
|
9902
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
9903
|
+
annotations: {
|
|
9904
|
+
readOnlyHint: true,
|
|
9905
|
+
destructiveHint: false,
|
|
9906
|
+
idempotentHint: true,
|
|
9907
|
+
openWorldHint: true
|
|
9908
|
+
}
|
|
9909
|
+
},
|
|
9910
|
+
handleListWebPresences
|
|
9911
|
+
);
|
|
9912
|
+
}
|
|
9913
|
+
|
|
9914
|
+
// src/tools/update-web-presence.ts
|
|
9915
|
+
import { z as z51 } from "zod";
|
|
9916
|
+
var inputSchema49 = z51.object({
|
|
9917
|
+
id: webPresenceIdSchema.describe(
|
|
9918
|
+
'Web presence GID to update (e.g., "gid://shopify/MarketWebPresence/123"). Use list-web-presences or get-market to find web presence IDs.'
|
|
9919
|
+
),
|
|
9920
|
+
defaultLocale: z51.string().optional().describe(
|
|
9921
|
+
'New default locale for the web presence. Use locale codes like "en-CA", "fr-FR", "de-DE". The locale must be enabled in shop settings.'
|
|
9922
|
+
),
|
|
9923
|
+
alternateLocales: z51.array(z51.string()).optional().describe(
|
|
9924
|
+
'New alternate locales (replaces existing). Example: ["fr-CA", "es-CA"]. Pass an empty array [] to remove all alternate locales. All locales must be enabled in shop settings.'
|
|
9925
|
+
),
|
|
9926
|
+
subfolderSuffix: z51.string().optional().describe(
|
|
9927
|
+
'New subfolder suffix (e.g., "canada" to change from /en-ca to /en-canada). Only ASCII characters allowed. Can only be changed for subfolder-based web presences, not domain-based.'
|
|
9928
|
+
)
|
|
9929
|
+
});
|
|
9930
|
+
var outputSchema49 = z51.object({
|
|
9931
|
+
id: z51.string().describe("Updated web presence GID"),
|
|
9932
|
+
defaultLocale: z51.object({
|
|
9933
|
+
locale: z51.string().describe("Locale code"),
|
|
9934
|
+
name: z51.string().describe("Locale display name")
|
|
9935
|
+
}),
|
|
9936
|
+
alternateLocales: z51.array(
|
|
9937
|
+
z51.object({
|
|
9938
|
+
locale: z51.string(),
|
|
9939
|
+
name: z51.string()
|
|
9940
|
+
})
|
|
9941
|
+
).optional(),
|
|
9942
|
+
subfolderSuffix: z51.string().optional().describe("Subfolder suffix if configured"),
|
|
9943
|
+
domain: z51.object({
|
|
9944
|
+
host: z51.string().describe("Domain hostname")
|
|
9945
|
+
}).optional().describe("Domain if configured")
|
|
9946
|
+
});
|
|
9947
|
+
var handleUpdateWebPresence = async (context, params) => {
|
|
9948
|
+
log.debug(`Updating web presence ${params.id} on shop: ${context.shopDomain}`);
|
|
9949
|
+
const input = {};
|
|
9950
|
+
if (params.defaultLocale !== void 0) {
|
|
9951
|
+
input.defaultLocale = params.defaultLocale;
|
|
9952
|
+
}
|
|
9953
|
+
if (params.alternateLocales !== void 0) {
|
|
9954
|
+
input.alternateLocales = params.alternateLocales;
|
|
9955
|
+
}
|
|
9956
|
+
if (params.subfolderSuffix !== void 0) {
|
|
9957
|
+
input.subfolderSuffix = params.subfolderSuffix;
|
|
9958
|
+
}
|
|
9959
|
+
const webPresence = await updateWebPresence(params.id, input);
|
|
9960
|
+
log.debug(`Updated web presence: ${webPresence.id}`);
|
|
9961
|
+
return webPresence;
|
|
9962
|
+
};
|
|
9963
|
+
function registerUpdateWebPresenceTool() {
|
|
9964
|
+
registerContextAwareTool(
|
|
9965
|
+
{
|
|
9966
|
+
name: "update-web-presence",
|
|
9967
|
+
title: "Update Web Presence",
|
|
9968
|
+
description: "Update an existing web presence configuration. Allows changing defaultLocale, alternateLocales (replaces existing), and subfolderSuffix. Only provided fields are updated; others remain unchanged. Note: subfolderSuffix can only be changed for subfolder-based web presences. **Prerequisites:** list-web-presences or get-market to find web presence IDs. **Follow-ups:** list-web-presences, get-market.",
|
|
9969
|
+
inputSchema: inputSchema49,
|
|
9970
|
+
outputSchema: outputSchema49,
|
|
9971
|
+
// AI Agent Optimization
|
|
9972
|
+
category: "market",
|
|
9973
|
+
relationships: {
|
|
9974
|
+
relatedTools: ["list-web-presences", "get-market"],
|
|
9975
|
+
followUps: ["list-web-presences", "get-market"]
|
|
9976
|
+
},
|
|
9977
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
9978
|
+
annotations: {
|
|
9979
|
+
readOnlyHint: false,
|
|
9980
|
+
destructiveHint: false,
|
|
9981
|
+
idempotentHint: true,
|
|
9982
|
+
// Same update with same params produces same result
|
|
9983
|
+
openWorldHint: true
|
|
9984
|
+
}
|
|
9985
|
+
},
|
|
9986
|
+
handleUpdateWebPresence
|
|
9987
|
+
);
|
|
9988
|
+
}
|
|
9989
|
+
|
|
9990
|
+
// src/tools/disable-shop-locale.ts
|
|
9991
|
+
import { z as z52 } from "zod";
|
|
9992
|
+
|
|
9993
|
+
// src/shopify/translations.ts
|
|
9994
|
+
var LIST_SHOP_LOCALES_QUERY = `
|
|
9995
|
+
query ShopLocales($published: Boolean) {
|
|
9996
|
+
shopLocales(published: $published) {
|
|
9997
|
+
locale
|
|
9998
|
+
name
|
|
9999
|
+
primary
|
|
10000
|
+
published
|
|
10001
|
+
}
|
|
10002
|
+
}
|
|
10003
|
+
`;
|
|
10004
|
+
var SHOP_LOCALE_ENABLE_MUTATION = `
|
|
10005
|
+
mutation ShopLocaleEnable($locale: String!, $marketWebPresenceIds: [ID!]) {
|
|
10006
|
+
shopLocaleEnable(locale: $locale, marketWebPresenceIds: $marketWebPresenceIds) {
|
|
10007
|
+
shopLocale {
|
|
10008
|
+
locale
|
|
10009
|
+
name
|
|
10010
|
+
published
|
|
10011
|
+
}
|
|
10012
|
+
userErrors { field message }
|
|
10013
|
+
}
|
|
10014
|
+
}
|
|
10015
|
+
`;
|
|
10016
|
+
var SHOP_LOCALE_DISABLE_MUTATION = `
|
|
10017
|
+
mutation ShopLocaleDisable($locale: String!) {
|
|
10018
|
+
shopLocaleDisable(locale: $locale) {
|
|
10019
|
+
locale
|
|
10020
|
+
userErrors { field message }
|
|
10021
|
+
}
|
|
10022
|
+
}
|
|
10023
|
+
`;
|
|
10024
|
+
var TRANSLATIONS_REGISTER_MUTATION = `
|
|
10025
|
+
mutation TranslationsRegister($resourceId: ID!, $translations: [TranslationInput!]!) {
|
|
10026
|
+
translationsRegister(resourceId: $resourceId, translations: $translations) {
|
|
10027
|
+
translations {
|
|
10028
|
+
key
|
|
10029
|
+
value
|
|
10030
|
+
locale
|
|
10031
|
+
}
|
|
10032
|
+
userErrors { field message }
|
|
10033
|
+
}
|
|
10034
|
+
}
|
|
10035
|
+
`;
|
|
10036
|
+
var TRANSLATIONS_REMOVE_MUTATION = `
|
|
10037
|
+
mutation TranslationsRemove($resourceId: ID!, $translationKeys: [String!]!, $locales: [String!]!) {
|
|
10038
|
+
translationsRemove(resourceId: $resourceId, translationKeys: $translationKeys, locales: $locales) {
|
|
10039
|
+
translations { key locale }
|
|
10040
|
+
userErrors { field message }
|
|
10041
|
+
}
|
|
10042
|
+
}
|
|
10043
|
+
`;
|
|
10044
|
+
async function listShopLocales(options = {}) {
|
|
10045
|
+
const client = await getShopifyClient();
|
|
10046
|
+
log.debug(`Listing shop locales: published=${options.published}`);
|
|
10047
|
+
const response = await withRateLimit(
|
|
10048
|
+
() => client.graphql.request(LIST_SHOP_LOCALES_QUERY, {
|
|
10049
|
+
variables: { published: options.published }
|
|
10050
|
+
}),
|
|
10051
|
+
"list-shop-locales"
|
|
10052
|
+
);
|
|
10053
|
+
if (response.errors && response.errors.length > 0) {
|
|
10054
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
10055
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
10056
|
+
}
|
|
10057
|
+
const locales = response.data?.shopLocales;
|
|
10058
|
+
if (!locales) {
|
|
10059
|
+
throw new Error("No response data from shopLocales query");
|
|
10060
|
+
}
|
|
10061
|
+
log.debug(`Listed ${locales.length} shop locales`);
|
|
10062
|
+
return locales;
|
|
10063
|
+
}
|
|
10064
|
+
async function enableShopLocale(locale, marketWebPresenceIds) {
|
|
10065
|
+
const client = await getShopifyClient();
|
|
10066
|
+
log.debug(`Enabling shop locale: ${locale}`);
|
|
10067
|
+
const variables = { locale };
|
|
10068
|
+
if (marketWebPresenceIds && marketWebPresenceIds.length > 0) {
|
|
10069
|
+
variables.marketWebPresenceIds = marketWebPresenceIds;
|
|
10070
|
+
}
|
|
10071
|
+
const response = await withRateLimit(
|
|
10072
|
+
() => client.graphql.request(SHOP_LOCALE_ENABLE_MUTATION, {
|
|
10073
|
+
variables
|
|
10074
|
+
}),
|
|
10075
|
+
"enable-shop-locale"
|
|
10076
|
+
);
|
|
10077
|
+
if (response.errors && response.errors.length > 0) {
|
|
10078
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
10079
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
10080
|
+
}
|
|
10081
|
+
const result = response.data?.shopLocaleEnable;
|
|
10082
|
+
if (!result) {
|
|
10083
|
+
throw new Error("No response data from shopLocaleEnable mutation");
|
|
10084
|
+
}
|
|
10085
|
+
if (result.userErrors && result.userErrors.length > 0) {
|
|
10086
|
+
throw new ShopifyUserErrorException(result.userErrors);
|
|
10087
|
+
}
|
|
10088
|
+
if (!result.shopLocale) {
|
|
10089
|
+
throw new Error("Shop locale enable succeeded but no locale was returned");
|
|
10090
|
+
}
|
|
10091
|
+
log.debug(`Enabled shop locale: ${result.shopLocale.locale}`);
|
|
10092
|
+
return result.shopLocale;
|
|
10093
|
+
}
|
|
10094
|
+
async function disableShopLocale(locale) {
|
|
10095
|
+
const client = await getShopifyClient();
|
|
10096
|
+
log.debug(`Disabling shop locale: ${locale}`);
|
|
10097
|
+
const response = await withRateLimit(
|
|
10098
|
+
() => client.graphql.request(SHOP_LOCALE_DISABLE_MUTATION, {
|
|
10099
|
+
variables: { locale }
|
|
10100
|
+
}),
|
|
10101
|
+
"disable-shop-locale"
|
|
10102
|
+
);
|
|
10103
|
+
if (response.errors && response.errors.length > 0) {
|
|
10104
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
10105
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
10106
|
+
}
|
|
10107
|
+
const result = response.data?.shopLocaleDisable;
|
|
10108
|
+
if (!result) {
|
|
10109
|
+
throw new Error("No response data from shopLocaleDisable mutation");
|
|
10110
|
+
}
|
|
10111
|
+
if (result.userErrors && result.userErrors.length > 0) {
|
|
10112
|
+
throw new ShopifyUserErrorException(result.userErrors);
|
|
10113
|
+
}
|
|
10114
|
+
if (!result.locale) {
|
|
10115
|
+
throw new Error("Shop locale disable succeeded but no locale was returned");
|
|
10116
|
+
}
|
|
10117
|
+
log.debug(`Disabled shop locale: ${result.locale}`);
|
|
10118
|
+
return result.locale;
|
|
10119
|
+
}
|
|
10120
|
+
async function registerTranslations(resourceId, translations) {
|
|
10121
|
+
const client = await getShopifyClient();
|
|
10122
|
+
log.debug(`Registering ${translations.length} translations for resource: ${resourceId}`);
|
|
10123
|
+
const response = await withRateLimit(
|
|
10124
|
+
() => client.graphql.request(TRANSLATIONS_REGISTER_MUTATION, {
|
|
10125
|
+
variables: { resourceId, translations }
|
|
10126
|
+
}),
|
|
10127
|
+
"register-translations"
|
|
10128
|
+
);
|
|
10129
|
+
if (response.errors && response.errors.length > 0) {
|
|
10130
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
10131
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
10132
|
+
}
|
|
10133
|
+
const result = response.data?.translationsRegister;
|
|
10134
|
+
if (!result) {
|
|
10135
|
+
throw new Error("No response data from translationsRegister mutation");
|
|
10136
|
+
}
|
|
10137
|
+
if (result.userErrors && result.userErrors.length > 0) {
|
|
10138
|
+
throw new ShopifyUserErrorException(result.userErrors);
|
|
10139
|
+
}
|
|
10140
|
+
log.debug(`Registered ${result.translations.length} translations`);
|
|
10141
|
+
return result.translations;
|
|
10142
|
+
}
|
|
10143
|
+
async function removeTranslations(resourceId, translationKeys, locales) {
|
|
10144
|
+
const client = await getShopifyClient();
|
|
10145
|
+
log.debug(
|
|
10146
|
+
`Removing translations for resource: ${resourceId}, keys: ${translationKeys.join(", ")}, locales: ${locales.join(", ")}`
|
|
10147
|
+
);
|
|
10148
|
+
const response = await withRateLimit(
|
|
10149
|
+
() => client.graphql.request(TRANSLATIONS_REMOVE_MUTATION, {
|
|
10150
|
+
variables: { resourceId, translationKeys, locales }
|
|
10151
|
+
}),
|
|
10152
|
+
"remove-translations"
|
|
10153
|
+
);
|
|
10154
|
+
if (response.errors && response.errors.length > 0) {
|
|
10155
|
+
const errorMessage = response.errors.map((e) => e.message).join(", ");
|
|
10156
|
+
throw new Error(`GraphQL error: ${errorMessage}`);
|
|
10157
|
+
}
|
|
10158
|
+
const result = response.data?.translationsRemove;
|
|
10159
|
+
if (!result) {
|
|
10160
|
+
throw new Error("No response data from translationsRemove mutation");
|
|
10161
|
+
}
|
|
10162
|
+
if (result.userErrors && result.userErrors.length > 0) {
|
|
10163
|
+
throw new ShopifyUserErrorException(result.userErrors);
|
|
10164
|
+
}
|
|
10165
|
+
log.debug(`Removed ${result.translations.length} translations`);
|
|
10166
|
+
return result.translations;
|
|
10167
|
+
}
|
|
10168
|
+
|
|
10169
|
+
// src/tools/disable-shop-locale.ts
|
|
10170
|
+
var inputSchema50 = z52.object({
|
|
10171
|
+
locale: localeCodeSchema.describe(
|
|
10172
|
+
'Locale code to disable (required). Use ISO 639-1 format (e.g., "fr", "de", "es") or with region (e.g., "fr-CA", "pt-BR"). \u26A0\uFE0F WARNING: Cannot disable the primary locale. Attempting to do so will result in an error.'
|
|
10173
|
+
)
|
|
10174
|
+
});
|
|
10175
|
+
var outputSchema50 = z52.object({
|
|
10176
|
+
disabledLocale: z52.string().describe("The locale code that was disabled"),
|
|
10177
|
+
warning: z52.string().describe("Warning about translation removal")
|
|
10178
|
+
});
|
|
10179
|
+
var handleDisableShopLocale = async (context, params) => {
|
|
10180
|
+
log.debug(`Disabling shop locale "${params.locale}" on shop: ${context.shopDomain}`);
|
|
10181
|
+
const disabledLocale = await disableShopLocale(params.locale);
|
|
10182
|
+
log.debug(`Disabled shop locale: ${disabledLocale}`);
|
|
10183
|
+
return {
|
|
10184
|
+
disabledLocale,
|
|
10185
|
+
warning: `Locale "${disabledLocale}" has been disabled. All translations for this locale have been removed.`
|
|
10186
|
+
};
|
|
10187
|
+
};
|
|
10188
|
+
function registerDisableShopLocaleTool() {
|
|
10189
|
+
registerContextAwareTool(
|
|
10190
|
+
{
|
|
10191
|
+
name: "disable-shop-locale",
|
|
10192
|
+
title: "Disable Shop Locale",
|
|
10193
|
+
description: 'Disable a shop locale (language) for the Shopify store. Provide a locale code (e.g., "fr", "de", "es") to disable that language. \u26A0\uFE0F **WARNING:** Disabling a locale will permanently remove ALL translations for that language. \u26A0\uFE0F **Cannot disable the primary locale.** Use list-shop-locales to see which locale is primary. Returns the disabled locale code with a warning about translation removal. **Prerequisites:** list-shop-locales to verify the locale exists and is not primary. **Follow-ups:** list-shop-locales to confirm removal.',
|
|
10194
|
+
inputSchema: inputSchema50,
|
|
10195
|
+
outputSchema: outputSchema50,
|
|
10196
|
+
// AI Agent Optimization
|
|
10197
|
+
category: "translation",
|
|
10198
|
+
relationships: {
|
|
10199
|
+
relatedTools: ["list-shop-locales", "enable-shop-locale"],
|
|
10200
|
+
prerequisites: ["list-shop-locales"],
|
|
10201
|
+
followUps: ["list-shop-locales"]
|
|
10202
|
+
},
|
|
10203
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
10204
|
+
annotations: {
|
|
10205
|
+
readOnlyHint: false,
|
|
10206
|
+
destructiveHint: true,
|
|
10207
|
+
// Removes all translations for the locale
|
|
10208
|
+
idempotentHint: false,
|
|
10209
|
+
// Disabling twice will error
|
|
10210
|
+
openWorldHint: true
|
|
10211
|
+
}
|
|
10212
|
+
},
|
|
10213
|
+
handleDisableShopLocale
|
|
10214
|
+
);
|
|
10215
|
+
}
|
|
10216
|
+
|
|
10217
|
+
// src/tools/enable-shop-locale.ts
|
|
10218
|
+
import { z as z53 } from "zod";
|
|
10219
|
+
var inputSchema51 = z53.object({
|
|
10220
|
+
locale: localeCodeSchema.describe(
|
|
10221
|
+
'Locale code to enable (required). Use ISO 639-1 format (e.g., "fr", "de", "es") or with region (e.g., "fr-CA", "pt-BR"). Common codes: fr (French), de (German), es (Spanish), it (Italian), ja (Japanese), zh (Chinese).'
|
|
10222
|
+
),
|
|
10223
|
+
marketWebPresenceIds: z53.array(webPresenceIdSchema).optional().describe(
|
|
10224
|
+
'Optional array of MarketWebPresence GIDs to associate this locale with. Use list-web-presences to get available web presence IDs. Example: ["gid://shopify/MarketWebPresence/123"]'
|
|
10225
|
+
)
|
|
10226
|
+
});
|
|
10227
|
+
var outputSchema51 = z53.object({
|
|
10228
|
+
locale: z53.string().describe("Enabled locale code"),
|
|
10229
|
+
name: z53.string().describe("Display name of the locale"),
|
|
10230
|
+
published: z53.boolean().describe("Whether the locale is published")
|
|
10231
|
+
});
|
|
10232
|
+
var handleEnableShopLocale = async (context, params) => {
|
|
10233
|
+
log.debug(`Enabling shop locale "${params.locale}" on shop: ${context.shopDomain}`);
|
|
10234
|
+
const result = await enableShopLocale(params.locale, params.marketWebPresenceIds);
|
|
10235
|
+
log.debug(`Enabled shop locale: ${result.locale}`);
|
|
10236
|
+
return result;
|
|
10237
|
+
};
|
|
10238
|
+
function registerEnableShopLocaleTool() {
|
|
10239
|
+
registerContextAwareTool(
|
|
10240
|
+
{
|
|
10241
|
+
name: "enable-shop-locale",
|
|
10242
|
+
title: "Enable Shop Locale",
|
|
10243
|
+
description: 'Enable a new shop locale (language) for the Shopify store. Provide a locale code (e.g., "fr", "de", "es", "fr-CA") to enable that language. Optionally associate with specific web presences for market-specific localization. Returns the enabled locale with locale code, name, and published status. **Note:** Enabling a locale makes it available for translations, but content still needs to be translated. **Typical workflow:** list-shop-locales \u2192 enable-shop-locale \u2192 register-translations. **Prerequisites:** list-shop-locales to see current locales. **Follow-ups:** register-translations to add translated content.',
|
|
10244
|
+
inputSchema: inputSchema51,
|
|
10245
|
+
outputSchema: outputSchema51,
|
|
10246
|
+
// AI Agent Optimization
|
|
10247
|
+
category: "translation",
|
|
10248
|
+
relationships: {
|
|
10249
|
+
relatedTools: ["list-shop-locales", "disable-shop-locale"],
|
|
10250
|
+
prerequisites: ["list-shop-locales"],
|
|
10251
|
+
followUps: ["register-translations"]
|
|
10252
|
+
},
|
|
10253
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
10254
|
+
annotations: {
|
|
10255
|
+
readOnlyHint: false,
|
|
10256
|
+
destructiveHint: false,
|
|
10257
|
+
idempotentHint: true,
|
|
10258
|
+
// Enabling same locale twice is idempotent
|
|
10259
|
+
openWorldHint: true
|
|
10260
|
+
}
|
|
10261
|
+
},
|
|
10262
|
+
handleEnableShopLocale
|
|
10263
|
+
);
|
|
10264
|
+
}
|
|
10265
|
+
|
|
10266
|
+
// src/tools/list-shop-locales.ts
|
|
10267
|
+
import { z as z54 } from "zod";
|
|
10268
|
+
var inputSchema52 = z54.object({
|
|
10269
|
+
published: z54.boolean().optional().describe(
|
|
10270
|
+
"If true, only return published locales (visible to customers). If false or omitted, returns all enabled locales including unpublished ones."
|
|
10271
|
+
)
|
|
10272
|
+
});
|
|
10273
|
+
var outputSchema52 = z54.object({
|
|
10274
|
+
locales: z54.array(
|
|
10275
|
+
z54.object({
|
|
10276
|
+
locale: z54.string().describe('Locale code (e.g., "en", "fr", "de", "fr-CA")'),
|
|
10277
|
+
name: z54.string().describe('Display name (e.g., "English", "French", "German")'),
|
|
10278
|
+
primary: z54.boolean().describe("Whether this is the primary locale"),
|
|
10279
|
+
published: z54.boolean().describe("Whether this locale is published (visible to customers)")
|
|
10280
|
+
})
|
|
10281
|
+
),
|
|
10282
|
+
summary: z54.object({
|
|
10283
|
+
totalLocales: z54.number().describe("Total number of locales returned"),
|
|
10284
|
+
primaryLocale: z54.string().describe("The primary locale code"),
|
|
10285
|
+
publishedCount: z54.number().describe("Number of published locales"),
|
|
10286
|
+
hint: z54.string().describe("Suggestion for next action")
|
|
10287
|
+
}).describe("AI-friendly summary for context management")
|
|
10288
|
+
});
|
|
10289
|
+
var handleListShopLocales = async (context, params) => {
|
|
10290
|
+
log.debug(`Listing shop locales on shop: ${context.shopDomain}`);
|
|
10291
|
+
const locales = await listShopLocales({ published: params.published });
|
|
10292
|
+
log.debug(`Listed ${locales.length} shop locales via tool`);
|
|
10293
|
+
const primaryLocale = locales.find((l) => l.primary)?.locale || "unknown";
|
|
10294
|
+
const publishedCount = locales.filter((l) => l.published).length;
|
|
10295
|
+
return {
|
|
10296
|
+
locales,
|
|
10297
|
+
summary: {
|
|
10298
|
+
totalLocales: locales.length,
|
|
10299
|
+
primaryLocale,
|
|
10300
|
+
publishedCount,
|
|
10301
|
+
hint: locales.length === 0 ? "No locales found. Shop may not have multi-language enabled." : locales.length === 1 ? "Only primary locale enabled. Use enable-shop-locale to add more languages." : `Shop has ${locales.length} locales (${publishedCount} published). Use enable-shop-locale to add more, or register-translations to add content.`
|
|
10302
|
+
}
|
|
10303
|
+
};
|
|
10304
|
+
};
|
|
10305
|
+
function registerListShopLocalesTool() {
|
|
10306
|
+
registerContextAwareTool(
|
|
10307
|
+
{
|
|
10308
|
+
name: "list-shop-locales",
|
|
10309
|
+
title: "List Shop Locales",
|
|
10310
|
+
description: "List all enabled shop locales (languages) for the Shopify store. Returns locale code, name, primary flag, and published flag for each locale. Use published=true to filter to only customer-visible locales. Response includes summary with totalLocales, primaryLocale, publishedCount. **Typical workflow:** list-shop-locales \u2192 enable-shop-locale (add language) \u2192 register-translations (add content). **Prerequisites:** Store must have multi-language feature enabled. **Follow-ups:** enable-shop-locale, register-translations.",
|
|
10311
|
+
inputSchema: inputSchema52,
|
|
10312
|
+
outputSchema: outputSchema52,
|
|
10313
|
+
// AI Agent Optimization
|
|
10314
|
+
category: "translation",
|
|
10315
|
+
relationships: {
|
|
10316
|
+
relatedTools: ["enable-shop-locale", "disable-shop-locale"],
|
|
10317
|
+
followUps: ["enable-shop-locale", "register-translations"]
|
|
10318
|
+
},
|
|
10319
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
10320
|
+
annotations: {
|
|
10321
|
+
readOnlyHint: true,
|
|
10322
|
+
destructiveHint: false,
|
|
10323
|
+
idempotentHint: true,
|
|
10324
|
+
openWorldHint: true
|
|
10325
|
+
}
|
|
10326
|
+
},
|
|
10327
|
+
handleListShopLocales
|
|
10328
|
+
);
|
|
10329
|
+
}
|
|
10330
|
+
|
|
10331
|
+
// src/tools/register-translations.ts
|
|
10332
|
+
import { z as z55 } from "zod";
|
|
10333
|
+
var translationInputSchema = z55.object({
|
|
10334
|
+
locale: localeCodeSchema.describe('Target locale code (e.g., "fr", "de", "fr-CA")'),
|
|
10335
|
+
key: z55.string().min(1).describe(
|
|
10336
|
+
'Translation key (field identifier). Common keys: "title", "body_html", "description", "meta_title", "meta_description", "handle". Query the resource to see available translatable fields.'
|
|
10337
|
+
),
|
|
10338
|
+
value: z55.string().describe("Translated value for this field"),
|
|
10339
|
+
translatableContentDigest: z55.string().min(1).describe(
|
|
10340
|
+
"Content digest hash from the translatable resource (required). This ensures the translation is linked to the correct version of the source content. Query the resource's translatableContent to get this value."
|
|
10341
|
+
),
|
|
10342
|
+
marketId: z55.string().optional().describe(
|
|
10343
|
+
'Optional market GID for market-specific translations. Use list-markets to get market IDs. Example: "gid://shopify/Market/123"'
|
|
10344
|
+
)
|
|
10345
|
+
});
|
|
10346
|
+
var inputSchema53 = z55.object({
|
|
10347
|
+
resourceId: gidSchema().describe(
|
|
10348
|
+
'GID of the translatable resource (required). Examples: "gid://shopify/Product/123", "gid://shopify/Collection/456", "gid://shopify/Page/789", "gid://shopify/Article/321"'
|
|
10349
|
+
),
|
|
10350
|
+
translations: z55.array(translationInputSchema).min(1).max(100).describe(
|
|
10351
|
+
"Array of translations to register (1-100 items). Each translation must include locale, key, value, and translatableContentDigest."
|
|
10352
|
+
)
|
|
10353
|
+
});
|
|
10354
|
+
var outputSchema53 = z55.object({
|
|
10355
|
+
translations: z55.array(
|
|
10356
|
+
z55.object({
|
|
10357
|
+
key: z55.string().describe("Translation key"),
|
|
10358
|
+
value: z55.string().describe("Translated value"),
|
|
10359
|
+
locale: z55.string().describe("Target locale")
|
|
10360
|
+
})
|
|
10361
|
+
),
|
|
10362
|
+
summary: z55.object({
|
|
10363
|
+
registered: z55.number().describe("Number of translations registered"),
|
|
10364
|
+
resourceId: z55.string().describe("Resource that was translated"),
|
|
10365
|
+
locales: z55.array(z55.string()).describe("Unique locales translated")
|
|
10366
|
+
}).describe("AI-friendly summary")
|
|
10367
|
+
});
|
|
10368
|
+
var handleRegisterTranslations = async (context, params) => {
|
|
10369
|
+
log.debug(
|
|
10370
|
+
`Registering ${params.translations.length} translations for resource: ${params.resourceId} on shop: ${context.shopDomain}`
|
|
10371
|
+
);
|
|
10372
|
+
const translations = await registerTranslations(params.resourceId, params.translations);
|
|
10373
|
+
log.debug(`Registered ${translations.length} translations`);
|
|
10374
|
+
const locales = [...new Set(translations.map((t) => t.locale))];
|
|
10375
|
+
return {
|
|
10376
|
+
translations,
|
|
10377
|
+
summary: {
|
|
10378
|
+
registered: translations.length,
|
|
10379
|
+
resourceId: params.resourceId,
|
|
10380
|
+
locales
|
|
10381
|
+
}
|
|
10382
|
+
};
|
|
10383
|
+
};
|
|
10384
|
+
function registerRegisterTranslationsTool() {
|
|
10385
|
+
registerContextAwareTool(
|
|
10386
|
+
{
|
|
10387
|
+
name: "register-translations",
|
|
10388
|
+
title: "Register Translations",
|
|
10389
|
+
description: "Register translations for a translatable Shopify resource. Creates or updates translations for products, collections, pages, articles, etc. Requires resourceId (GID) and array of translations with locale, key, value, and translatableContentDigest. \u26A0\uFE0F **Important:** The translatableContentDigest is required. This hash ensures translations match the source content version. Query the resource's translatableContent field to get digests for each translatable field. **Common keys:** title, body_html, description, meta_title, meta_description, handle. **Typical workflow:** get-product \u2192 query translatableResource \u2192 register-translations. **Prerequisites:** enable-shop-locale (ensure target locale is enabled), get-product/get-page (get resource and content digests). **Follow-ups:** Query resource to verify translations.",
|
|
10390
|
+
inputSchema: inputSchema53,
|
|
10391
|
+
outputSchema: outputSchema53,
|
|
10392
|
+
// AI Agent Optimization
|
|
10393
|
+
category: "translation",
|
|
10394
|
+
relationships: {
|
|
10395
|
+
relatedTools: ["remove-translations", "list-shop-locales"],
|
|
10396
|
+
prerequisites: ["enable-shop-locale", "get-product"],
|
|
10397
|
+
followUps: ["get-product"]
|
|
10398
|
+
},
|
|
10399
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
10400
|
+
annotations: {
|
|
10401
|
+
readOnlyHint: false,
|
|
10402
|
+
destructiveHint: false,
|
|
10403
|
+
idempotentHint: true,
|
|
10404
|
+
// Registering same translation twice updates it
|
|
10405
|
+
openWorldHint: true
|
|
10406
|
+
}
|
|
10407
|
+
},
|
|
10408
|
+
handleRegisterTranslations
|
|
10409
|
+
);
|
|
10410
|
+
}
|
|
10411
|
+
|
|
10412
|
+
// src/tools/remove-translations.ts
|
|
10413
|
+
import { z as z56 } from "zod";
|
|
10414
|
+
var inputSchema54 = z56.object({
|
|
10415
|
+
resourceId: gidSchema().describe(
|
|
10416
|
+
'GID of the translatable resource (required). Examples: "gid://shopify/Product/123", "gid://shopify/Collection/456", "gid://shopify/Page/789", "gid://shopify/Article/321"'
|
|
10417
|
+
),
|
|
10418
|
+
translationKeys: z56.array(z56.string().min(1)).min(1).describe(
|
|
10419
|
+
'Array of translation keys to remove (required). Common keys: "title", "body_html", "description", "meta_title", "meta_description". Example: ["title", "body_html"]'
|
|
10420
|
+
),
|
|
10421
|
+
locales: z56.array(localeCodeSchema).min(1).describe(
|
|
10422
|
+
'Array of locale codes to remove translations for (required). Example: ["fr", "de"] to remove French and German translations.'
|
|
10423
|
+
)
|
|
10424
|
+
});
|
|
10425
|
+
var outputSchema54 = z56.object({
|
|
10426
|
+
removed: z56.array(
|
|
10427
|
+
z56.object({
|
|
10428
|
+
key: z56.string().describe("Removed translation key"),
|
|
10429
|
+
locale: z56.string().describe("Removed locale")
|
|
10430
|
+
})
|
|
10431
|
+
),
|
|
10432
|
+
summary: z56.object({
|
|
10433
|
+
removedCount: z56.number().describe("Number of translations removed"),
|
|
10434
|
+
resourceId: z56.string().describe("Resource that was updated"),
|
|
10435
|
+
locales: z56.array(z56.string()).describe("Locales affected"),
|
|
10436
|
+
keys: z56.array(z56.string()).describe("Keys removed")
|
|
10437
|
+
}).describe("AI-friendly summary")
|
|
10438
|
+
});
|
|
10439
|
+
var handleRemoveTranslations = async (context, params) => {
|
|
10440
|
+
log.debug(
|
|
10441
|
+
`Removing translations for resource: ${params.resourceId}, keys: ${params.translationKeys.join(", ")}, locales: ${params.locales.join(", ")} on shop: ${context.shopDomain}`
|
|
10442
|
+
);
|
|
10443
|
+
const removed = await removeTranslations(
|
|
10444
|
+
params.resourceId,
|
|
10445
|
+
params.translationKeys,
|
|
10446
|
+
params.locales
|
|
10447
|
+
);
|
|
10448
|
+
log.debug(`Removed ${removed.length} translations`);
|
|
10449
|
+
return {
|
|
10450
|
+
removed,
|
|
10451
|
+
summary: {
|
|
10452
|
+
removedCount: removed.length,
|
|
10453
|
+
resourceId: params.resourceId,
|
|
10454
|
+
locales: params.locales,
|
|
10455
|
+
keys: params.translationKeys
|
|
10456
|
+
}
|
|
10457
|
+
};
|
|
10458
|
+
};
|
|
10459
|
+
function registerRemoveTranslationsTool() {
|
|
10460
|
+
registerContextAwareTool(
|
|
10461
|
+
{
|
|
10462
|
+
name: "remove-translations",
|
|
10463
|
+
title: "Remove Translations",
|
|
10464
|
+
description: 'Remove translations from a translatable Shopify resource. Deletes specified translations by key and locale from products, collections, pages, articles, etc. Requires resourceId (GID), translationKeys array, and locales array. \u26A0\uFE0F **Warning:** This permanently deletes the specified translations. This action cannot be undone. Returns array of removed translations (key + locale pairs). **Example:** Remove French and German translations for title and description: translationKeys=["title", "body_html"], locales=["fr", "de"]. **Prerequisites:** Query resource to verify which translations exist. **Follow-ups:** Query resource to confirm removal.',
|
|
10465
|
+
inputSchema: inputSchema54,
|
|
10466
|
+
outputSchema: outputSchema54,
|
|
10467
|
+
// AI Agent Optimization
|
|
10468
|
+
category: "translation",
|
|
10469
|
+
relationships: {
|
|
10470
|
+
relatedTools: ["register-translations", "list-shop-locales"],
|
|
10471
|
+
followUps: ["get-product"]
|
|
10472
|
+
},
|
|
10473
|
+
// MCP Tool Annotations (Epic 9.5 - MCP Best Practices)
|
|
10474
|
+
annotations: {
|
|
10475
|
+
readOnlyHint: false,
|
|
10476
|
+
destructiveHint: true,
|
|
10477
|
+
// Permanently removes translations
|
|
10478
|
+
idempotentHint: false,
|
|
10479
|
+
// Removing twice may error or behave differently
|
|
10480
|
+
openWorldHint: true
|
|
10481
|
+
}
|
|
10482
|
+
},
|
|
10483
|
+
handleRemoveTranslations
|
|
10484
|
+
);
|
|
10485
|
+
}
|
|
10486
|
+
|
|
9350
10487
|
// src/tools/index.ts
|
|
9351
10488
|
function setupToolHandlers(server) {
|
|
9352
10489
|
server.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -9448,6 +10585,15 @@ function registerAllTools(server) {
|
|
|
9448
10585
|
registerGetMarketTool();
|
|
9449
10586
|
registerListMarketsTool();
|
|
9450
10587
|
registerUpdateMarketTool();
|
|
10588
|
+
registerCreateWebPresenceTool();
|
|
10589
|
+
registerDeleteWebPresenceTool();
|
|
10590
|
+
registerListWebPresencesTool();
|
|
10591
|
+
registerUpdateWebPresenceTool();
|
|
10592
|
+
registerDisableShopLocaleTool();
|
|
10593
|
+
registerEnableShopLocaleTool();
|
|
10594
|
+
registerListShopLocalesTool();
|
|
10595
|
+
registerRegisterTranslationsTool();
|
|
10596
|
+
registerRemoveTranslationsTool();
|
|
9451
10597
|
const toolCount = getRegisteredTools().length;
|
|
9452
10598
|
log.debug(`Tool registration complete: ${toolCount} tools registered`);
|
|
9453
10599
|
}
|