@anton.andrusenko/shopify-mcp-admin 0.7.0 → 0.8.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 +76 -5
- package/dist/index.js +1006 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @anton.andrusenko/shopify-mcp-admin
|
|
2
2
|
|
|
3
|
-
> 🛍️ **MCP Server for Shopify Admin API** — Enable AI agents to manage Shopify stores with
|
|
3
|
+
> 🛍️ **MCP Server for Shopify Admin API** — Enable AI agents to manage Shopify stores with 79 powerful tools
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@anton.andrusenko/shopify-mcp-admin)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## ✨ Features
|
|
13
13
|
|
|
14
|
-
- 🛠️ **
|
|
14
|
+
- 🛠️ **79 MCP Tools** — Complete store management: products, inventory, collections, pages, blogs, redirects, metafields, markets, locales & translations
|
|
15
15
|
- 🤖 **AI-Optimized** — Tool descriptions and error messages designed for LLM comprehension
|
|
16
16
|
- 🔌 **Dual Transport** — STDIO for Claude Desktop, HTTP for ChatGPT/OpenAI
|
|
17
17
|
- ⚡ **Rate Limiting** — Automatic retry with exponential backoff for Shopify API limits
|
|
@@ -250,14 +250,60 @@ Each tool can be converted to OpenAI function format:
|
|
|
250
250
|
|
|
251
251
|
## 🛠️ Available Tools
|
|
252
252
|
|
|
253
|
-
@anton.andrusenko/shopify-mcp-admin provides **
|
|
253
|
+
@anton.andrusenko/shopify-mcp-admin provides **79 tools** organized into 16 categories:
|
|
254
254
|
|
|
255
255
|
<details>
|
|
256
|
-
<summary><strong>🏪 Store Info (
|
|
256
|
+
<summary><strong>🏪 Store Info (9 tools)</strong></summary>
|
|
257
257
|
|
|
258
258
|
| Tool | Description |
|
|
259
259
|
|------|-------------|
|
|
260
260
|
| `get-store-info` | Get basic store information (name, domain, plan, currency, timezone, email) |
|
|
261
|
+
| `get-store-limits` | Get store resource limits (max variants, max options, location limit) |
|
|
262
|
+
| `get-store-features` | Get feature flags (gift cards, reports, storefront, bundles, subscriptions) |
|
|
263
|
+
| `get-store-currencies` | Get multi-currency configuration (base currency, presentment currencies, formats) |
|
|
264
|
+
| `get-store-shipping` | Get shipping configuration (ships-to countries, countries in shipping zones) |
|
|
265
|
+
| `get-store-domain` | Get primary domain configuration (hostname, URL, SSL status) |
|
|
266
|
+
| `get-store-taxes` | Get tax configuration (taxes included in prices, tax shipping) |
|
|
267
|
+
| `get-store-policies` | Get legal policies (privacy, terms of service, refund policy) |
|
|
268
|
+
| `get-store-alerts` | Get admin alerts and setup requirements |
|
|
269
|
+
|
|
270
|
+
#### Example: Get Store Limits
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"maxProductVariants": 2000,
|
|
275
|
+
"maxProductOptions": 3,
|
|
276
|
+
"locationLimit": 1000,
|
|
277
|
+
"redirectLimitReached": false
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### Example: Get Store Features
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"giftCards": true,
|
|
286
|
+
"reports": true,
|
|
287
|
+
"storefront": true,
|
|
288
|
+
"bundles": {
|
|
289
|
+
"eligibleForBundles": true
|
|
290
|
+
},
|
|
291
|
+
"sellsSubscriptions": false
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### Example: Get Store Currencies
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"currencyCode": "USD",
|
|
300
|
+
"enabledPresentmentCurrencies": ["USD", "EUR", "GBP", "CAD"],
|
|
301
|
+
"currencyFormats": {
|
|
302
|
+
"moneyFormat": "${{amount}}",
|
|
303
|
+
"moneyWithCurrencyFormat": "${{amount}} USD"
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
261
307
|
|
|
262
308
|
</details>
|
|
263
309
|
|
|
@@ -497,13 +543,38 @@ npm run inspect:config
|
|
|
497
543
|
|
|
498
544
|
MCP Inspector opens a web UI at `http://localhost:6274` where you can:
|
|
499
545
|
|
|
500
|
-
- 📋 Browse all
|
|
546
|
+
- 📋 Browse all 79 registered tools with schemas
|
|
547
|
+
- 📊 Access 9 MCP resources for store context
|
|
501
548
|
- ▶️ Execute tools interactively and view results
|
|
502
549
|
- 🔍 Inspect JSON-RPC protocol messages
|
|
503
550
|
- 📊 Monitor server events in real-time
|
|
504
551
|
|
|
505
552
|
> **Tip:** Use `npm run inspect:dev` during development for instant feedback without rebuilding.
|
|
506
553
|
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## 📊 MCP Resources
|
|
557
|
+
|
|
558
|
+
The MCP server exposes comprehensive store context information via MCP resources. These are read-only data sources that AI agents can query for context.
|
|
559
|
+
|
|
560
|
+
### Extended Store Resources
|
|
561
|
+
|
|
562
|
+
| Resource URI | Description |
|
|
563
|
+
|--------------|-------------|
|
|
564
|
+
| `shopify://store/info` | Basic store info (name, domain, plan, currency, timezone) |
|
|
565
|
+
| `shopify://store/limits` | Resource limits (max variants, max options, location limit) |
|
|
566
|
+
| `shopify://store/features` | Feature flags (gift cards, reports, storefront, bundles) |
|
|
567
|
+
| `shopify://store/currencies` | Multi-currency configuration (base currency, presentment currencies) |
|
|
568
|
+
| `shopify://store/shipping` | Shipping configuration (ships-to countries, shipping zones) |
|
|
569
|
+
| `shopify://store/domain` | Primary domain configuration (hostname, URL, SSL status) |
|
|
570
|
+
| `shopify://store/taxes` | Tax configuration (taxes included, tax shipping) |
|
|
571
|
+
| `shopify://store/policies` | Legal policies (privacy, terms of service, refund) |
|
|
572
|
+
| `shopify://store/alerts` | Admin alerts and setup requirements |
|
|
573
|
+
|
|
574
|
+
> **Note:** For MCP clients that don't support resources (e.g., Claude Desktop), equivalent tools are available. See **Store Info Tools** below.
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
507
578
|
### FAQ
|
|
508
579
|
|
|
509
580
|
**Q: Can I connect to multiple stores?**
|
package/dist/index.js
CHANGED
|
@@ -917,9 +917,351 @@ var PRODUCTS_QUERY = `
|
|
|
917
917
|
}
|
|
918
918
|
}
|
|
919
919
|
`;
|
|
920
|
+
var STORE_LIMITS_QUERY = `
|
|
921
|
+
query ShopResourceLimits {
|
|
922
|
+
shop {
|
|
923
|
+
resourceLimits {
|
|
924
|
+
maxProductVariants
|
|
925
|
+
maxProductOptions
|
|
926
|
+
locationLimit
|
|
927
|
+
redirectLimitReached
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
`;
|
|
932
|
+
var STORE_FEATURES_QUERY = `
|
|
933
|
+
query ShopFeatures {
|
|
934
|
+
shop {
|
|
935
|
+
features {
|
|
936
|
+
giftCards
|
|
937
|
+
reports
|
|
938
|
+
storefront
|
|
939
|
+
bundles {
|
|
940
|
+
eligibleForBundles
|
|
941
|
+
}
|
|
942
|
+
sellsSubscriptions
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
`;
|
|
947
|
+
var STORE_CURRENCIES_QUERY = `
|
|
948
|
+
query ShopCurrencies {
|
|
949
|
+
shop {
|
|
950
|
+
currencyCode
|
|
951
|
+
enabledPresentmentCurrencies
|
|
952
|
+
currencyFormats {
|
|
953
|
+
moneyFormat
|
|
954
|
+
moneyWithCurrencyFormat
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
`;
|
|
959
|
+
var STORE_SHIPPING_QUERY = `
|
|
960
|
+
query ShopShipping {
|
|
961
|
+
shop {
|
|
962
|
+
shipsToCountries
|
|
963
|
+
countriesInShippingZones {
|
|
964
|
+
countryCodes
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
`;
|
|
969
|
+
var STORE_DOMAIN_QUERY = `
|
|
970
|
+
query ShopDomain {
|
|
971
|
+
shop {
|
|
972
|
+
primaryDomain {
|
|
973
|
+
host
|
|
974
|
+
url
|
|
975
|
+
sslEnabled
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
`;
|
|
980
|
+
var STORE_TAXES_QUERY = `
|
|
981
|
+
query ShopTaxes {
|
|
982
|
+
shop {
|
|
983
|
+
taxesIncluded
|
|
984
|
+
taxShipping
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
`;
|
|
988
|
+
var STORE_POLICIES_QUERY = `
|
|
989
|
+
query ShopPolicies {
|
|
990
|
+
shop {
|
|
991
|
+
shopPolicies {
|
|
992
|
+
title
|
|
993
|
+
type
|
|
994
|
+
url
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
`;
|
|
999
|
+
var STORE_ALERTS_QUERY = `
|
|
1000
|
+
query ShopAlerts {
|
|
1001
|
+
shop {
|
|
1002
|
+
alerts {
|
|
1003
|
+
action {
|
|
1004
|
+
title
|
|
1005
|
+
url
|
|
1006
|
+
}
|
|
1007
|
+
description
|
|
1008
|
+
}
|
|
1009
|
+
setupRequired
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
`;
|
|
920
1013
|
|
|
921
|
-
// src/shopify/store-info.ts
|
|
1014
|
+
// src/shopify/store-info-extended.ts
|
|
922
1015
|
var DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1016
|
+
var ALERTS_CACHE_TTL_MS = 60 * 1e3;
|
|
1017
|
+
var _cachedStoreLimits = null;
|
|
1018
|
+
var _limitsTimestamp = 0;
|
|
1019
|
+
var _cachedStoreFeatures = null;
|
|
1020
|
+
var _featuresTimestamp = 0;
|
|
1021
|
+
var _cachedStoreCurrencies = null;
|
|
1022
|
+
var _currenciesTimestamp = 0;
|
|
1023
|
+
var _cachedStoreShipping = null;
|
|
1024
|
+
var _shippingTimestamp = 0;
|
|
1025
|
+
var _cachedStoreDomain = null;
|
|
1026
|
+
var _domainTimestamp = 0;
|
|
1027
|
+
var _cachedStoreTaxes = null;
|
|
1028
|
+
var _taxesTimestamp = 0;
|
|
1029
|
+
var _cachedStorePolicies = null;
|
|
1030
|
+
var _policiesTimestamp = 0;
|
|
1031
|
+
var _cachedStoreAlerts = null;
|
|
1032
|
+
var _alertsTimestamp = 0;
|
|
1033
|
+
async function getStoreLimits() {
|
|
1034
|
+
const now = Date.now();
|
|
1035
|
+
const config = getConfig();
|
|
1036
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
1037
|
+
if (_cachedStoreLimits && now - _limitsTimestamp < ttl) {
|
|
1038
|
+
log.debug("Returning cached store limits");
|
|
1039
|
+
return _cachedStoreLimits;
|
|
1040
|
+
}
|
|
1041
|
+
log.debug("Fetching fresh store limits from Shopify");
|
|
1042
|
+
const client = await getShopifyClient();
|
|
1043
|
+
const response = await withRateLimit(
|
|
1044
|
+
() => client.graphql.request(STORE_LIMITS_QUERY),
|
|
1045
|
+
"getStoreLimits"
|
|
1046
|
+
);
|
|
1047
|
+
if (response.errors && response.errors.length > 0) {
|
|
1048
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1049
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1050
|
+
}
|
|
1051
|
+
if (!response.data?.shop?.resourceLimits) {
|
|
1052
|
+
throw new Error("Invalid response: missing shop.resourceLimits data");
|
|
1053
|
+
}
|
|
1054
|
+
_cachedStoreLimits = response.data.shop.resourceLimits;
|
|
1055
|
+
_limitsTimestamp = now;
|
|
1056
|
+
return _cachedStoreLimits;
|
|
1057
|
+
}
|
|
1058
|
+
async function getStoreFeatures() {
|
|
1059
|
+
const now = Date.now();
|
|
1060
|
+
const config = getConfig();
|
|
1061
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
1062
|
+
if (_cachedStoreFeatures && now - _featuresTimestamp < ttl) {
|
|
1063
|
+
log.debug("Returning cached store features");
|
|
1064
|
+
return _cachedStoreFeatures;
|
|
1065
|
+
}
|
|
1066
|
+
log.debug("Fetching fresh store features from Shopify");
|
|
1067
|
+
const client = await getShopifyClient();
|
|
1068
|
+
const response = await withRateLimit(
|
|
1069
|
+
() => client.graphql.request(STORE_FEATURES_QUERY),
|
|
1070
|
+
"getStoreFeatures"
|
|
1071
|
+
);
|
|
1072
|
+
if (response.errors && response.errors.length > 0) {
|
|
1073
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1074
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1075
|
+
}
|
|
1076
|
+
if (!response.data?.shop?.features) {
|
|
1077
|
+
throw new Error("Invalid response: missing shop.features data");
|
|
1078
|
+
}
|
|
1079
|
+
_cachedStoreFeatures = response.data.shop.features;
|
|
1080
|
+
_featuresTimestamp = now;
|
|
1081
|
+
return _cachedStoreFeatures;
|
|
1082
|
+
}
|
|
1083
|
+
async function getStoreCurrencies() {
|
|
1084
|
+
const now = Date.now();
|
|
1085
|
+
const config = getConfig();
|
|
1086
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
1087
|
+
if (_cachedStoreCurrencies && now - _currenciesTimestamp < ttl) {
|
|
1088
|
+
log.debug("Returning cached store currencies");
|
|
1089
|
+
return _cachedStoreCurrencies;
|
|
1090
|
+
}
|
|
1091
|
+
log.debug("Fetching fresh store currencies from Shopify");
|
|
1092
|
+
const client = await getShopifyClient();
|
|
1093
|
+
const response = await withRateLimit(
|
|
1094
|
+
() => client.graphql.request(STORE_CURRENCIES_QUERY),
|
|
1095
|
+
"getStoreCurrencies"
|
|
1096
|
+
);
|
|
1097
|
+
if (response.errors && response.errors.length > 0) {
|
|
1098
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1099
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1100
|
+
}
|
|
1101
|
+
if (!response.data?.shop) {
|
|
1102
|
+
throw new Error("Invalid response: missing shop data");
|
|
1103
|
+
}
|
|
1104
|
+
const { currencyCode, enabledPresentmentCurrencies, currencyFormats } = response.data.shop;
|
|
1105
|
+
_cachedStoreCurrencies = {
|
|
1106
|
+
currencyCode,
|
|
1107
|
+
enabledPresentmentCurrencies,
|
|
1108
|
+
currencyFormats
|
|
1109
|
+
};
|
|
1110
|
+
_currenciesTimestamp = now;
|
|
1111
|
+
return _cachedStoreCurrencies;
|
|
1112
|
+
}
|
|
1113
|
+
async function getStoreShipping() {
|
|
1114
|
+
const now = Date.now();
|
|
1115
|
+
const config = getConfig();
|
|
1116
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
1117
|
+
if (_cachedStoreShipping && now - _shippingTimestamp < ttl) {
|
|
1118
|
+
log.debug("Returning cached store shipping");
|
|
1119
|
+
return _cachedStoreShipping;
|
|
1120
|
+
}
|
|
1121
|
+
log.debug("Fetching fresh store shipping from Shopify");
|
|
1122
|
+
const client = await getShopifyClient();
|
|
1123
|
+
const response = await withRateLimit(
|
|
1124
|
+
() => client.graphql.request(STORE_SHIPPING_QUERY),
|
|
1125
|
+
"getStoreShipping"
|
|
1126
|
+
);
|
|
1127
|
+
if (response.errors && response.errors.length > 0) {
|
|
1128
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1129
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1130
|
+
}
|
|
1131
|
+
if (!response.data?.shop) {
|
|
1132
|
+
throw new Error("Invalid response: missing shop data");
|
|
1133
|
+
}
|
|
1134
|
+
_cachedStoreShipping = {
|
|
1135
|
+
shipsToCountries: response.data.shop.shipsToCountries,
|
|
1136
|
+
countriesInShippingZones: response.data.shop.countriesInShippingZones
|
|
1137
|
+
};
|
|
1138
|
+
_shippingTimestamp = now;
|
|
1139
|
+
return _cachedStoreShipping;
|
|
1140
|
+
}
|
|
1141
|
+
async function getStoreDomain() {
|
|
1142
|
+
const now = Date.now();
|
|
1143
|
+
const config = getConfig();
|
|
1144
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
1145
|
+
if (_cachedStoreDomain && now - _domainTimestamp < ttl) {
|
|
1146
|
+
log.debug("Returning cached store domain");
|
|
1147
|
+
return _cachedStoreDomain;
|
|
1148
|
+
}
|
|
1149
|
+
log.debug("Fetching fresh store domain from Shopify");
|
|
1150
|
+
const client = await getShopifyClient();
|
|
1151
|
+
const response = await withRateLimit(
|
|
1152
|
+
() => client.graphql.request(STORE_DOMAIN_QUERY),
|
|
1153
|
+
"getStoreDomain"
|
|
1154
|
+
);
|
|
1155
|
+
if (response.errors && response.errors.length > 0) {
|
|
1156
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1157
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1158
|
+
}
|
|
1159
|
+
if (!response.data?.shop?.primaryDomain) {
|
|
1160
|
+
throw new Error("Invalid response: missing shop.primaryDomain data");
|
|
1161
|
+
}
|
|
1162
|
+
_cachedStoreDomain = {
|
|
1163
|
+
primaryDomain: response.data.shop.primaryDomain
|
|
1164
|
+
};
|
|
1165
|
+
_domainTimestamp = now;
|
|
1166
|
+
return _cachedStoreDomain;
|
|
1167
|
+
}
|
|
1168
|
+
async function getStoreTaxes() {
|
|
1169
|
+
const now = Date.now();
|
|
1170
|
+
const config = getConfig();
|
|
1171
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
1172
|
+
if (_cachedStoreTaxes && now - _taxesTimestamp < ttl) {
|
|
1173
|
+
log.debug("Returning cached store taxes");
|
|
1174
|
+
return _cachedStoreTaxes;
|
|
1175
|
+
}
|
|
1176
|
+
log.debug("Fetching fresh store taxes from Shopify");
|
|
1177
|
+
const client = await getShopifyClient();
|
|
1178
|
+
const response = await withRateLimit(
|
|
1179
|
+
() => client.graphql.request(STORE_TAXES_QUERY),
|
|
1180
|
+
"getStoreTaxes"
|
|
1181
|
+
);
|
|
1182
|
+
if (response.errors && response.errors.length > 0) {
|
|
1183
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1184
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1185
|
+
}
|
|
1186
|
+
if (response.data?.shop === void 0 || response.data?.shop === null) {
|
|
1187
|
+
throw new Error("Invalid response: missing shop data");
|
|
1188
|
+
}
|
|
1189
|
+
_cachedStoreTaxes = {
|
|
1190
|
+
taxesIncluded: response.data.shop.taxesIncluded,
|
|
1191
|
+
taxShipping: response.data.shop.taxShipping
|
|
1192
|
+
};
|
|
1193
|
+
_taxesTimestamp = now;
|
|
1194
|
+
return _cachedStoreTaxes;
|
|
1195
|
+
}
|
|
1196
|
+
async function getStorePolicies() {
|
|
1197
|
+
const now = Date.now();
|
|
1198
|
+
const config = getConfig();
|
|
1199
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS;
|
|
1200
|
+
if (_cachedStorePolicies && now - _policiesTimestamp < ttl) {
|
|
1201
|
+
log.debug("Returning cached store policies");
|
|
1202
|
+
return _cachedStorePolicies;
|
|
1203
|
+
}
|
|
1204
|
+
try {
|
|
1205
|
+
log.debug("Fetching fresh store policies from Shopify");
|
|
1206
|
+
const client = await getShopifyClient();
|
|
1207
|
+
const response = await withRateLimit(
|
|
1208
|
+
() => client.graphql.request(STORE_POLICIES_QUERY),
|
|
1209
|
+
"getStorePolicies"
|
|
1210
|
+
);
|
|
1211
|
+
if (response.errors && response.errors.length > 0) {
|
|
1212
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1213
|
+
if (errorMessages.toLowerCase().includes("access denied") || errorMessages.toLowerCase().includes("unauthorized")) {
|
|
1214
|
+
log.debug("read_legal_policies scope not available, returning empty policies");
|
|
1215
|
+
_cachedStorePolicies = { shopPolicies: [] };
|
|
1216
|
+
_policiesTimestamp = now;
|
|
1217
|
+
return _cachedStorePolicies;
|
|
1218
|
+
}
|
|
1219
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1220
|
+
}
|
|
1221
|
+
const policies = response.data?.shop?.shopPolicies ?? [];
|
|
1222
|
+
_cachedStorePolicies = { shopPolicies: policies };
|
|
1223
|
+
_policiesTimestamp = now;
|
|
1224
|
+
return _cachedStorePolicies;
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
if (error instanceof Error && (error.message.toLowerCase().includes("access denied") || error.message.toLowerCase().includes("unauthorized"))) {
|
|
1227
|
+
log.debug("read_legal_policies scope not available, returning empty policies");
|
|
1228
|
+
_cachedStorePolicies = { shopPolicies: [] };
|
|
1229
|
+
_policiesTimestamp = now;
|
|
1230
|
+
return _cachedStorePolicies;
|
|
1231
|
+
}
|
|
1232
|
+
throw error;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
async function getStoreAlerts() {
|
|
1236
|
+
const now = Date.now();
|
|
1237
|
+
const ttl = ALERTS_CACHE_TTL_MS;
|
|
1238
|
+
if (_cachedStoreAlerts && now - _alertsTimestamp < ttl) {
|
|
1239
|
+
log.debug("Returning cached store alerts");
|
|
1240
|
+
return _cachedStoreAlerts;
|
|
1241
|
+
}
|
|
1242
|
+
log.debug("Fetching fresh store alerts from Shopify");
|
|
1243
|
+
const client = await getShopifyClient();
|
|
1244
|
+
const response = await withRateLimit(
|
|
1245
|
+
() => client.graphql.request(STORE_ALERTS_QUERY),
|
|
1246
|
+
"getStoreAlerts"
|
|
1247
|
+
);
|
|
1248
|
+
if (response.errors && response.errors.length > 0) {
|
|
1249
|
+
const errorMessages = response.errors.map((e) => e.message).join(", ");
|
|
1250
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1251
|
+
}
|
|
1252
|
+
if (response.data?.shop === void 0 || response.data?.shop === null) {
|
|
1253
|
+
throw new Error("Invalid response: missing shop data");
|
|
1254
|
+
}
|
|
1255
|
+
_cachedStoreAlerts = {
|
|
1256
|
+
alerts: response.data.shop.alerts ?? [],
|
|
1257
|
+
setupRequired: response.data.shop.setupRequired
|
|
1258
|
+
};
|
|
1259
|
+
_alertsTimestamp = now;
|
|
1260
|
+
return _cachedStoreAlerts;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/shopify/store-info.ts
|
|
1264
|
+
var DEFAULT_CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
923
1265
|
var _cachedStoreInfo = null;
|
|
924
1266
|
var _cacheTimestamp = 0;
|
|
925
1267
|
function transformShopResponse(shop) {
|
|
@@ -939,7 +1281,7 @@ function transformShopResponse(shop) {
|
|
|
939
1281
|
async function getStoreInfo() {
|
|
940
1282
|
const now = Date.now();
|
|
941
1283
|
const config = getConfig();
|
|
942
|
-
const ttl = config.STORE_INFO_CACHE_TTL_MS ??
|
|
1284
|
+
const ttl = config.STORE_INFO_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS2;
|
|
943
1285
|
if (_cachedStoreInfo && now - _cacheTimestamp < ttl) {
|
|
944
1286
|
log.debug("Returning cached store info");
|
|
945
1287
|
return _cachedStoreInfo;
|
|
@@ -1009,54 +1351,358 @@ function createServer() {
|
|
|
1009
1351
|
const server = new McpServer(
|
|
1010
1352
|
// Server info (sent to clients in initialize response)
|
|
1011
1353
|
{
|
|
1012
|
-
name: SERVER_NAME,
|
|
1013
|
-
version: getServerVersion()
|
|
1354
|
+
name: SERVER_NAME,
|
|
1355
|
+
version: getServerVersion()
|
|
1356
|
+
},
|
|
1357
|
+
// Server options (capabilities for handler enforcement)
|
|
1358
|
+
{
|
|
1359
|
+
capabilities: {
|
|
1360
|
+
tools: {},
|
|
1361
|
+
// Enable tools capability (AC-2.1.4)
|
|
1362
|
+
resources: {},
|
|
1363
|
+
// Enable resources capability (AC-2.1.4)
|
|
1364
|
+
logging: {}
|
|
1365
|
+
// Enable logging capability (AC-9.5.1.2)
|
|
1366
|
+
},
|
|
1367
|
+
instructions: SERVER_INSTRUCTIONS
|
|
1368
|
+
// Provide Shopify context to AI agents (AC-9.5.1.1)
|
|
1369
|
+
}
|
|
1370
|
+
);
|
|
1371
|
+
return server;
|
|
1372
|
+
}
|
|
1373
|
+
function registerResources(server) {
|
|
1374
|
+
server.resource(
|
|
1375
|
+
"store-info",
|
|
1376
|
+
"shopify://store/info",
|
|
1377
|
+
{
|
|
1378
|
+
description: "Basic information about the connected Shopify store including name, domain, plan, currency, and timezone. Use this to understand the store context before performing operations.",
|
|
1379
|
+
mimeType: "application/json"
|
|
1380
|
+
},
|
|
1381
|
+
async () => {
|
|
1382
|
+
try {
|
|
1383
|
+
const storeInfo = await getStoreInfo();
|
|
1384
|
+
return {
|
|
1385
|
+
contents: [
|
|
1386
|
+
{
|
|
1387
|
+
uri: "shopify://store/info",
|
|
1388
|
+
mimeType: "application/json",
|
|
1389
|
+
text: JSON.stringify(storeInfo, null, 2)
|
|
1390
|
+
}
|
|
1391
|
+
]
|
|
1392
|
+
};
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
log.error("Failed to fetch store info", error instanceof Error ? error : void 0);
|
|
1395
|
+
return {
|
|
1396
|
+
contents: [
|
|
1397
|
+
{
|
|
1398
|
+
uri: "shopify://store/info",
|
|
1399
|
+
mimeType: "application/json",
|
|
1400
|
+
text: JSON.stringify({
|
|
1401
|
+
error: "Failed to fetch store information",
|
|
1402
|
+
message: sanitizeLogMessage(
|
|
1403
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1404
|
+
)
|
|
1405
|
+
})
|
|
1406
|
+
}
|
|
1407
|
+
]
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
);
|
|
1412
|
+
server.resource(
|
|
1413
|
+
"store-limits",
|
|
1414
|
+
"shopify://store/limits",
|
|
1415
|
+
{
|
|
1416
|
+
description: "Resource limits for the Shopify store including max product variants, max product options, location limit, and redirect limit status. Use this to validate operations against store constraints.",
|
|
1417
|
+
mimeType: "application/json"
|
|
1418
|
+
},
|
|
1419
|
+
async () => {
|
|
1420
|
+
try {
|
|
1421
|
+
const limits = await getStoreLimits();
|
|
1422
|
+
return {
|
|
1423
|
+
contents: [
|
|
1424
|
+
{
|
|
1425
|
+
uri: "shopify://store/limits",
|
|
1426
|
+
mimeType: "application/json",
|
|
1427
|
+
text: JSON.stringify(limits, null, 2)
|
|
1428
|
+
}
|
|
1429
|
+
]
|
|
1430
|
+
};
|
|
1431
|
+
} catch (error) {
|
|
1432
|
+
log.error("Failed to fetch store limits", error instanceof Error ? error : void 0);
|
|
1433
|
+
return {
|
|
1434
|
+
contents: [
|
|
1435
|
+
{
|
|
1436
|
+
uri: "shopify://store/limits",
|
|
1437
|
+
mimeType: "application/json",
|
|
1438
|
+
text: JSON.stringify({
|
|
1439
|
+
error: "Failed to fetch store limits",
|
|
1440
|
+
message: sanitizeLogMessage(
|
|
1441
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1442
|
+
)
|
|
1443
|
+
})
|
|
1444
|
+
}
|
|
1445
|
+
]
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
);
|
|
1450
|
+
server.resource(
|
|
1451
|
+
"store-features",
|
|
1452
|
+
"shopify://store/features",
|
|
1453
|
+
{
|
|
1454
|
+
description: "Feature flags for the Shopify store including gift cards, reports, storefront, bundles eligibility, and subscriptions. Use this to determine available store capabilities.",
|
|
1455
|
+
mimeType: "application/json"
|
|
1456
|
+
},
|
|
1457
|
+
async () => {
|
|
1458
|
+
try {
|
|
1459
|
+
const features = await getStoreFeatures();
|
|
1460
|
+
return {
|
|
1461
|
+
contents: [
|
|
1462
|
+
{
|
|
1463
|
+
uri: "shopify://store/features",
|
|
1464
|
+
mimeType: "application/json",
|
|
1465
|
+
text: JSON.stringify(features, null, 2)
|
|
1466
|
+
}
|
|
1467
|
+
]
|
|
1468
|
+
};
|
|
1469
|
+
} catch (error) {
|
|
1470
|
+
log.error("Failed to fetch store features", error instanceof Error ? error : void 0);
|
|
1471
|
+
return {
|
|
1472
|
+
contents: [
|
|
1473
|
+
{
|
|
1474
|
+
uri: "shopify://store/features",
|
|
1475
|
+
mimeType: "application/json",
|
|
1476
|
+
text: JSON.stringify({
|
|
1477
|
+
error: "Failed to fetch store features",
|
|
1478
|
+
message: sanitizeLogMessage(
|
|
1479
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1480
|
+
)
|
|
1481
|
+
})
|
|
1482
|
+
}
|
|
1483
|
+
]
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
);
|
|
1488
|
+
server.resource(
|
|
1489
|
+
"store-currencies",
|
|
1490
|
+
"shopify://store/currencies",
|
|
1491
|
+
{
|
|
1492
|
+
description: "Currency configuration for the Shopify store including base currency, enabled presentment currencies, and money format patterns. Use this to understand multi-currency setup.",
|
|
1493
|
+
mimeType: "application/json"
|
|
1494
|
+
},
|
|
1495
|
+
async () => {
|
|
1496
|
+
try {
|
|
1497
|
+
const currencies = await getStoreCurrencies();
|
|
1498
|
+
return {
|
|
1499
|
+
contents: [
|
|
1500
|
+
{
|
|
1501
|
+
uri: "shopify://store/currencies",
|
|
1502
|
+
mimeType: "application/json",
|
|
1503
|
+
text: JSON.stringify(currencies, null, 2)
|
|
1504
|
+
}
|
|
1505
|
+
]
|
|
1506
|
+
};
|
|
1507
|
+
} catch (error) {
|
|
1508
|
+
log.error("Failed to fetch store currencies", error instanceof Error ? error : void 0);
|
|
1509
|
+
return {
|
|
1510
|
+
contents: [
|
|
1511
|
+
{
|
|
1512
|
+
uri: "shopify://store/currencies",
|
|
1513
|
+
mimeType: "application/json",
|
|
1514
|
+
text: JSON.stringify({
|
|
1515
|
+
error: "Failed to fetch store currencies",
|
|
1516
|
+
message: sanitizeLogMessage(
|
|
1517
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1518
|
+
)
|
|
1519
|
+
})
|
|
1520
|
+
}
|
|
1521
|
+
]
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
);
|
|
1526
|
+
server.resource(
|
|
1527
|
+
"store-shipping",
|
|
1528
|
+
"shopify://store/shipping",
|
|
1529
|
+
{
|
|
1530
|
+
description: "Shipping configuration for the Shopify store including ships-to countries and countries with configured shipping zones. Use this to understand international shipping capabilities.",
|
|
1531
|
+
mimeType: "application/json"
|
|
1532
|
+
},
|
|
1533
|
+
async () => {
|
|
1534
|
+
try {
|
|
1535
|
+
const shipping = await getStoreShipping();
|
|
1536
|
+
return {
|
|
1537
|
+
contents: [
|
|
1538
|
+
{
|
|
1539
|
+
uri: "shopify://store/shipping",
|
|
1540
|
+
mimeType: "application/json",
|
|
1541
|
+
text: JSON.stringify(shipping, null, 2)
|
|
1542
|
+
}
|
|
1543
|
+
]
|
|
1544
|
+
};
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
log.error("Failed to fetch store shipping", error instanceof Error ? error : void 0);
|
|
1547
|
+
return {
|
|
1548
|
+
contents: [
|
|
1549
|
+
{
|
|
1550
|
+
uri: "shopify://store/shipping",
|
|
1551
|
+
mimeType: "application/json",
|
|
1552
|
+
text: JSON.stringify({
|
|
1553
|
+
error: "Failed to fetch store shipping",
|
|
1554
|
+
message: sanitizeLogMessage(
|
|
1555
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1556
|
+
)
|
|
1557
|
+
})
|
|
1558
|
+
}
|
|
1559
|
+
]
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1564
|
+
server.resource(
|
|
1565
|
+
"store-domain",
|
|
1566
|
+
"shopify://store/domain",
|
|
1567
|
+
{
|
|
1568
|
+
description: "Primary domain configuration for the Shopify store including hostname, URL, and SSL status. Use this to construct proper URLs and understand store identity.",
|
|
1569
|
+
mimeType: "application/json"
|
|
1014
1570
|
},
|
|
1015
|
-
|
|
1571
|
+
async () => {
|
|
1572
|
+
try {
|
|
1573
|
+
const domain = await getStoreDomain();
|
|
1574
|
+
return {
|
|
1575
|
+
contents: [
|
|
1576
|
+
{
|
|
1577
|
+
uri: "shopify://store/domain",
|
|
1578
|
+
mimeType: "application/json",
|
|
1579
|
+
text: JSON.stringify(domain, null, 2)
|
|
1580
|
+
}
|
|
1581
|
+
]
|
|
1582
|
+
};
|
|
1583
|
+
} catch (error) {
|
|
1584
|
+
log.error("Failed to fetch store domain", error instanceof Error ? error : void 0);
|
|
1585
|
+
return {
|
|
1586
|
+
contents: [
|
|
1587
|
+
{
|
|
1588
|
+
uri: "shopify://store/domain",
|
|
1589
|
+
mimeType: "application/json",
|
|
1590
|
+
text: JSON.stringify({
|
|
1591
|
+
error: "Failed to fetch store domain",
|
|
1592
|
+
message: sanitizeLogMessage(
|
|
1593
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1594
|
+
)
|
|
1595
|
+
})
|
|
1596
|
+
}
|
|
1597
|
+
]
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
);
|
|
1602
|
+
server.resource(
|
|
1603
|
+
"store-taxes",
|
|
1604
|
+
"shopify://store/taxes",
|
|
1016
1605
|
{
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1606
|
+
description: "Tax configuration for the Shopify store including whether taxes are included in prices and tax shipping settings. Use this to understand pricing structure for tax calculations.",
|
|
1607
|
+
mimeType: "application/json"
|
|
1608
|
+
},
|
|
1609
|
+
async () => {
|
|
1610
|
+
try {
|
|
1611
|
+
const taxes = await getStoreTaxes();
|
|
1612
|
+
return {
|
|
1613
|
+
contents: [
|
|
1614
|
+
{
|
|
1615
|
+
uri: "shopify://store/taxes",
|
|
1616
|
+
mimeType: "application/json",
|
|
1617
|
+
text: JSON.stringify(taxes, null, 2)
|
|
1618
|
+
}
|
|
1619
|
+
]
|
|
1620
|
+
};
|
|
1621
|
+
} catch (error) {
|
|
1622
|
+
log.error("Failed to fetch store taxes", error instanceof Error ? error : void 0);
|
|
1623
|
+
return {
|
|
1624
|
+
contents: [
|
|
1625
|
+
{
|
|
1626
|
+
uri: "shopify://store/taxes",
|
|
1627
|
+
mimeType: "application/json",
|
|
1628
|
+
text: JSON.stringify({
|
|
1629
|
+
error: "Failed to fetch store taxes",
|
|
1630
|
+
message: sanitizeLogMessage(
|
|
1631
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1632
|
+
)
|
|
1633
|
+
})
|
|
1634
|
+
}
|
|
1635
|
+
]
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1027
1638
|
}
|
|
1028
1639
|
);
|
|
1029
|
-
return server;
|
|
1030
|
-
}
|
|
1031
|
-
function registerResources(server) {
|
|
1032
1640
|
server.resource(
|
|
1033
|
-
"store-
|
|
1034
|
-
"shopify://store/
|
|
1641
|
+
"store-policies",
|
|
1642
|
+
"shopify://store/policies",
|
|
1035
1643
|
{
|
|
1036
|
-
description: "
|
|
1644
|
+
description: "Legal policies for the Shopify store (privacy policy, terms of service, refund policy). Returns empty array if read_legal_policies scope is not available. Use this to reference store policies in customer communications.",
|
|
1037
1645
|
mimeType: "application/json"
|
|
1038
1646
|
},
|
|
1039
1647
|
async () => {
|
|
1040
1648
|
try {
|
|
1041
|
-
const
|
|
1649
|
+
const policies = await getStorePolicies();
|
|
1042
1650
|
return {
|
|
1043
1651
|
contents: [
|
|
1044
1652
|
{
|
|
1045
|
-
uri: "shopify://store/
|
|
1653
|
+
uri: "shopify://store/policies",
|
|
1046
1654
|
mimeType: "application/json",
|
|
1047
|
-
text: JSON.stringify(
|
|
1655
|
+
text: JSON.stringify(policies, null, 2)
|
|
1048
1656
|
}
|
|
1049
1657
|
]
|
|
1050
1658
|
};
|
|
1051
1659
|
} catch (error) {
|
|
1052
|
-
log.error("Failed to fetch store
|
|
1660
|
+
log.error("Failed to fetch store policies", error instanceof Error ? error : void 0);
|
|
1053
1661
|
return {
|
|
1054
1662
|
contents: [
|
|
1055
1663
|
{
|
|
1056
|
-
uri: "shopify://store/
|
|
1664
|
+
uri: "shopify://store/policies",
|
|
1057
1665
|
mimeType: "application/json",
|
|
1058
1666
|
text: JSON.stringify({
|
|
1059
|
-
error: "Failed to fetch store
|
|
1667
|
+
error: "Failed to fetch store policies",
|
|
1668
|
+
message: sanitizeLogMessage(
|
|
1669
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1670
|
+
)
|
|
1671
|
+
})
|
|
1672
|
+
}
|
|
1673
|
+
]
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
);
|
|
1678
|
+
server.resource(
|
|
1679
|
+
"store-alerts",
|
|
1680
|
+
"shopify://store/alerts",
|
|
1681
|
+
{
|
|
1682
|
+
description: "Active admin alerts and setup requirements for the Shopify store. Use this to proactively identify issues needing merchant attention.",
|
|
1683
|
+
mimeType: "application/json"
|
|
1684
|
+
},
|
|
1685
|
+
async () => {
|
|
1686
|
+
try {
|
|
1687
|
+
const alerts = await getStoreAlerts();
|
|
1688
|
+
return {
|
|
1689
|
+
contents: [
|
|
1690
|
+
{
|
|
1691
|
+
uri: "shopify://store/alerts",
|
|
1692
|
+
mimeType: "application/json",
|
|
1693
|
+
text: JSON.stringify(alerts, null, 2)
|
|
1694
|
+
}
|
|
1695
|
+
]
|
|
1696
|
+
};
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
log.error("Failed to fetch store alerts", error instanceof Error ? error : void 0);
|
|
1699
|
+
return {
|
|
1700
|
+
contents: [
|
|
1701
|
+
{
|
|
1702
|
+
uri: "shopify://store/alerts",
|
|
1703
|
+
mimeType: "application/json",
|
|
1704
|
+
text: JSON.stringify({
|
|
1705
|
+
error: "Failed to fetch store alerts",
|
|
1060
1706
|
message: sanitizeLogMessage(
|
|
1061
1707
|
error instanceof Error ? error.message : "Unknown error"
|
|
1062
1708
|
)
|
|
@@ -2451,7 +3097,7 @@ function wrapToolHandler(toolName, schema, handler) {
|
|
|
2451
3097
|
};
|
|
2452
3098
|
}
|
|
2453
3099
|
function registerTool(definition, handler, options = {}) {
|
|
2454
|
-
const { name, title, description, inputSchema:
|
|
3100
|
+
const { name, title, description, inputSchema: inputSchema80, annotations } = definition;
|
|
2455
3101
|
try {
|
|
2456
3102
|
if (!options.skipNameValidation) {
|
|
2457
3103
|
validateToolName(name);
|
|
@@ -2459,8 +3105,8 @@ function registerTool(definition, handler, options = {}) {
|
|
|
2459
3105
|
if (registeredTools.has(name)) {
|
|
2460
3106
|
throw new Error(`Tool "${name}" is already registered. Tool names must be unique.`);
|
|
2461
3107
|
}
|
|
2462
|
-
const jsonSchema = convertZodToJsonSchema(
|
|
2463
|
-
const wrappedHandler = wrapToolHandler(name,
|
|
3108
|
+
const jsonSchema = convertZodToJsonSchema(inputSchema80);
|
|
3109
|
+
const wrappedHandler = wrapToolHandler(name, inputSchema80, handler);
|
|
2464
3110
|
const finalAnnotations = {
|
|
2465
3111
|
...deriveDefaultAnnotations(name),
|
|
2466
3112
|
...annotations,
|
|
@@ -2472,7 +3118,7 @@ function registerTool(definition, handler, options = {}) {
|
|
|
2472
3118
|
title,
|
|
2473
3119
|
description,
|
|
2474
3120
|
inputSchema: jsonSchema,
|
|
2475
|
-
zodSchema:
|
|
3121
|
+
zodSchema: inputSchema80,
|
|
2476
3122
|
handler: wrappedHandler,
|
|
2477
3123
|
annotations: finalAnnotations
|
|
2478
3124
|
};
|
|
@@ -12560,20 +13206,180 @@ function registerSubmitRedirectImportTool() {
|
|
|
12560
13206
|
);
|
|
12561
13207
|
}
|
|
12562
13208
|
|
|
12563
|
-
// src/tools/get-store-
|
|
13209
|
+
// src/tools/get-store-alerts.ts
|
|
12564
13210
|
import { z as z73 } from "zod";
|
|
12565
13211
|
var inputSchema71 = z73.object({});
|
|
12566
13212
|
var outputSchema71 = z73.object({
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
|
|
12572
|
-
|
|
13213
|
+
alerts: z73.array(
|
|
13214
|
+
z73.object({
|
|
13215
|
+
action: z73.object({
|
|
13216
|
+
title: z73.string().describe('The text for the button in the alert (e.g., "Add credit card")'),
|
|
13217
|
+
url: z73.string().describe("The target URL that the button links to")
|
|
13218
|
+
}).describe("Action to resolve the alert"),
|
|
13219
|
+
description: z73.string().describe("Human-readable alert description")
|
|
13220
|
+
})
|
|
13221
|
+
).describe("Array of active admin alerts"),
|
|
13222
|
+
setupRequired: z73.boolean().describe("Whether store setup is incomplete")
|
|
13223
|
+
});
|
|
13224
|
+
async function handleGetStoreAlerts(context, _params) {
|
|
13225
|
+
log.debug(`Getting store alerts for shop: ${context.shopDomain}`);
|
|
13226
|
+
return await getStoreAlerts();
|
|
13227
|
+
}
|
|
13228
|
+
function registerGetStoreAlertsTool() {
|
|
13229
|
+
registerContextAwareTool(
|
|
13230
|
+
{
|
|
13231
|
+
name: "get-store-alerts",
|
|
13232
|
+
title: "Get Store Alerts",
|
|
13233
|
+
description: "Get active admin alerts and setup requirements for the connected Shopify store. **Prerequisites:** None. **Use this** to proactively identify issues needing merchant attention.",
|
|
13234
|
+
inputSchema: inputSchema71,
|
|
13235
|
+
outputSchema: outputSchema71,
|
|
13236
|
+
category: "store",
|
|
13237
|
+
relationships: {
|
|
13238
|
+
relatedTools: ["get-store-info", "list-low-inventory"],
|
|
13239
|
+
followUps: []
|
|
13240
|
+
},
|
|
13241
|
+
annotations: {
|
|
13242
|
+
readOnlyHint: true,
|
|
13243
|
+
destructiveHint: false,
|
|
13244
|
+
idempotentHint: true,
|
|
13245
|
+
openWorldHint: true
|
|
13246
|
+
}
|
|
13247
|
+
},
|
|
13248
|
+
handleGetStoreAlerts
|
|
13249
|
+
);
|
|
13250
|
+
}
|
|
13251
|
+
|
|
13252
|
+
// src/tools/get-store-currencies.ts
|
|
13253
|
+
import { z as z74 } from "zod";
|
|
13254
|
+
var inputSchema72 = z74.object({});
|
|
13255
|
+
var outputSchema72 = z74.object({
|
|
13256
|
+
currencyCode: z74.string().describe("Base currency code (e.g., USD)"),
|
|
13257
|
+
enabledPresentmentCurrencies: z74.array(z74.string()).describe("Enabled multi-currency codes"),
|
|
13258
|
+
currencyFormats: z74.object({
|
|
13259
|
+
moneyFormat: z74.string().describe("Money format pattern (e.g., ${{amount}})"),
|
|
13260
|
+
moneyWithCurrencyFormat: z74.string().describe("Money with currency format (e.g., ${{amount}} USD)")
|
|
13261
|
+
}).describe("Currency format patterns")
|
|
13262
|
+
});
|
|
13263
|
+
async function handleGetStoreCurrencies(context, _params) {
|
|
13264
|
+
log.debug(`Getting store currencies for shop: ${context.shopDomain}`);
|
|
13265
|
+
return await getStoreCurrencies();
|
|
13266
|
+
}
|
|
13267
|
+
function registerGetStoreCurrenciesTool() {
|
|
13268
|
+
registerContextAwareTool(
|
|
13269
|
+
{
|
|
13270
|
+
name: "get-store-currencies",
|
|
13271
|
+
title: "Get Store Currencies",
|
|
13272
|
+
description: "Get currency configuration for the connected Shopify store including base currency, enabled presentment currencies, and money format patterns. **Prerequisites:** None. **Use this** to understand multi-currency setup.",
|
|
13273
|
+
inputSchema: inputSchema72,
|
|
13274
|
+
outputSchema: outputSchema72,
|
|
13275
|
+
category: "store",
|
|
13276
|
+
relationships: {
|
|
13277
|
+
relatedTools: ["get-store-info", "get-store-limits"],
|
|
13278
|
+
followUps: ["list-markets", "list-products"]
|
|
13279
|
+
},
|
|
13280
|
+
annotations: {
|
|
13281
|
+
readOnlyHint: true,
|
|
13282
|
+
destructiveHint: false,
|
|
13283
|
+
idempotentHint: true,
|
|
13284
|
+
openWorldHint: true
|
|
13285
|
+
}
|
|
13286
|
+
},
|
|
13287
|
+
handleGetStoreCurrencies
|
|
13288
|
+
);
|
|
13289
|
+
}
|
|
13290
|
+
|
|
13291
|
+
// src/tools/get-store-domain.ts
|
|
13292
|
+
import { z as z75 } from "zod";
|
|
13293
|
+
var inputSchema73 = z75.object({});
|
|
13294
|
+
var outputSchema73 = z75.object({
|
|
13295
|
+
primaryDomain: z75.object({
|
|
13296
|
+
host: z75.string().describe('Domain hostname (e.g., "mystore.com")'),
|
|
13297
|
+
url: z75.string().describe('Full domain URL (e.g., "https://mystore.com")'),
|
|
13298
|
+
sslEnabled: z75.boolean().describe("Whether SSL is enabled")
|
|
13299
|
+
})
|
|
13300
|
+
});
|
|
13301
|
+
async function handleGetStoreDomain(context, _params) {
|
|
13302
|
+
log.debug(`Getting store domain for shop: ${context.shopDomain}`);
|
|
13303
|
+
return await getStoreDomain();
|
|
13304
|
+
}
|
|
13305
|
+
function registerGetStoreDomainTool() {
|
|
13306
|
+
registerContextAwareTool(
|
|
13307
|
+
{
|
|
13308
|
+
name: "get-store-domain",
|
|
13309
|
+
title: "Get Store Domain",
|
|
13310
|
+
description: "Get primary domain configuration for the connected Shopify store including hostname, URL, and SSL status. **Prerequisites:** None. **Use this** to construct proper URLs and understand store identity.",
|
|
13311
|
+
inputSchema: inputSchema73,
|
|
13312
|
+
outputSchema: outputSchema73,
|
|
13313
|
+
category: "store",
|
|
13314
|
+
relationships: {
|
|
13315
|
+
relatedTools: ["get-store-info", "get-store-shipping"],
|
|
13316
|
+
followUps: ["create-redirect"]
|
|
13317
|
+
},
|
|
13318
|
+
annotations: {
|
|
13319
|
+
readOnlyHint: true,
|
|
13320
|
+
destructiveHint: false,
|
|
13321
|
+
idempotentHint: true,
|
|
13322
|
+
openWorldHint: true
|
|
13323
|
+
}
|
|
13324
|
+
},
|
|
13325
|
+
handleGetStoreDomain
|
|
13326
|
+
);
|
|
13327
|
+
}
|
|
13328
|
+
|
|
13329
|
+
// src/tools/get-store-features.ts
|
|
13330
|
+
import { z as z76 } from "zod";
|
|
13331
|
+
var inputSchema74 = z76.object({});
|
|
13332
|
+
var outputSchema74 = z76.object({
|
|
13333
|
+
giftCards: z76.boolean().describe("Gift cards enabled"),
|
|
13334
|
+
reports: z76.boolean().describe("Reports enabled"),
|
|
13335
|
+
storefront: z76.boolean().describe("Storefront enabled"),
|
|
13336
|
+
bundles: z76.object({
|
|
13337
|
+
eligibleForBundles: z76.boolean().describe("Whether store is eligible for bundles")
|
|
13338
|
+
}).describe("Bundles feature configuration"),
|
|
13339
|
+
sellsSubscriptions: z76.boolean().describe("Subscriptions enabled")
|
|
13340
|
+
});
|
|
13341
|
+
async function handleGetStoreFeatures(context, _params) {
|
|
13342
|
+
log.debug(`Getting store features for shop: ${context.shopDomain}`);
|
|
13343
|
+
return await getStoreFeatures();
|
|
13344
|
+
}
|
|
13345
|
+
function registerGetStoreFeaturesTool() {
|
|
13346
|
+
registerContextAwareTool(
|
|
13347
|
+
{
|
|
13348
|
+
name: "get-store-features",
|
|
13349
|
+
title: "Get Store Features",
|
|
13350
|
+
description: "Get feature flags for the connected Shopify store including gift cards, reports, storefront, bundles eligibility, and subscriptions. **Prerequisites:** None. **Use this** to determine available store capabilities.",
|
|
13351
|
+
inputSchema: inputSchema74,
|
|
13352
|
+
outputSchema: outputSchema74,
|
|
13353
|
+
category: "store",
|
|
13354
|
+
relationships: {
|
|
13355
|
+
relatedTools: ["get-store-info", "get-store-limits"],
|
|
13356
|
+
followUps: ["list-products", "list-collections"]
|
|
13357
|
+
},
|
|
13358
|
+
annotations: {
|
|
13359
|
+
readOnlyHint: true,
|
|
13360
|
+
destructiveHint: false,
|
|
13361
|
+
idempotentHint: true,
|
|
13362
|
+
openWorldHint: true
|
|
13363
|
+
}
|
|
13364
|
+
},
|
|
13365
|
+
handleGetStoreFeatures
|
|
13366
|
+
);
|
|
13367
|
+
}
|
|
13368
|
+
|
|
13369
|
+
// src/tools/get-store-info.ts
|
|
13370
|
+
import { z as z77 } from "zod";
|
|
13371
|
+
var inputSchema75 = z77.object({});
|
|
13372
|
+
var outputSchema75 = z77.object({
|
|
13373
|
+
name: z77.string().describe("Store name"),
|
|
13374
|
+
domain: z77.string().describe("Store domain (without .myshopify.com)"),
|
|
13375
|
+
myshopifyDomain: z77.string().describe("Full myshopify.com domain"),
|
|
13376
|
+
plan: z77.object({
|
|
13377
|
+
displayName: z77.string().describe("Current plan name"),
|
|
13378
|
+
partnerDevelopment: z77.boolean().describe("Whether partner development store")
|
|
12573
13379
|
}).describe("Store plan details"),
|
|
12574
|
-
currencyCode:
|
|
12575
|
-
timezone:
|
|
12576
|
-
contactEmail:
|
|
13380
|
+
currencyCode: z77.string().describe("Store currency code (e.g., USD, EUR)"),
|
|
13381
|
+
timezone: z77.string().describe("Store IANA timezone"),
|
|
13382
|
+
contactEmail: z77.string().describe("Store contact email")
|
|
12577
13383
|
});
|
|
12578
13384
|
async function handleGetStoreInfo(context, _params) {
|
|
12579
13385
|
log.debug(`Getting store info for shop: ${context.shopDomain}`);
|
|
@@ -12585,8 +13391,8 @@ function registerGetStoreInfoTool() {
|
|
|
12585
13391
|
name: "get-store-info",
|
|
12586
13392
|
title: "Get Store Info",
|
|
12587
13393
|
description: "Get basic information about the connected Shopify store including name, domain, plan, currency, timezone, and contact email. **Prerequisites:** None. **Use this** to understand store context before performing operations.",
|
|
12588
|
-
inputSchema:
|
|
12589
|
-
outputSchema:
|
|
13394
|
+
inputSchema: inputSchema75,
|
|
13395
|
+
outputSchema: outputSchema75,
|
|
12590
13396
|
category: "store",
|
|
12591
13397
|
relationships: {
|
|
12592
13398
|
relatedTools: [],
|
|
@@ -12603,6 +13409,155 @@ function registerGetStoreInfoTool() {
|
|
|
12603
13409
|
);
|
|
12604
13410
|
}
|
|
12605
13411
|
|
|
13412
|
+
// src/tools/get-store-limits.ts
|
|
13413
|
+
import { z as z78 } from "zod";
|
|
13414
|
+
var inputSchema76 = z78.object({});
|
|
13415
|
+
var outputSchema76 = z78.object({
|
|
13416
|
+
maxProductVariants: z78.number().describe("Maximum variants per product"),
|
|
13417
|
+
maxProductOptions: z78.number().describe("Maximum product options"),
|
|
13418
|
+
locationLimit: z78.number().describe("Maximum locations"),
|
|
13419
|
+
redirectLimitReached: z78.boolean().describe("Whether redirect limit is reached")
|
|
13420
|
+
});
|
|
13421
|
+
async function handleGetStoreLimits(context, _params) {
|
|
13422
|
+
log.debug(`Getting store limits for shop: ${context.shopDomain}`);
|
|
13423
|
+
return await getStoreLimits();
|
|
13424
|
+
}
|
|
13425
|
+
function registerGetStoreLimitsTool() {
|
|
13426
|
+
registerContextAwareTool(
|
|
13427
|
+
{
|
|
13428
|
+
name: "get-store-limits",
|
|
13429
|
+
title: "Get Store Limits",
|
|
13430
|
+
description: "Get resource limits for the connected Shopify store including max product variants, max product options, location limit, and redirect limit status. **Prerequisites:** None. **Use this** to validate operations against store constraints.",
|
|
13431
|
+
inputSchema: inputSchema76,
|
|
13432
|
+
outputSchema: outputSchema76,
|
|
13433
|
+
category: "store",
|
|
13434
|
+
relationships: {
|
|
13435
|
+
relatedTools: ["get-store-info", "get-store-features"],
|
|
13436
|
+
followUps: ["list-products", "create-product"]
|
|
13437
|
+
},
|
|
13438
|
+
annotations: {
|
|
13439
|
+
readOnlyHint: true,
|
|
13440
|
+
destructiveHint: false,
|
|
13441
|
+
idempotentHint: true,
|
|
13442
|
+
openWorldHint: true
|
|
13443
|
+
}
|
|
13444
|
+
},
|
|
13445
|
+
handleGetStoreLimits
|
|
13446
|
+
);
|
|
13447
|
+
}
|
|
13448
|
+
|
|
13449
|
+
// src/tools/get-store-policies.ts
|
|
13450
|
+
import { z as z79 } from "zod";
|
|
13451
|
+
var inputSchema77 = z79.object({});
|
|
13452
|
+
var outputSchema77 = z79.object({
|
|
13453
|
+
shopPolicies: z79.array(
|
|
13454
|
+
z79.object({
|
|
13455
|
+
title: z79.string().describe('Policy title (e.g., "Privacy Policy")'),
|
|
13456
|
+
type: z79.string().describe('Policy type (e.g., "PRIVACY_POLICY")'),
|
|
13457
|
+
url: z79.string().describe("Full URL to the policy page")
|
|
13458
|
+
})
|
|
13459
|
+
).describe("Array of store policy items (empty if scope unavailable)")
|
|
13460
|
+
});
|
|
13461
|
+
async function handleGetStorePolicies(context, _params) {
|
|
13462
|
+
log.debug(`Getting store policies for shop: ${context.shopDomain}`);
|
|
13463
|
+
return await getStorePolicies();
|
|
13464
|
+
}
|
|
13465
|
+
function registerGetStorePoliciesTool() {
|
|
13466
|
+
registerContextAwareTool(
|
|
13467
|
+
{
|
|
13468
|
+
name: "get-store-policies",
|
|
13469
|
+
title: "Get Store Policies",
|
|
13470
|
+
description: "Get legal policies for the connected Shopify store (privacy policy, terms of service, refund policy). Returns empty array if read_legal_policies scope is not available. **Prerequisites:** None. **Use this** to reference store policies in customer communications.",
|
|
13471
|
+
inputSchema: inputSchema77,
|
|
13472
|
+
outputSchema: outputSchema77,
|
|
13473
|
+
category: "store",
|
|
13474
|
+
relationships: {
|
|
13475
|
+
relatedTools: ["get-store-info", "get-store-domain"],
|
|
13476
|
+
followUps: []
|
|
13477
|
+
},
|
|
13478
|
+
annotations: {
|
|
13479
|
+
readOnlyHint: true,
|
|
13480
|
+
destructiveHint: false,
|
|
13481
|
+
idempotentHint: true,
|
|
13482
|
+
openWorldHint: true
|
|
13483
|
+
}
|
|
13484
|
+
},
|
|
13485
|
+
handleGetStorePolicies
|
|
13486
|
+
);
|
|
13487
|
+
}
|
|
13488
|
+
|
|
13489
|
+
// src/tools/get-store-shipping.ts
|
|
13490
|
+
import { z as z80 } from "zod";
|
|
13491
|
+
var inputSchema78 = z80.object({});
|
|
13492
|
+
var outputSchema78 = z80.object({
|
|
13493
|
+
shipsToCountries: z80.array(z80.string()).describe("Countries the store ships to"),
|
|
13494
|
+
countriesInShippingZones: z80.object({
|
|
13495
|
+
countryCodes: z80.array(z80.string()).describe("Countries with configured shipping zones")
|
|
13496
|
+
})
|
|
13497
|
+
});
|
|
13498
|
+
async function handleGetStoreShipping(context, _params) {
|
|
13499
|
+
log.debug(`Getting store shipping for shop: ${context.shopDomain}`);
|
|
13500
|
+
return await getStoreShipping();
|
|
13501
|
+
}
|
|
13502
|
+
function registerGetStoreShippingTool() {
|
|
13503
|
+
registerContextAwareTool(
|
|
13504
|
+
{
|
|
13505
|
+
name: "get-store-shipping",
|
|
13506
|
+
title: "Get Store Shipping",
|
|
13507
|
+
description: "Get shipping configuration for the connected Shopify store including ships-to countries and countries with configured shipping zones. **Prerequisites:** None. **Use this** to understand international shipping capabilities.",
|
|
13508
|
+
inputSchema: inputSchema78,
|
|
13509
|
+
outputSchema: outputSchema78,
|
|
13510
|
+
category: "store",
|
|
13511
|
+
relationships: {
|
|
13512
|
+
relatedTools: ["get-store-info", "get-store-domain", "list-markets"],
|
|
13513
|
+
followUps: ["get-store-domain"]
|
|
13514
|
+
},
|
|
13515
|
+
annotations: {
|
|
13516
|
+
readOnlyHint: true,
|
|
13517
|
+
destructiveHint: false,
|
|
13518
|
+
idempotentHint: true,
|
|
13519
|
+
openWorldHint: true
|
|
13520
|
+
}
|
|
13521
|
+
},
|
|
13522
|
+
handleGetStoreShipping
|
|
13523
|
+
);
|
|
13524
|
+
}
|
|
13525
|
+
|
|
13526
|
+
// src/tools/get-store-taxes.ts
|
|
13527
|
+
import { z as z81 } from "zod";
|
|
13528
|
+
var inputSchema79 = z81.object({});
|
|
13529
|
+
var outputSchema79 = z81.object({
|
|
13530
|
+
taxesIncluded: z81.boolean().describe("Whether taxes are included in product prices"),
|
|
13531
|
+
taxShipping: z81.boolean().describe("Whether shipping is taxed")
|
|
13532
|
+
});
|
|
13533
|
+
async function handleGetStoreTaxes(context, _params) {
|
|
13534
|
+
log.debug(`Getting store taxes for shop: ${context.shopDomain}`);
|
|
13535
|
+
return await getStoreTaxes();
|
|
13536
|
+
}
|
|
13537
|
+
function registerGetStoreTaxesTool() {
|
|
13538
|
+
registerContextAwareTool(
|
|
13539
|
+
{
|
|
13540
|
+
name: "get-store-taxes",
|
|
13541
|
+
title: "Get Store Taxes",
|
|
13542
|
+
description: "Get tax configuration for the connected Shopify store including whether taxes are included in prices and tax shipping settings. **Prerequisites:** None. **Use this** to understand pricing structure for tax calculations.",
|
|
13543
|
+
inputSchema: inputSchema79,
|
|
13544
|
+
outputSchema: outputSchema79,
|
|
13545
|
+
category: "store",
|
|
13546
|
+
relationships: {
|
|
13547
|
+
relatedTools: ["get-store-info", "get-store-currencies"],
|
|
13548
|
+
followUps: []
|
|
13549
|
+
},
|
|
13550
|
+
annotations: {
|
|
13551
|
+
readOnlyHint: true,
|
|
13552
|
+
destructiveHint: false,
|
|
13553
|
+
idempotentHint: true,
|
|
13554
|
+
openWorldHint: true
|
|
13555
|
+
}
|
|
13556
|
+
},
|
|
13557
|
+
handleGetStoreTaxes
|
|
13558
|
+
);
|
|
13559
|
+
}
|
|
13560
|
+
|
|
12606
13561
|
// src/tools/index.ts
|
|
12607
13562
|
function setupToolHandlers(server) {
|
|
12608
13563
|
server.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -12730,6 +13685,14 @@ function registerAllTools(server) {
|
|
|
12730
13685
|
registerImportRedirectsTool();
|
|
12731
13686
|
registerSubmitRedirectImportTool();
|
|
12732
13687
|
registerGetStoreInfoTool();
|
|
13688
|
+
registerGetStoreLimitsTool();
|
|
13689
|
+
registerGetStoreFeaturesTool();
|
|
13690
|
+
registerGetStoreCurrenciesTool();
|
|
13691
|
+
registerGetStoreShippingTool();
|
|
13692
|
+
registerGetStoreDomainTool();
|
|
13693
|
+
registerGetStoreTaxesTool();
|
|
13694
|
+
registerGetStorePoliciesTool();
|
|
13695
|
+
registerGetStoreAlertsTool();
|
|
12733
13696
|
const toolCount = getRegisteredTools().length;
|
|
12734
13697
|
log.debug(`Tool registration complete: ${toolCount} tools registered`);
|
|
12735
13698
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anton.andrusenko/shopify-mcp-admin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP server for Shopify Admin API - enables AI agents to manage Shopify stores with 71 tools for products, inventory, collections, content, SEO, metafields, markets & translations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|