@anton.andrusenko/shopify-mcp-admin 0.8.0 β 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +344 -5
- package/dist/index.js +755 -29
- 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 81 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,8 @@
|
|
|
11
11
|
|
|
12
12
|
## β¨ Features
|
|
13
13
|
|
|
14
|
-
- π οΈ **
|
|
14
|
+
- π οΈ **81 MCP Tools** β Complete store management: products, inventory, collections, pages, blogs, redirects, metafields, markets, locales & translations
|
|
15
|
+
- π¦ **Modular Architecture** β 7 modules with lazy loading for optimal AI performance (see [Tool Modules](#-tool-modules))
|
|
15
16
|
- π€ **AI-Optimized** β Tool descriptions and error messages designed for LLM comprehension
|
|
16
17
|
- π **Dual Transport** β STDIO for Claude Desktop, HTTP for ChatGPT/OpenAI
|
|
17
18
|
- β‘ **Rate Limiting** β Automatic retry with exponential backoff for Shopify API limits
|
|
@@ -107,6 +108,8 @@ Tokens are automatically refreshed every 24 hours.
|
|
|
107
108
|
| `PORT` | No | `3000` | HTTP server port (when `TRANSPORT=http`) |
|
|
108
109
|
| `DEBUG` | No | β | Enable debug logging (`1` or `true`) |
|
|
109
110
|
| `LOG_LEVEL` | No | `info` | Log level: `debug`, `info`, `warn`, `error` |
|
|
111
|
+
| `SHOPIFY_MCP_LAZY_LOADING` | No | `true` | Enable modular lazy loading (set to `false` for all tools at startup) |
|
|
112
|
+
| `SHOPIFY_MCP_ROLE` | No | β | Role preset for automatic module loading (see [Role Presets](#-role-presets)) |
|
|
110
113
|
|
|
111
114
|
**β‘ Authentication:** Provide EITHER `SHOPIFY_ACCESS_TOKEN` (legacy) OR both `SHOPIFY_CLIENT_ID` and `SHOPIFY_CLIENT_SECRET` (Dev Dashboard).
|
|
112
115
|
|
|
@@ -248,9 +251,61 @@ Each tool can be converted to OpenAI function format:
|
|
|
248
251
|
|
|
249
252
|
---
|
|
250
253
|
|
|
254
|
+
## π Role Presets
|
|
255
|
+
|
|
256
|
+
Role presets automatically load appropriate tool modules at startup, optimizing the tool set for specific workflows and reducing AI context overhead.
|
|
257
|
+
|
|
258
|
+
### Available Presets
|
|
259
|
+
|
|
260
|
+
| Preset | Modules | Tools | Use Case |
|
|
261
|
+
|--------|---------|-------|----------|
|
|
262
|
+
| `inventory-manager` | core | 15 | Stock management and inventory operations |
|
|
263
|
+
| `product-manager` | core, store-context, collections, product-extensions | 41 | Full product catalog management |
|
|
264
|
+
| `content-manager` | core, content | 37 | Pages, blogs, and content marketing |
|
|
265
|
+
| `seo-specialist` | core, collections, product-extensions, advanced-redirects | 38 | SEO optimization and URL management |
|
|
266
|
+
| `international-manager` | core, store-context, international, collections | 46 | Multi-market and translation management |
|
|
267
|
+
| `full-access` | All 7 modules | 81 | Full store administration |
|
|
268
|
+
|
|
269
|
+
### Using Role Presets
|
|
270
|
+
|
|
271
|
+
Set the `SHOPIFY_MCP_ROLE` environment variable:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
export SHOPIFY_MCP_ROLE=product-manager
|
|
275
|
+
npx @anton.andrusenko/shopify-mcp-admin
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Or in Claude Desktop config:
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"mcpServers": {
|
|
283
|
+
"shopify": {
|
|
284
|
+
"command": "npx",
|
|
285
|
+
"args": ["-y", "@anton.andrusenko/shopify-mcp-admin"],
|
|
286
|
+
"env": {
|
|
287
|
+
"SHOPIFY_STORE_URL": "your-store.myshopify.com",
|
|
288
|
+
"SHOPIFY_ACCESS_TOKEN": "shpat_xxxxx",
|
|
289
|
+
"SHOPIFY_MCP_ROLE": "product-manager"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Module Discovery
|
|
297
|
+
|
|
298
|
+
If no role is set, the server starts with core tools only (15 tools). AI agents can discover and load additional modules dynamically:
|
|
299
|
+
|
|
300
|
+
1. **Discover modules**: Use `list-modules` to see available modules
|
|
301
|
+
2. **Load modules**: Use `load-module` with the module name to enable tools
|
|
302
|
+
3. **Suggestions**: Tool responses include suggestions for relevant modules to load
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
251
306
|
## π οΈ Available Tools
|
|
252
307
|
|
|
253
|
-
@anton.andrusenko/shopify-mcp-admin provides **
|
|
308
|
+
@anton.andrusenko/shopify-mcp-admin provides **81 tools** (79 domain tools + 2 meta-tools) organized into 16 categories:
|
|
254
309
|
|
|
255
310
|
<details>
|
|
256
311
|
<summary><strong>πͺ Store Info (9 tools)</strong></summary>
|
|
@@ -499,6 +554,289 @@ Each tool can be converted to OpenAI function format:
|
|
|
499
554
|
|
|
500
555
|
---
|
|
501
556
|
|
|
557
|
+
## π¦ Tool Modules
|
|
558
|
+
|
|
559
|
+
shopify-mcp-admin uses a modular architecture to optimize AI agent performance. Instead of loading all 81 tools at startup (which can degrade AI performance by up to 85%), tools are organized into 7 modules that can be loaded on demand.
|
|
560
|
+
|
|
561
|
+
### How Module Loading Works
|
|
562
|
+
|
|
563
|
+
1. **At startup:** Only the **Core Module** (15 tools) is loaded by default
|
|
564
|
+
2. **Discovery:** AI agents use `list-modules` to see available modules
|
|
565
|
+
3. **Loading:** AI agents use `load-module` to enable additional tools
|
|
566
|
+
4. **Suggestions:** Tool responses include hints about which modules to load next
|
|
567
|
+
|
|
568
|
+
### Module Dependency Diagram
|
|
569
|
+
|
|
570
|
+
```
|
|
571
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
572
|
+
β CORE MODULE β
|
|
573
|
+
β (Always Loaded - 15 tools) β
|
|
574
|
+
β Products (7) β’ Inventory (4) β’ Store Info (2) β’ Meta (2) β
|
|
575
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
576
|
+
β
|
|
577
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
578
|
+
β β
|
|
579
|
+
βΌ βΌ
|
|
580
|
+
βββββββββββββββββββββ βββββββββββββββββββββ βββββββββββββββββββββ
|
|
581
|
+
β STORE CONTEXT β β COLLECTIONS β β CONTENT β
|
|
582
|
+
β (7 tools) β β (10 tools) β β (22 tools) β
|
|
583
|
+
β No deps β β No deps β β No deps β
|
|
584
|
+
βββββββββββββββββββββ βββββββββββββββββββββ βββββββββββββββββββββ
|
|
585
|
+
β
|
|
586
|
+
ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
|
|
587
|
+
β β β
|
|
588
|
+
βΌ βΌ βΌ
|
|
589
|
+
βββββββββββββββββββββ βββββββββββββββββββββ βββββββββββββββββββββ
|
|
590
|
+
β INTERNATIONAL β β PRODUCT EXTENSIONSβ β ADVANCED REDIRECTSβ
|
|
591
|
+
β (14 tools) β β (9 tools) β β (4 tools) β
|
|
592
|
+
β No deps β β No deps β β Deps: product-ext β
|
|
593
|
+
βββββββββββββββββββββ βββββββββββββββββββββ βββββββββββββββββββββ
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Module: Core (15 tools) β Always Loaded
|
|
597
|
+
|
|
598
|
+
**Description:** Essential product and inventory management tools that form the foundation for most Shopify operations. Always loaded at startup.
|
|
599
|
+
|
|
600
|
+
**Dependencies:** None
|
|
601
|
+
|
|
602
|
+
**Tools:**
|
|
603
|
+
|
|
604
|
+
| Category | Tools |
|
|
605
|
+
|----------|-------|
|
|
606
|
+
| Products (7) | `create-product`, `get-product`, `update-product`, `delete-product`, `list-products`, `update-product-variant`, `add-product-image` |
|
|
607
|
+
| Inventory (4) | `get-inventory`, `update-inventory`, `list-low-inventory`, `get-bulk-inventory` |
|
|
608
|
+
| Store Info (2) | `get-store-info`, `get-store-limits` |
|
|
609
|
+
| Meta Tools (2) | `list-modules`, `load-module` |
|
|
610
|
+
|
|
611
|
+
**Use Case:** Basic product catalog management, inventory tracking, and store information queries.
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
### Module: Store Context (7 tools)
|
|
616
|
+
|
|
617
|
+
**Description:** Extended store configuration and context tools from Epic 11. Provides deep insights into store configuration including features, currencies, shipping, and more.
|
|
618
|
+
|
|
619
|
+
**Dependencies:** None
|
|
620
|
+
|
|
621
|
+
**Tools:**
|
|
622
|
+
- `get-store-features` β Feature flags (gift cards, reports, storefront, bundles)
|
|
623
|
+
- `get-store-currencies` β Multi-currency configuration
|
|
624
|
+
- `get-store-shipping` β Shipping countries and zones
|
|
625
|
+
- `get-store-domain` β Primary domain configuration
|
|
626
|
+
- `get-store-taxes` β Tax configuration
|
|
627
|
+
- `get-store-policies` β Legal policies (privacy, terms, refund)
|
|
628
|
+
- `get-store-alerts` β Admin alerts and setup requirements
|
|
629
|
+
|
|
630
|
+
**Use Case:** Understanding store capabilities before operations, multi-currency setup, tax configuration.
|
|
631
|
+
|
|
632
|
+
**Load Command:** `load-module(module: "store-context")`
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
### Module: Collections (10 tools)
|
|
637
|
+
|
|
638
|
+
**Description:** Product collection management for organizing products and navigation. Includes CRUD operations and metafields.
|
|
639
|
+
|
|
640
|
+
**Dependencies:** None
|
|
641
|
+
|
|
642
|
+
**Tools:**
|
|
643
|
+
|
|
644
|
+
| Category | Tools |
|
|
645
|
+
|----------|-------|
|
|
646
|
+
| CRUD (7) | `list-collections`, `get-collection`, `create-collection`, `update-collection`, `delete-collection`, `add-products-to-collection`, `remove-products-from-collection` |
|
|
647
|
+
| Metafields (3) | `get-collection-metafields`, `set-collection-metafields`, `delete-collection-metafields` |
|
|
648
|
+
|
|
649
|
+
**Use Case:** Organizing products into collections, SEO optimization, storefront navigation.
|
|
650
|
+
|
|
651
|
+
**Load Command:** `load-module(module: "collections")`
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
|
|
655
|
+
### Module: Product Extensions (9 tools)
|
|
656
|
+
|
|
657
|
+
**Description:** Extended product management including metafields, advanced image operations, and URL redirects for SEO.
|
|
658
|
+
|
|
659
|
+
**Dependencies:** None
|
|
660
|
+
|
|
661
|
+
**Tools:**
|
|
662
|
+
|
|
663
|
+
| Category | Tools |
|
|
664
|
+
|----------|-------|
|
|
665
|
+
| Metafields (3) | `get-product-metafields`, `set-product-metafields`, `delete-product-metafields` |
|
|
666
|
+
| Images (3) | `update-product-image`, `delete-product-image`, `reorder-product-images` |
|
|
667
|
+
| Redirects (3) | `create-redirect`, `list-redirects`, `delete-redirect` |
|
|
668
|
+
|
|
669
|
+
**Use Case:** Adding custom metadata to products, SEO image alt text, URL redirects when changing product handles.
|
|
670
|
+
|
|
671
|
+
**Load Command:** `load-module(module: "product-extensions")`
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
### Module: Content (22 tools)
|
|
676
|
+
|
|
677
|
+
**Description:** Content management for pages, blogs, and articles. Complete CRUD operations plus metafields for content marketing and SEO.
|
|
678
|
+
|
|
679
|
+
**Dependencies:** None
|
|
680
|
+
|
|
681
|
+
**Tools:**
|
|
682
|
+
|
|
683
|
+
| Category | Tools |
|
|
684
|
+
|----------|-------|
|
|
685
|
+
| Pages (8) | `list-pages`, `get-page`, `create-page`, `update-page`, `delete-page`, `get-page-metafields`, `set-page-metafields`, `delete-page-metafields` |
|
|
686
|
+
| Blogs (7) | `list-blogs`, `create-blog`, `update-blog`, `delete-blog`, `get-blog-metafields`, `set-blog-metafields`, `delete-blog-metafields` |
|
|
687
|
+
| Articles (7) | `list-articles`, `create-article`, `update-article`, `delete-article`, `get-article-metafields`, `set-article-metafields`, `delete-article-metafields` |
|
|
688
|
+
|
|
689
|
+
**Use Case:** Content marketing, blog management, static pages (About, Contact, FAQ).
|
|
690
|
+
|
|
691
|
+
**Load Command:** `load-module(module: "content")`
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
### Module: International (14 tools)
|
|
696
|
+
|
|
697
|
+
**Description:** International commerce and localization tools. Markets, web presence, and shop locale translations for multi-region stores.
|
|
698
|
+
|
|
699
|
+
**Dependencies:** None
|
|
700
|
+
|
|
701
|
+
**Tools:**
|
|
702
|
+
|
|
703
|
+
| Category | Tools |
|
|
704
|
+
|----------|-------|
|
|
705
|
+
| Markets (5) | `list-markets`, `get-market`, `create-market`, `update-market`, `delete-market` |
|
|
706
|
+
| Web Presence (4) | `list-web-presences`, `create-web-presence`, `update-web-presence`, `delete-web-presence` |
|
|
707
|
+
| Translations (5) | `list-shop-locales`, `enable-shop-locale`, `disable-shop-locale`, `register-translations`, `remove-translations` |
|
|
708
|
+
|
|
709
|
+
**Use Case:** Multi-region selling, localized storefronts, translation management.
|
|
710
|
+
|
|
711
|
+
**Load Command:** `load-module(module: "international")`
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
### Module: Advanced Redirects (4 tools)
|
|
716
|
+
|
|
717
|
+
**Description:** Bulk URL redirect operations for large-scale SEO management. Import and mass-delete capabilities.
|
|
718
|
+
|
|
719
|
+
**Dependencies:** `product-extensions` (auto-loaded when you load this module)
|
|
720
|
+
|
|
721
|
+
**Tools:**
|
|
722
|
+
- `bulk-delete-redirects` β Delete multiple redirects by ID
|
|
723
|
+
- `bulk-delete-redirects-by-search` β Delete redirects matching a search query
|
|
724
|
+
- `import-redirects` β Import redirects from CSV URL
|
|
725
|
+
- `submit-redirect-import` β Submit a redirect import job for processing
|
|
726
|
+
|
|
727
|
+
**Use Case:** Site migrations, bulk URL updates, SEO cleanup operations.
|
|
728
|
+
|
|
729
|
+
**Load Command:** `load-module(module: "advanced-redirects")`
|
|
730
|
+
|
|
731
|
+
> **Note:** Loading this module automatically loads `product-extensions` (9 tools) as a dependency.
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
### Choosing the Right Module
|
|
736
|
+
|
|
737
|
+
| If you need to... | Load these modules |
|
|
738
|
+
|-------------------|-------------------|
|
|
739
|
+
| Manage basic products and inventory | Core only (default) |
|
|
740
|
+
| Work with collections and organize products | `collections` |
|
|
741
|
+
| Add product metafields or manage images | `product-extensions` |
|
|
742
|
+
| Manage blog posts and pages | `content` |
|
|
743
|
+
| Set up international markets | `international` |
|
|
744
|
+
| Bulk manage URL redirects | `advanced-redirects` |
|
|
745
|
+
| Full store administration | Use `full-access` role preset |
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## π Upgrading from Previous Versions
|
|
750
|
+
|
|
751
|
+
### What's New in v0.8.0+: Modular Tool Loading
|
|
752
|
+
|
|
753
|
+
Starting with v0.8.0, shopify-mcp-admin uses **lazy loading** to optimize AI agent performance. Research shows that presenting more than 30 tools to an AI agent can degrade performance by up to 85%.
|
|
754
|
+
|
|
755
|
+
**Default Behavior (Lazy Loading Enabled):**
|
|
756
|
+
- Only 15 core tools are loaded at startup
|
|
757
|
+
- Additional tools are loaded on-demand via `load-module`
|
|
758
|
+
- AI agents discover available modules using `list-modules`
|
|
759
|
+
- Token usage reduced by up to 81% compared to loading all tools
|
|
760
|
+
|
|
761
|
+
### Breaking Changes
|
|
762
|
+
|
|
763
|
+
| Previous Behavior | New Behavior (v0.8.0+) |
|
|
764
|
+
|-------------------|------------------------|
|
|
765
|
+
| All 79+ tools loaded at startup | 15 core tools loaded at startup |
|
|
766
|
+
| No module concept | 7 modules with lazy loading |
|
|
767
|
+
| N/A | `list-modules` and `load-module` meta-tools added |
|
|
768
|
+
|
|
769
|
+
### Legacy Mode: Restore Previous Behavior
|
|
770
|
+
|
|
771
|
+
If you prefer all tools loaded at startup (not recommended for optimal AI performance):
|
|
772
|
+
|
|
773
|
+
```bash
|
|
774
|
+
# Option 1: Environment variable
|
|
775
|
+
export SHOPIFY_MCP_LAZY_LOADING=false
|
|
776
|
+
|
|
777
|
+
# Option 2: In Claude Desktop config
|
|
778
|
+
{
|
|
779
|
+
"mcpServers": {
|
|
780
|
+
"shopify": {
|
|
781
|
+
"command": "npx",
|
|
782
|
+
"args": ["-y", "@anton.andrusenko/shopify-mcp-admin"],
|
|
783
|
+
"env": {
|
|
784
|
+
"SHOPIFY_STORE_URL": "your-store.myshopify.com",
|
|
785
|
+
"SHOPIFY_ACCESS_TOKEN": "shpat_xxxxx",
|
|
786
|
+
"SHOPIFY_MCP_LAZY_LOADING": "false"
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Migration Steps
|
|
794
|
+
|
|
795
|
+
1. **Review your use case** β Do you need all 81 tools? Most workflows only need a subset.
|
|
796
|
+
|
|
797
|
+
2. **Choose a role preset** (recommended) β Set `SHOPIFY_MCP_ROLE` for pre-configured modules:
|
|
798
|
+
```bash
|
|
799
|
+
export SHOPIFY_MCP_ROLE=product-manager
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
3. **Or use on-demand loading** β Let AI agents discover and load modules as needed:
|
|
803
|
+
- AI uses `list-modules` to see available modules
|
|
804
|
+
- AI uses `load-module` with module name to enable tools
|
|
805
|
+
- Tool responses suggest relevant modules automatically
|
|
806
|
+
|
|
807
|
+
4. **Or disable lazy loading** β Set `SHOPIFY_MCP_LAZY_LOADING=false` for legacy behavior
|
|
808
|
+
|
|
809
|
+
### When to Use Each Mode
|
|
810
|
+
|
|
811
|
+
| Mode | Tools | Best For |
|
|
812
|
+
|------|-------|----------|
|
|
813
|
+
| **Lazy Loading (default)** | 15 at startup | New projects, optimal AI performance |
|
|
814
|
+
| **Role Preset** | 15-81 based on role | Specific workflows (e.g., content manager) |
|
|
815
|
+
| **Legacy Mode** | All 81 | Backward compatibility, full store admin |
|
|
816
|
+
|
|
817
|
+
### Performance Comparison
|
|
818
|
+
|
|
819
|
+
| Mode | Tools at Startup | Est. Token Usage | AI Performance |
|
|
820
|
+
|------|------------------|------------------|----------------|
|
|
821
|
+
| Lazy Loading (default) | 15 | ~4,500 | βββ Optimal |
|
|
822
|
+
| inventory-manager | 15 | ~4,500 | βββ Optimal |
|
|
823
|
+
| content-manager | 37 | ~11,100 | ββ Good |
|
|
824
|
+
| product-manager | 41 | ~12,300 | ββ Good |
|
|
825
|
+
| international-manager | 46 | ~13,800 | ββ Good |
|
|
826
|
+
| full-access | 81 | ~24,000 | β May degrade |
|
|
827
|
+
| Legacy Mode | 81 | ~24,000 | β May degrade |
|
|
828
|
+
|
|
829
|
+
### Troubleshooting Module Loading
|
|
830
|
+
|
|
831
|
+
| Issue | Solution |
|
|
832
|
+
|-------|----------|
|
|
833
|
+
| Tool not found after upgrade | Load the appropriate module: `load-module(module: "collections")` |
|
|
834
|
+
| "Module not found" error | Use `list-modules` to see valid module names |
|
|
835
|
+
| Want all tools at startup | Set `SHOPIFY_MCP_LAZY_LOADING=false` |
|
|
836
|
+
| AI agent loading too many modules | Use a role preset instead of on-demand loading |
|
|
837
|
+
|
|
838
|
+
---
|
|
839
|
+
|
|
502
840
|
## π§ Troubleshooting
|
|
503
841
|
|
|
504
842
|
### Common Errors
|
|
@@ -543,13 +881,14 @@ npm run inspect:config
|
|
|
543
881
|
|
|
544
882
|
MCP Inspector opens a web UI at `http://localhost:6274` where you can:
|
|
545
883
|
|
|
546
|
-
- π Browse
|
|
884
|
+
- π Browse registered tools with schemas (15 core tools by default, up to 81 with modules)
|
|
885
|
+
- π¦ Test module loading via `list-modules` and `load-module`
|
|
547
886
|
- π Access 9 MCP resources for store context
|
|
548
887
|
- βΆοΈ Execute tools interactively and view results
|
|
549
888
|
- π Inspect JSON-RPC protocol messages
|
|
550
889
|
- π Monitor server events in real-time
|
|
551
890
|
|
|
552
|
-
> **Tip:**
|
|
891
|
+
> **Tip:** Copy `mcp-inspector.example.json` to `mcp-inspector.json` for pre-configured role presets like `product-manager` or `full-access`.
|
|
553
892
|
|
|
554
893
|
---
|
|
555
894
|
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,17 @@ var configSchema = z.object({
|
|
|
25
25
|
TRANSPORT: z.enum(["stdio", "http"]).default("stdio"),
|
|
26
26
|
// Store info cache TTL (milliseconds)
|
|
27
27
|
// Default: 5 minutes (300000ms) - configurable for performance tuning
|
|
28
|
-
STORE_INFO_CACHE_TTL_MS: z.string().optional().default("300000").transform(Number).describe("Cache TTL for store info in milliseconds (default: 5 minutes)")
|
|
28
|
+
STORE_INFO_CACHE_TTL_MS: z.string().optional().default("300000").transform(Number).describe("Cache TTL for store info in milliseconds (default: 5 minutes)"),
|
|
29
|
+
// Lazy loading configuration (Epic 12)
|
|
30
|
+
// Default: true - enables module-based lazy loading for optimal AI performance
|
|
31
|
+
// Set to 'false' or '0' to load all tools at startup (legacy mode)
|
|
32
|
+
SHOPIFY_MCP_LAZY_LOADING: z.string().optional().default("true").transform((val) => val === "true" || val === "1").describe("Enable lazy loading of tools via modules (default: true)"),
|
|
33
|
+
// Role preset configuration (Story 12.3: Progressive Loading)
|
|
34
|
+
// Automatically loads appropriate modules at startup based on role
|
|
35
|
+
// Valid roles: inventory-manager, product-manager, content-manager,
|
|
36
|
+
// seo-specialist, international-manager, full-access
|
|
37
|
+
// When not set, only core module is loaded (requires manual module loading)
|
|
38
|
+
SHOPIFY_MCP_ROLE: z.string().optional().describe("Role preset for automatic module loading at startup")
|
|
29
39
|
}).refine(
|
|
30
40
|
(data) => {
|
|
31
41
|
const hasLegacyAuth = !!data.SHOPIFY_ACCESS_TOKEN;
|
|
@@ -73,6 +83,12 @@ function isDebugEnabled(debugValue) {
|
|
|
73
83
|
const normalized = debugValue.toLowerCase().trim();
|
|
74
84
|
return normalized === "1" || normalized === "true";
|
|
75
85
|
}
|
|
86
|
+
function isLazyLoadingEnabled(config) {
|
|
87
|
+
return config.SHOPIFY_MCP_LAZY_LOADING;
|
|
88
|
+
}
|
|
89
|
+
function getConfiguredRole(config) {
|
|
90
|
+
return config.SHOPIFY_MCP_ROLE;
|
|
91
|
+
}
|
|
76
92
|
|
|
77
93
|
// src/config/index.ts
|
|
78
94
|
var _config = null;
|
|
@@ -1310,14 +1326,45 @@ var packageJson = require2("../package.json");
|
|
|
1310
1326
|
var SERVER_NAME = "shopify-mcp-admin";
|
|
1311
1327
|
var SERVER_INSTRUCTIONS = `This MCP server provides access to a Shopify store's Admin API.
|
|
1312
1328
|
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
-
|
|
1329
|
+
## TOOL MODULES (Modular Architecture)
|
|
1330
|
+
|
|
1331
|
+
Tools are organized into modules for optimal AI performance. Start with core tools and load additional modules as needed.
|
|
1332
|
+
|
|
1333
|
+
**Discovery & Loading:**
|
|
1334
|
+
- Use "list-modules" to see all available modules and their loading status
|
|
1335
|
+
- Use "load-module" with module name to enable additional tools
|
|
1336
|
+
- Tool responses may include suggestions for relevant modules to load
|
|
1337
|
+
|
|
1338
|
+
**Available Modules:**
|
|
1339
|
+
| Module | Description | Tools |
|
|
1340
|
+
|--------|-------------|-------|
|
|
1341
|
+
| core | Products, inventory, store info (always loaded) | 15 |
|
|
1342
|
+
| store-context | Extended store configuration (features, currencies, shipping) | 7 |
|
|
1343
|
+
| collections | Collection management and metafields | 10 |
|
|
1344
|
+
| product-extensions | Product metafields, images, and URL redirects | 9 |
|
|
1345
|
+
| content | Pages, blogs, and articles management | 22 |
|
|
1346
|
+
| international | Markets, web presence, and translations | 14 |
|
|
1347
|
+
| advanced-redirects | Bulk redirect operations | 4 |
|
|
1348
|
+
|
|
1349
|
+
**Role Presets (SHOPIFY_MCP_ROLE):**
|
|
1350
|
+
Pre-configured module sets for common workflows:
|
|
1351
|
+
- inventory-manager: Core only (15 tools) - stock management
|
|
1352
|
+
- product-manager: Core + store-context + collections + product-extensions (41 tools)
|
|
1353
|
+
- content-manager: Core + content (37 tools) - pages and blogs
|
|
1354
|
+
- seo-specialist: Core + collections + product-extensions + advanced-redirects (38 tools)
|
|
1355
|
+
- international-manager: Core + store-context + international + collections (46 tools)
|
|
1356
|
+
- full-access: All modules (81 tools) - full store administration
|
|
1357
|
+
|
|
1358
|
+
## KEY CONCEPTS
|
|
1359
|
+
|
|
1360
|
+
- **Products & Variants**: Products contain variants; each variant has its own price, SKU, and inventory
|
|
1361
|
+
- **Multi-location Inventory**: Inventory is tracked per variant AND per location
|
|
1362
|
+
- **Metafields**: Store custom data using namespace/key pairs on products, collections, pages, blogs, and articles
|
|
1363
|
+
- **Collections**: Organize products for navigation and SEO
|
|
1364
|
+
- **GID Format**: All IDs use Shopify GID format: "gid://shopify/Product/123"
|
|
1365
|
+
|
|
1366
|
+
## RESOURCE TYPES & GID FORMATS
|
|
1319
1367
|
|
|
1320
|
-
RESOURCE TYPES & GID FORMATS:
|
|
1321
1368
|
- Products: gid://shopify/Product/{id}
|
|
1322
1369
|
- Variants: gid://shopify/ProductVariant/{id}
|
|
1323
1370
|
- Collections: gid://shopify/Collection/{id}
|
|
@@ -1329,21 +1376,25 @@ RESOURCE TYPES & GID FORMATS:
|
|
|
1329
1376
|
- InventoryItems: gid://shopify/InventoryItem/{id}
|
|
1330
1377
|
- Locations: gid://shopify/Location/{id}
|
|
1331
1378
|
|
|
1332
|
-
BEST PRACTICES
|
|
1333
|
-
|
|
1334
|
-
- Use
|
|
1335
|
-
-
|
|
1336
|
-
-
|
|
1337
|
-
-
|
|
1379
|
+
## BEST PRACTICES
|
|
1380
|
+
|
|
1381
|
+
- **Verify before modifying**: Use get-product to verify IDs before updates
|
|
1382
|
+
- **Proactive inventory**: Use list-low-inventory to identify stock issues
|
|
1383
|
+
- **SEO redirects**: When updating product handles, consider creating URL redirects
|
|
1384
|
+
- **Bulk operations**: Use bulk tools (get-bulk-inventory, bulk-delete-redirects) when available
|
|
1385
|
+
- **Metafields for SEO**: Store custom SEO data (JSON-LD, Open Graph) in metafields
|
|
1386
|
+
|
|
1387
|
+
## RATE LIMITS
|
|
1338
1388
|
|
|
1339
|
-
RATE LIMITS:
|
|
1340
1389
|
- Shopify GraphQL has cost-based rate limiting (~50 points/sec)
|
|
1341
1390
|
- Large queries may retry automatically with exponential backoff
|
|
1342
1391
|
- Rate limit errors include helpful retry suggestions
|
|
1343
1392
|
|
|
1344
|
-
WORKFLOW HINTS
|
|
1393
|
+
## WORKFLOW HINTS
|
|
1394
|
+
|
|
1345
1395
|
- Tool descriptions include **Prerequisites:** and **Follow-ups:** for multi-step operations
|
|
1346
|
-
- Check tool category and relationships for semantic grouping
|
|
1396
|
+
- Check tool category and relationships for semantic grouping
|
|
1397
|
+
- After successful operations, note any module suggestions in the response`;
|
|
1347
1398
|
function getServerVersion() {
|
|
1348
1399
|
return packageJson.version;
|
|
1349
1400
|
}
|
|
@@ -1364,8 +1415,12 @@ function createServer() {
|
|
|
1364
1415
|
logging: {}
|
|
1365
1416
|
// Enable logging capability (AC-9.5.1.2)
|
|
1366
1417
|
},
|
|
1367
|
-
instructions: SERVER_INSTRUCTIONS
|
|
1418
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
1368
1419
|
// Provide Shopify context to AI agents (AC-9.5.1.1)
|
|
1420
|
+
// Epic 12: Notification debouncing for module loading (AC-12.1.6)
|
|
1421
|
+
// When multiple tools are enabled in rapid succession (e.g., loading a module),
|
|
1422
|
+
// coalesce into a single notifications/tools/list_changed notification
|
|
1423
|
+
debouncedNotificationMethods: ["notifications/tools/list_changed"]
|
|
1369
1424
|
}
|
|
1370
1425
|
);
|
|
1371
1426
|
return server;
|
|
@@ -3039,6 +3094,7 @@ function deriveDefaultAnnotations(name) {
|
|
|
3039
3094
|
};
|
|
3040
3095
|
}
|
|
3041
3096
|
var registeredTools = /* @__PURE__ */ new Map();
|
|
3097
|
+
var toolEnabledState = /* @__PURE__ */ new Map();
|
|
3042
3098
|
function validateToolName(name) {
|
|
3043
3099
|
if (!name || name.trim() === "") {
|
|
3044
3100
|
throw new Error("Tool name cannot be empty");
|
|
@@ -3097,7 +3153,7 @@ function wrapToolHandler(toolName, schema, handler) {
|
|
|
3097
3153
|
};
|
|
3098
3154
|
}
|
|
3099
3155
|
function registerTool(definition, handler, options = {}) {
|
|
3100
|
-
const { name, title, description, inputSchema:
|
|
3156
|
+
const { name, title, description, inputSchema: inputSchema82, annotations } = definition;
|
|
3101
3157
|
try {
|
|
3102
3158
|
if (!options.skipNameValidation) {
|
|
3103
3159
|
validateToolName(name);
|
|
@@ -3105,8 +3161,8 @@ function registerTool(definition, handler, options = {}) {
|
|
|
3105
3161
|
if (registeredTools.has(name)) {
|
|
3106
3162
|
throw new Error(`Tool "${name}" is already registered. Tool names must be unique.`);
|
|
3107
3163
|
}
|
|
3108
|
-
const jsonSchema = convertZodToJsonSchema(
|
|
3109
|
-
const wrappedHandler = wrapToolHandler(name,
|
|
3164
|
+
const jsonSchema = convertZodToJsonSchema(inputSchema82);
|
|
3165
|
+
const wrappedHandler = wrapToolHandler(name, inputSchema82, handler);
|
|
3110
3166
|
const finalAnnotations = {
|
|
3111
3167
|
...deriveDefaultAnnotations(name),
|
|
3112
3168
|
...annotations,
|
|
@@ -3118,7 +3174,7 @@ function registerTool(definition, handler, options = {}) {
|
|
|
3118
3174
|
title,
|
|
3119
3175
|
description,
|
|
3120
3176
|
inputSchema: jsonSchema,
|
|
3121
|
-
zodSchema:
|
|
3177
|
+
zodSchema: inputSchema82,
|
|
3122
3178
|
handler: wrappedHandler,
|
|
3123
3179
|
annotations: finalAnnotations
|
|
3124
3180
|
};
|
|
@@ -3136,7 +3192,7 @@ function registerTool(definition, handler, options = {}) {
|
|
|
3136
3192
|
}
|
|
3137
3193
|
}
|
|
3138
3194
|
function getRegisteredTools() {
|
|
3139
|
-
return Array.from(registeredTools.values()).map((tool) => ({
|
|
3195
|
+
return Array.from(registeredTools.values()).filter((tool) => isToolEnabled(tool.name)).map((tool) => ({
|
|
3140
3196
|
name: tool.name,
|
|
3141
3197
|
description: `${tool.title}: ${tool.description}`,
|
|
3142
3198
|
inputSchema: tool.inputSchema,
|
|
@@ -3146,6 +3202,32 @@ function getRegisteredTools() {
|
|
|
3146
3202
|
function getToolByName(name) {
|
|
3147
3203
|
return registeredTools.get(name);
|
|
3148
3204
|
}
|
|
3205
|
+
function getToolNames() {
|
|
3206
|
+
return Array.from(registeredTools.keys());
|
|
3207
|
+
}
|
|
3208
|
+
function enableTool(name) {
|
|
3209
|
+
const tool = registeredTools.get(name);
|
|
3210
|
+
if (!tool) {
|
|
3211
|
+
log.debug(`Cannot enable tool: ${name} not found`);
|
|
3212
|
+
return false;
|
|
3213
|
+
}
|
|
3214
|
+
toolEnabledState.set(name, true);
|
|
3215
|
+
log.debug(`Tool enabled: ${name}`);
|
|
3216
|
+
return true;
|
|
3217
|
+
}
|
|
3218
|
+
function disableTool(name) {
|
|
3219
|
+
const tool = registeredTools.get(name);
|
|
3220
|
+
if (!tool) {
|
|
3221
|
+
log.debug(`Cannot disable tool: ${name} not found`);
|
|
3222
|
+
return false;
|
|
3223
|
+
}
|
|
3224
|
+
toolEnabledState.set(name, false);
|
|
3225
|
+
log.debug(`Tool disabled: ${name}`);
|
|
3226
|
+
return true;
|
|
3227
|
+
}
|
|
3228
|
+
function isToolEnabled(name) {
|
|
3229
|
+
return toolEnabledState.get(name) ?? true;
|
|
3230
|
+
}
|
|
3149
3231
|
function createHandlerWithContext(contextAwareHandler) {
|
|
3150
3232
|
return async (params) => {
|
|
3151
3233
|
const client = await getShopifyClient();
|
|
@@ -13558,6 +13640,549 @@ function registerGetStoreTaxesTool() {
|
|
|
13558
13640
|
);
|
|
13559
13641
|
}
|
|
13560
13642
|
|
|
13643
|
+
// src/tools/list-modules.ts
|
|
13644
|
+
import { z as z83 } from "zod";
|
|
13645
|
+
|
|
13646
|
+
// src/tools/modules/registry.ts
|
|
13647
|
+
var CORE_MODULE = {
|
|
13648
|
+
name: "core",
|
|
13649
|
+
description: "Essential product and inventory management tools. Includes product CRUD, variants, basic images, inventory operations, and store info. Always loaded at startup.",
|
|
13650
|
+
alwaysLoaded: true,
|
|
13651
|
+
dependencies: [],
|
|
13652
|
+
tools: [
|
|
13653
|
+
// Products (7)
|
|
13654
|
+
"create-product",
|
|
13655
|
+
"get-product",
|
|
13656
|
+
"update-product",
|
|
13657
|
+
"delete-product",
|
|
13658
|
+
"list-products",
|
|
13659
|
+
"update-product-variant",
|
|
13660
|
+
"add-product-image",
|
|
13661
|
+
// Inventory (4)
|
|
13662
|
+
"get-inventory",
|
|
13663
|
+
"update-inventory",
|
|
13664
|
+
"list-low-inventory",
|
|
13665
|
+
"get-bulk-inventory",
|
|
13666
|
+
// Store Info (2)
|
|
13667
|
+
"get-store-info",
|
|
13668
|
+
"get-store-limits",
|
|
13669
|
+
// Meta Tools (2) - Added by this story
|
|
13670
|
+
"list-modules",
|
|
13671
|
+
"load-module"
|
|
13672
|
+
]
|
|
13673
|
+
};
|
|
13674
|
+
var STORE_CONTEXT_MODULE = {
|
|
13675
|
+
name: "store-context",
|
|
13676
|
+
description: "Extended store configuration and context tools. Includes features, currencies, shipping countries, domain, taxes, policies, and alerts. Use for comprehensive store insights.",
|
|
13677
|
+
alwaysLoaded: false,
|
|
13678
|
+
dependencies: [],
|
|
13679
|
+
tools: [
|
|
13680
|
+
"get-store-features",
|
|
13681
|
+
"get-store-currencies",
|
|
13682
|
+
"get-store-shipping",
|
|
13683
|
+
"get-store-domain",
|
|
13684
|
+
"get-store-taxes",
|
|
13685
|
+
"get-store-policies",
|
|
13686
|
+
"get-store-alerts"
|
|
13687
|
+
]
|
|
13688
|
+
};
|
|
13689
|
+
var COLLECTIONS_MODULE = {
|
|
13690
|
+
name: "collections",
|
|
13691
|
+
description: "Product collection management tools. Includes collection CRUD, adding/removing products, and collection metafields. Use for organizing products and SEO.",
|
|
13692
|
+
alwaysLoaded: false,
|
|
13693
|
+
dependencies: [],
|
|
13694
|
+
tools: [
|
|
13695
|
+
// CRUD (7)
|
|
13696
|
+
"list-collections",
|
|
13697
|
+
"get-collection",
|
|
13698
|
+
"create-collection",
|
|
13699
|
+
"update-collection",
|
|
13700
|
+
"delete-collection",
|
|
13701
|
+
"add-products-to-collection",
|
|
13702
|
+
"remove-products-from-collection",
|
|
13703
|
+
// Metafields (3)
|
|
13704
|
+
"get-collection-metafields",
|
|
13705
|
+
"set-collection-metafields",
|
|
13706
|
+
"delete-collection-metafields"
|
|
13707
|
+
]
|
|
13708
|
+
};
|
|
13709
|
+
var PRODUCT_EXTENSIONS_MODULE = {
|
|
13710
|
+
name: "product-extensions",
|
|
13711
|
+
description: "Extended product management tools. Includes product metafields, advanced image operations (update, delete, reorder), and URL redirects for SEO.",
|
|
13712
|
+
alwaysLoaded: false,
|
|
13713
|
+
dependencies: [],
|
|
13714
|
+
tools: [
|
|
13715
|
+
// Metafields (3)
|
|
13716
|
+
"get-product-metafields",
|
|
13717
|
+
"set-product-metafields",
|
|
13718
|
+
"delete-product-metafields",
|
|
13719
|
+
// Images (3)
|
|
13720
|
+
"update-product-image",
|
|
13721
|
+
"delete-product-image",
|
|
13722
|
+
"reorder-product-images",
|
|
13723
|
+
// Redirects (3)
|
|
13724
|
+
"create-redirect",
|
|
13725
|
+
"list-redirects",
|
|
13726
|
+
"delete-redirect"
|
|
13727
|
+
]
|
|
13728
|
+
};
|
|
13729
|
+
var CONTENT_MODULE = {
|
|
13730
|
+
name: "content",
|
|
13731
|
+
description: "Content management tools for pages, blogs, and articles. Includes CRUD operations and metafields for all content types. Use for content marketing and SEO.",
|
|
13732
|
+
alwaysLoaded: false,
|
|
13733
|
+
dependencies: [],
|
|
13734
|
+
tools: [
|
|
13735
|
+
// Pages (8)
|
|
13736
|
+
"list-pages",
|
|
13737
|
+
"get-page",
|
|
13738
|
+
"create-page",
|
|
13739
|
+
"update-page",
|
|
13740
|
+
"delete-page",
|
|
13741
|
+
"get-page-metafields",
|
|
13742
|
+
"set-page-metafields",
|
|
13743
|
+
"delete-page-metafields",
|
|
13744
|
+
// Blogs (7)
|
|
13745
|
+
"list-blogs",
|
|
13746
|
+
"create-blog",
|
|
13747
|
+
"update-blog",
|
|
13748
|
+
"delete-blog",
|
|
13749
|
+
"get-blog-metafields",
|
|
13750
|
+
"set-blog-metafields",
|
|
13751
|
+
"delete-blog-metafields",
|
|
13752
|
+
// Articles (7)
|
|
13753
|
+
"list-articles",
|
|
13754
|
+
"create-article",
|
|
13755
|
+
"update-article",
|
|
13756
|
+
"delete-article",
|
|
13757
|
+
"get-article-metafields",
|
|
13758
|
+
"set-article-metafields",
|
|
13759
|
+
"delete-article-metafields"
|
|
13760
|
+
]
|
|
13761
|
+
};
|
|
13762
|
+
var INTERNATIONAL_MODULE = {
|
|
13763
|
+
name: "international",
|
|
13764
|
+
description: "International commerce and localization tools. Includes markets management, web presence configuration, and shop locale translations. Use for multi-region and multi-language stores.",
|
|
13765
|
+
alwaysLoaded: false,
|
|
13766
|
+
dependencies: [],
|
|
13767
|
+
tools: [
|
|
13768
|
+
// Markets (5)
|
|
13769
|
+
"list-markets",
|
|
13770
|
+
"get-market",
|
|
13771
|
+
"create-market",
|
|
13772
|
+
"update-market",
|
|
13773
|
+
"delete-market",
|
|
13774
|
+
// Web Presence (4)
|
|
13775
|
+
"list-web-presences",
|
|
13776
|
+
"create-web-presence",
|
|
13777
|
+
"update-web-presence",
|
|
13778
|
+
"delete-web-presence",
|
|
13779
|
+
// Translations (5)
|
|
13780
|
+
"list-shop-locales",
|
|
13781
|
+
"enable-shop-locale",
|
|
13782
|
+
"disable-shop-locale",
|
|
13783
|
+
"register-translations",
|
|
13784
|
+
"remove-translations"
|
|
13785
|
+
]
|
|
13786
|
+
};
|
|
13787
|
+
var ADVANCED_REDIRECTS_MODULE = {
|
|
13788
|
+
name: "advanced-redirects",
|
|
13789
|
+
description: "Bulk URL redirect operations for large-scale SEO management. Includes bulk delete, delete by search, and import capabilities. Requires product-extensions module.",
|
|
13790
|
+
alwaysLoaded: false,
|
|
13791
|
+
dependencies: ["product-extensions"],
|
|
13792
|
+
tools: [
|
|
13793
|
+
"bulk-delete-redirects",
|
|
13794
|
+
"bulk-delete-redirects-by-search",
|
|
13795
|
+
"import-redirects",
|
|
13796
|
+
"submit-redirect-import"
|
|
13797
|
+
]
|
|
13798
|
+
};
|
|
13799
|
+
var MODULE_DEFINITIONS = [
|
|
13800
|
+
CORE_MODULE,
|
|
13801
|
+
STORE_CONTEXT_MODULE,
|
|
13802
|
+
COLLECTIONS_MODULE,
|
|
13803
|
+
PRODUCT_EXTENSIONS_MODULE,
|
|
13804
|
+
CONTENT_MODULE,
|
|
13805
|
+
INTERNATIONAL_MODULE,
|
|
13806
|
+
ADVANCED_REDIRECTS_MODULE
|
|
13807
|
+
];
|
|
13808
|
+
var registry = null;
|
|
13809
|
+
function initializeRegistry() {
|
|
13810
|
+
const modules = /* @__PURE__ */ new Map();
|
|
13811
|
+
const loadedModules = /* @__PURE__ */ new Set();
|
|
13812
|
+
const toolModuleMap = /* @__PURE__ */ new Map();
|
|
13813
|
+
for (const module of MODULE_DEFINITIONS) {
|
|
13814
|
+
modules.set(module.name, module);
|
|
13815
|
+
if (module.alwaysLoaded) {
|
|
13816
|
+
loadedModules.add(module.name);
|
|
13817
|
+
}
|
|
13818
|
+
for (const toolName of module.tools) {
|
|
13819
|
+
toolModuleMap.set(toolName, module.name);
|
|
13820
|
+
}
|
|
13821
|
+
}
|
|
13822
|
+
registry = {
|
|
13823
|
+
modules,
|
|
13824
|
+
loadedModules,
|
|
13825
|
+
toolModuleMap
|
|
13826
|
+
};
|
|
13827
|
+
log.debug(
|
|
13828
|
+
`Module registry initialized: ${modules.size} modules, ${toolModuleMap.size} tools mapped`
|
|
13829
|
+
);
|
|
13830
|
+
return registry;
|
|
13831
|
+
}
|
|
13832
|
+
function getRegistry() {
|
|
13833
|
+
if (!registry) {
|
|
13834
|
+
return initializeRegistry();
|
|
13835
|
+
}
|
|
13836
|
+
return registry;
|
|
13837
|
+
}
|
|
13838
|
+
function getModule(name) {
|
|
13839
|
+
return getRegistry().modules.get(name);
|
|
13840
|
+
}
|
|
13841
|
+
function isModuleLoaded(name) {
|
|
13842
|
+
return getRegistry().loadedModules.has(name);
|
|
13843
|
+
}
|
|
13844
|
+
function getModuleForTool(toolName) {
|
|
13845
|
+
return getRegistry().toolModuleMap.get(toolName);
|
|
13846
|
+
}
|
|
13847
|
+
function getLoadedModules() {
|
|
13848
|
+
return Array.from(getRegistry().loadedModules);
|
|
13849
|
+
}
|
|
13850
|
+
function markModuleLoaded(name) {
|
|
13851
|
+
getRegistry().loadedModules.add(name);
|
|
13852
|
+
log.debug(`Module marked as loaded: ${name}`);
|
|
13853
|
+
}
|
|
13854
|
+
function getToolsForModule(moduleName) {
|
|
13855
|
+
const module = getModule(moduleName);
|
|
13856
|
+
return module ? module.tools : [];
|
|
13857
|
+
}
|
|
13858
|
+
|
|
13859
|
+
// src/tools/modules/loading.ts
|
|
13860
|
+
async function loadModule(moduleName) {
|
|
13861
|
+
log.debug(`Loading module: ${moduleName}`);
|
|
13862
|
+
const module = getModule(moduleName);
|
|
13863
|
+
if (!module) {
|
|
13864
|
+
const availableModules = Array.from(getRegistry().modules.keys()).join(", ");
|
|
13865
|
+
throw new ToolError(
|
|
13866
|
+
`Module '${moduleName}' not found. Available modules: ${availableModules}`,
|
|
13867
|
+
`Use list-modules to see available modules. Valid options: ${availableModules}`
|
|
13868
|
+
);
|
|
13869
|
+
}
|
|
13870
|
+
if (isModuleLoaded(moduleName)) {
|
|
13871
|
+
log.debug(`Module already loaded: ${moduleName}`);
|
|
13872
|
+
return {
|
|
13873
|
+
success: true,
|
|
13874
|
+
moduleName,
|
|
13875
|
+
toolsLoaded: 0,
|
|
13876
|
+
tools: [],
|
|
13877
|
+
dependenciesLoaded: []
|
|
13878
|
+
};
|
|
13879
|
+
}
|
|
13880
|
+
const dependenciesLoaded = [];
|
|
13881
|
+
const allLoadedTools = [];
|
|
13882
|
+
for (const depName of module.dependencies) {
|
|
13883
|
+
if (!isModuleLoaded(depName)) {
|
|
13884
|
+
log.debug(`Loading dependency: ${depName} for ${moduleName}`);
|
|
13885
|
+
const depResult = await loadModule(depName);
|
|
13886
|
+
if (depResult.success && depResult.toolsLoaded > 0) {
|
|
13887
|
+
dependenciesLoaded.push(depName);
|
|
13888
|
+
allLoadedTools.push(...depResult.tools);
|
|
13889
|
+
if (depResult.dependenciesLoaded.length > 0) {
|
|
13890
|
+
dependenciesLoaded.push(...depResult.dependenciesLoaded);
|
|
13891
|
+
}
|
|
13892
|
+
}
|
|
13893
|
+
}
|
|
13894
|
+
}
|
|
13895
|
+
markModuleLoaded(moduleName);
|
|
13896
|
+
for (const toolName of module.tools) {
|
|
13897
|
+
enableTool(toolName);
|
|
13898
|
+
}
|
|
13899
|
+
const result = {
|
|
13900
|
+
success: true,
|
|
13901
|
+
moduleName,
|
|
13902
|
+
toolsLoaded: module.tools.length,
|
|
13903
|
+
tools: [...module.tools],
|
|
13904
|
+
dependenciesLoaded
|
|
13905
|
+
};
|
|
13906
|
+
log.debug(
|
|
13907
|
+
`Module loaded: ${moduleName} (${result.toolsLoaded} tools, ${dependenciesLoaded.length} dependencies)`
|
|
13908
|
+
);
|
|
13909
|
+
return result;
|
|
13910
|
+
}
|
|
13911
|
+
async function loadModules(moduleNames) {
|
|
13912
|
+
const results = [];
|
|
13913
|
+
for (const moduleName of moduleNames) {
|
|
13914
|
+
try {
|
|
13915
|
+
const result = await loadModule(moduleName);
|
|
13916
|
+
results.push(result);
|
|
13917
|
+
} catch (error) {
|
|
13918
|
+
results.push({
|
|
13919
|
+
success: false,
|
|
13920
|
+
moduleName,
|
|
13921
|
+
toolsLoaded: 0,
|
|
13922
|
+
tools: [],
|
|
13923
|
+
dependenciesLoaded: [],
|
|
13924
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
13925
|
+
});
|
|
13926
|
+
}
|
|
13927
|
+
}
|
|
13928
|
+
return results;
|
|
13929
|
+
}
|
|
13930
|
+
function getModuleStatus() {
|
|
13931
|
+
const registry2 = getRegistry();
|
|
13932
|
+
const modules = Array.from(registry2.modules.values()).map((module) => ({
|
|
13933
|
+
name: module.name,
|
|
13934
|
+
description: module.description,
|
|
13935
|
+
toolCount: module.tools.length,
|
|
13936
|
+
loaded: registry2.loadedModules.has(module.name),
|
|
13937
|
+
dependencies: module.dependencies,
|
|
13938
|
+
tools: module.tools
|
|
13939
|
+
}));
|
|
13940
|
+
let totalTools = 0;
|
|
13941
|
+
let loadedTools = 0;
|
|
13942
|
+
for (const module of modules) {
|
|
13943
|
+
totalTools += module.toolCount;
|
|
13944
|
+
if (module.loaded) {
|
|
13945
|
+
loadedTools += module.toolCount;
|
|
13946
|
+
}
|
|
13947
|
+
}
|
|
13948
|
+
return {
|
|
13949
|
+
modules,
|
|
13950
|
+
summary: {
|
|
13951
|
+
totalModules: modules.length,
|
|
13952
|
+
loadedModules: getLoadedModules().length,
|
|
13953
|
+
totalTools,
|
|
13954
|
+
loadedTools
|
|
13955
|
+
}
|
|
13956
|
+
};
|
|
13957
|
+
}
|
|
13958
|
+
|
|
13959
|
+
// src/tools/modules/types.ts
|
|
13960
|
+
import { z as z82 } from "zod";
|
|
13961
|
+
var ToolModuleSchema = z82.object({
|
|
13962
|
+
/** Unique module identifier (kebab-case) */
|
|
13963
|
+
name: z82.string().regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/, {
|
|
13964
|
+
message: "Module name must be kebab-case"
|
|
13965
|
+
}),
|
|
13966
|
+
/** Human-readable description for AI discovery */
|
|
13967
|
+
description: z82.string().min(10, {
|
|
13968
|
+
message: "Description must be at least 10 characters"
|
|
13969
|
+
}),
|
|
13970
|
+
/** Whether this module is loaded at server startup */
|
|
13971
|
+
alwaysLoaded: z82.boolean().default(false),
|
|
13972
|
+
/** Module names that must be loaded before this module */
|
|
13973
|
+
dependencies: z82.array(z82.string()).default([]),
|
|
13974
|
+
/** Tool names belonging to this module */
|
|
13975
|
+
tools: z82.array(z82.string())
|
|
13976
|
+
});
|
|
13977
|
+
var LOADABLE_MODULE_NAMES = [
|
|
13978
|
+
"store-context",
|
|
13979
|
+
"collections",
|
|
13980
|
+
"product-extensions",
|
|
13981
|
+
"content",
|
|
13982
|
+
"international",
|
|
13983
|
+
"advanced-redirects"
|
|
13984
|
+
];
|
|
13985
|
+
var ALL_MODULE_NAMES = ["core", ...LOADABLE_MODULE_NAMES];
|
|
13986
|
+
var ModuleInfoSchema = z82.object({
|
|
13987
|
+
name: z82.string(),
|
|
13988
|
+
description: z82.string(),
|
|
13989
|
+
toolCount: z82.number().int().nonnegative(),
|
|
13990
|
+
loaded: z82.boolean(),
|
|
13991
|
+
dependencies: z82.array(z82.string()),
|
|
13992
|
+
tools: z82.array(z82.string())
|
|
13993
|
+
});
|
|
13994
|
+
var ModuleSummarySchema = z82.object({
|
|
13995
|
+
totalModules: z82.number().int().nonnegative(),
|
|
13996
|
+
loadedModules: z82.number().int().nonnegative(),
|
|
13997
|
+
totalTools: z82.number().int().nonnegative(),
|
|
13998
|
+
loadedTools: z82.number().int().nonnegative()
|
|
13999
|
+
});
|
|
14000
|
+
var ListModulesResponseSchema = z82.object({
|
|
14001
|
+
modules: z82.array(ModuleInfoSchema),
|
|
14002
|
+
summary: ModuleSummarySchema
|
|
14003
|
+
});
|
|
14004
|
+
var ModuleLoadResultSchema = z82.object({
|
|
14005
|
+
success: z82.boolean(),
|
|
14006
|
+
moduleName: z82.string(),
|
|
14007
|
+
toolsLoaded: z82.number().int().nonnegative(),
|
|
14008
|
+
tools: z82.array(z82.string()),
|
|
14009
|
+
dependenciesLoaded: z82.array(z82.string()),
|
|
14010
|
+
error: z82.string().optional()
|
|
14011
|
+
});
|
|
14012
|
+
|
|
14013
|
+
// src/tools/list-modules.ts
|
|
14014
|
+
var inputSchema80 = z83.object({});
|
|
14015
|
+
async function handleListModules(_context, _params) {
|
|
14016
|
+
log.debug("Listing available modules");
|
|
14017
|
+
const status = getModuleStatus();
|
|
14018
|
+
log.debug(
|
|
14019
|
+
`Modules listed: ${status.summary.loadedModules}/${status.summary.totalModules} loaded, ${status.summary.loadedTools}/${status.summary.totalTools} tools available`
|
|
14020
|
+
);
|
|
14021
|
+
return status;
|
|
14022
|
+
}
|
|
14023
|
+
function registerListModulesTool() {
|
|
14024
|
+
registerContextAwareTool(
|
|
14025
|
+
{
|
|
14026
|
+
name: "list-modules",
|
|
14027
|
+
title: "List Available Modules",
|
|
14028
|
+
description: "List all available tool modules and their loading status. Shows module names, descriptions, tool counts, dependencies, and whether each module is loaded. Use this to discover which modules can be loaded to extend functionality. **Note:** The core module is always loaded with essential product and inventory tools.",
|
|
14029
|
+
inputSchema: inputSchema80,
|
|
14030
|
+
outputSchema: ListModulesResponseSchema,
|
|
14031
|
+
category: "store",
|
|
14032
|
+
relationships: {
|
|
14033
|
+
relatedTools: ["load-module"],
|
|
14034
|
+
followUps: ["load-module"]
|
|
14035
|
+
},
|
|
14036
|
+
annotations: {
|
|
14037
|
+
readOnlyHint: true,
|
|
14038
|
+
destructiveHint: false,
|
|
14039
|
+
idempotentHint: true,
|
|
14040
|
+
// Does NOT call Shopify API - internal state only
|
|
14041
|
+
openWorldHint: false
|
|
14042
|
+
}
|
|
14043
|
+
},
|
|
14044
|
+
handleListModules
|
|
14045
|
+
);
|
|
14046
|
+
}
|
|
14047
|
+
|
|
14048
|
+
// src/tools/load-module.ts
|
|
14049
|
+
import { z as z84 } from "zod";
|
|
14050
|
+
var inputSchema81 = z84.object({
|
|
14051
|
+
module: z84.enum(LOADABLE_MODULE_NAMES).describe(
|
|
14052
|
+
"Name of the module to load. Options: store-context, collections, product-extensions, content, international, advanced-redirects"
|
|
14053
|
+
)
|
|
14054
|
+
});
|
|
14055
|
+
async function handleLoadModule(_context, params) {
|
|
14056
|
+
log.debug(`Loading module: ${params.module}`);
|
|
14057
|
+
const result = await loadModule(params.module);
|
|
14058
|
+
if (result.success) {
|
|
14059
|
+
if (result.toolsLoaded > 0) {
|
|
14060
|
+
log.debug(
|
|
14061
|
+
`Module ${params.module} loaded: ${result.toolsLoaded} tools, ${result.dependenciesLoaded.length} dependencies`
|
|
14062
|
+
);
|
|
14063
|
+
} else {
|
|
14064
|
+
log.debug(`Module ${params.module} was already loaded`);
|
|
14065
|
+
}
|
|
14066
|
+
}
|
|
14067
|
+
return result;
|
|
14068
|
+
}
|
|
14069
|
+
function registerLoadModuleTool() {
|
|
14070
|
+
registerContextAwareTool(
|
|
14071
|
+
{
|
|
14072
|
+
name: "load-module",
|
|
14073
|
+
title: "Load Tool Module",
|
|
14074
|
+
description: "Load a tool module to enable additional tools for a specific domain. Dependencies are automatically loaded. Use list-modules to see available modules. **Available modules:** store-context (store configuration), collections (product organization), product-extensions (metafields, images, redirects), content (pages, blogs, articles), international (markets, translations), advanced-redirects (bulk operations). **Note:** Once loaded, modules remain available for the session.",
|
|
14075
|
+
inputSchema: inputSchema81,
|
|
14076
|
+
outputSchema: ModuleLoadResultSchema,
|
|
14077
|
+
category: "store",
|
|
14078
|
+
relationships: {
|
|
14079
|
+
prerequisites: ["list-modules"],
|
|
14080
|
+
relatedTools: ["list-modules"]
|
|
14081
|
+
},
|
|
14082
|
+
annotations: {
|
|
14083
|
+
// Not read-only: changes internal tool availability state
|
|
14084
|
+
readOnlyHint: false,
|
|
14085
|
+
destructiveHint: false,
|
|
14086
|
+
// Idempotent: loading same module twice returns success
|
|
14087
|
+
idempotentHint: true,
|
|
14088
|
+
// Does NOT call Shopify API - internal state only
|
|
14089
|
+
openWorldHint: false
|
|
14090
|
+
}
|
|
14091
|
+
},
|
|
14092
|
+
handleLoadModule
|
|
14093
|
+
);
|
|
14094
|
+
}
|
|
14095
|
+
|
|
14096
|
+
// src/tools/modules/presets.ts
|
|
14097
|
+
import { z as z85 } from "zod";
|
|
14098
|
+
var RolePresetSchema = z85.object({
|
|
14099
|
+
description: z85.string(),
|
|
14100
|
+
modules: z85.array(z85.string())
|
|
14101
|
+
});
|
|
14102
|
+
var ROLE_PRESETS = {
|
|
14103
|
+
"inventory-manager": {
|
|
14104
|
+
description: "Stock management and inventory operations",
|
|
14105
|
+
modules: ["core"]
|
|
14106
|
+
},
|
|
14107
|
+
"product-manager": {
|
|
14108
|
+
description: "Full product catalog management",
|
|
14109
|
+
modules: ["core", "store-context", "collections", "product-extensions"]
|
|
14110
|
+
},
|
|
14111
|
+
"content-manager": {
|
|
14112
|
+
description: "Pages, blogs, and content marketing",
|
|
14113
|
+
modules: ["core", "content"]
|
|
14114
|
+
},
|
|
14115
|
+
"seo-specialist": {
|
|
14116
|
+
description: "SEO optimization and URL management",
|
|
14117
|
+
modules: ["core", "collections", "product-extensions", "advanced-redirects"]
|
|
14118
|
+
},
|
|
14119
|
+
"international-manager": {
|
|
14120
|
+
description: "Multi-market and translation management",
|
|
14121
|
+
modules: ["core", "store-context", "international", "collections"]
|
|
14122
|
+
},
|
|
14123
|
+
"full-access": {
|
|
14124
|
+
description: "All tools for store administrators",
|
|
14125
|
+
modules: [
|
|
14126
|
+
"core",
|
|
14127
|
+
"store-context",
|
|
14128
|
+
"collections",
|
|
14129
|
+
"product-extensions",
|
|
14130
|
+
"content",
|
|
14131
|
+
"international",
|
|
14132
|
+
"advanced-redirects"
|
|
14133
|
+
]
|
|
14134
|
+
}
|
|
14135
|
+
};
|
|
14136
|
+
var VALID_ROLE_NAMES = Object.keys(ROLE_PRESETS);
|
|
14137
|
+
function isValidRoleName(name) {
|
|
14138
|
+
return name in ROLE_PRESETS;
|
|
14139
|
+
}
|
|
14140
|
+
function getPresetModules(role) {
|
|
14141
|
+
return [...ROLE_PRESETS[role].modules];
|
|
14142
|
+
}
|
|
14143
|
+
|
|
14144
|
+
// src/tools/suggestions.ts
|
|
14145
|
+
var TOOL_SUGGESTIONS = {
|
|
14146
|
+
// Product tools β suggest product-extensions and collections
|
|
14147
|
+
"create-product": [
|
|
14148
|
+
{ name: "product-extensions", reason: "Add metafields, manage images" },
|
|
14149
|
+
{ name: "collections", reason: "Organize in collections" }
|
|
14150
|
+
],
|
|
14151
|
+
"get-product": [{ name: "product-extensions", reason: "View metafields, images" }],
|
|
14152
|
+
"update-product": [
|
|
14153
|
+
{ name: "product-extensions", reason: "Update metafields" },
|
|
14154
|
+
{ name: "collections", reason: "Manage collection membership" }
|
|
14155
|
+
],
|
|
14156
|
+
"list-products": [{ name: "collections", reason: "Categorize and filter by collection" }],
|
|
14157
|
+
"delete-product": [
|
|
14158
|
+
{ name: "product-extensions", reason: "Check redirects, clean up metafields" }
|
|
14159
|
+
],
|
|
14160
|
+
"update-product-variant": [{ name: "product-extensions", reason: "Manage variant metafields" }],
|
|
14161
|
+
"add-product-image": [{ name: "product-extensions", reason: "Reorder images, update alt text" }],
|
|
14162
|
+
// Inventory tools β suggest store-context for location info
|
|
14163
|
+
"get-inventory": [{ name: "store-context", reason: "Location information" }],
|
|
14164
|
+
"update-inventory": [{ name: "store-context", reason: "Location details" }],
|
|
14165
|
+
"list-low-inventory": [{ name: "store-context", reason: "Location context" }],
|
|
14166
|
+
"get-bulk-inventory": [{ name: "store-context", reason: "Location information" }],
|
|
14167
|
+
// Store info tools β suggest store-context for full configuration
|
|
14168
|
+
"get-store-info": [{ name: "store-context", reason: "Full store configuration" }],
|
|
14169
|
+
"get-store-limits": [{ name: "store-context", reason: "Additional store details" }]
|
|
14170
|
+
};
|
|
14171
|
+
function getSuggestionsForTool(toolName) {
|
|
14172
|
+
const suggestions = TOOL_SUGGESTIONS[toolName];
|
|
14173
|
+
if (!suggestions || suggestions.length === 0) {
|
|
14174
|
+
return null;
|
|
14175
|
+
}
|
|
14176
|
+
const unloadedSuggestions = suggestions.filter((s) => !isModuleLoaded(s.name));
|
|
14177
|
+
if (unloadedSuggestions.length === 0) {
|
|
14178
|
+
return null;
|
|
14179
|
+
}
|
|
14180
|
+
return {
|
|
14181
|
+
modules: unloadedSuggestions,
|
|
14182
|
+
hint: "Use load-module to enable these tools"
|
|
14183
|
+
};
|
|
14184
|
+
}
|
|
14185
|
+
|
|
13561
14186
|
// src/tools/index.ts
|
|
13562
14187
|
function setupToolHandlers(server) {
|
|
13563
14188
|
server.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -13574,18 +14199,23 @@ function setupToolHandlers(server) {
|
|
|
13574
14199
|
};
|
|
13575
14200
|
});
|
|
13576
14201
|
server.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
13577
|
-
const { name, arguments: params } = request.params;
|
|
13578
|
-
log.debug(`tools/call received: ${
|
|
14202
|
+
const { name: rawName, arguments: params } = request.params;
|
|
14203
|
+
log.debug(`tools/call received: ${rawName}`);
|
|
14204
|
+
const nameParts = rawName.split(":");
|
|
14205
|
+
const name = nameParts.length > 1 ? nameParts[nameParts.length - 1] : rawName;
|
|
14206
|
+
if (name !== rawName) {
|
|
14207
|
+
log.debug(`Stripped prefix from tool name: "${rawName}" -> "${name}"`);
|
|
14208
|
+
}
|
|
13579
14209
|
const tool = getToolByName(name);
|
|
13580
14210
|
if (!tool) {
|
|
13581
14211
|
const availableTools = getRegisteredTools();
|
|
13582
14212
|
const toolList = availableTools.length > 0 ? `Available tools: ${availableTools.map((t) => t.name).join(", ")}` : "No tools are currently registered.";
|
|
13583
|
-
log.warn(`Tool not found: ${name}`);
|
|
14213
|
+
log.warn(`Tool not found: ${name}${name !== rawName ? ` (received as "${rawName}")` : ""}`);
|
|
13584
14214
|
return {
|
|
13585
14215
|
content: [
|
|
13586
14216
|
{
|
|
13587
14217
|
type: "text",
|
|
13588
|
-
text: `Error: Tool "${name}" not found.
|
|
14218
|
+
text: `Error: Tool "${name}" not found${name !== rawName ? ` (received as "${rawName}")` : ""}.
|
|
13589
14219
|
|
|
13590
14220
|
Suggestion: Use tools/list to see available tools. ${toolList}`
|
|
13591
14221
|
}
|
|
@@ -13593,8 +14223,42 @@ Suggestion: Use tools/list to see available tools. ${toolList}`
|
|
|
13593
14223
|
isError: true
|
|
13594
14224
|
};
|
|
13595
14225
|
}
|
|
14226
|
+
if (!isToolEnabled(name)) {
|
|
14227
|
+
const moduleName = getModuleForTool(name);
|
|
14228
|
+
const suggestion = moduleName ? `Load the '${moduleName}' module first: use load-module with module: "${moduleName}"` : "This tool is currently disabled. Use list-modules to see available modules.";
|
|
14229
|
+
log.debug(`Tool "${name}" is disabled, suggesting module: ${moduleName || "unknown"}`);
|
|
14230
|
+
return {
|
|
14231
|
+
content: [
|
|
14232
|
+
{
|
|
14233
|
+
type: "text",
|
|
14234
|
+
text: `Error: Tool "${name}" is not currently loaded.
|
|
14235
|
+
|
|
14236
|
+
Suggestion: ${suggestion}`
|
|
14237
|
+
}
|
|
14238
|
+
],
|
|
14239
|
+
isError: true
|
|
14240
|
+
};
|
|
14241
|
+
}
|
|
13596
14242
|
try {
|
|
13597
14243
|
const response = await tool.handler(params ?? {});
|
|
14244
|
+
const config = getConfig();
|
|
14245
|
+
if (isLazyLoadingEnabled(config) && !response.isError) {
|
|
14246
|
+
const suggestions = getSuggestionsForTool(name);
|
|
14247
|
+
if (suggestions) {
|
|
14248
|
+
const suggestionText = `
|
|
14249
|
+
---
|
|
14250
|
+
\u{1F4A1} **Related modules available:**
|
|
14251
|
+
${suggestions.modules.map((m) => `- **${m.name}**: ${m.reason}`).join("\n")}
|
|
14252
|
+
|
|
14253
|
+
_${suggestions.hint}_`;
|
|
14254
|
+
if (Array.isArray(response.content)) {
|
|
14255
|
+
response.content.push({
|
|
14256
|
+
type: "text",
|
|
14257
|
+
text: suggestionText
|
|
14258
|
+
});
|
|
14259
|
+
}
|
|
14260
|
+
}
|
|
14261
|
+
}
|
|
13598
14262
|
return response;
|
|
13599
14263
|
} catch (error) {
|
|
13600
14264
|
log.error(`Unexpected error in tool ${name}:`, error instanceof Error ? error : void 0);
|
|
@@ -13614,6 +14278,7 @@ Suggestion: Please try again or contact support if the issue persists.`
|
|
|
13614
14278
|
}
|
|
13615
14279
|
function registerAllTools(server) {
|
|
13616
14280
|
setupToolHandlers(server);
|
|
14281
|
+
initializeRegistry();
|
|
13617
14282
|
registerAddProductImageTool();
|
|
13618
14283
|
registerCreateProductTool();
|
|
13619
14284
|
registerDeleteProductTool();
|
|
@@ -13693,8 +14358,69 @@ function registerAllTools(server) {
|
|
|
13693
14358
|
registerGetStoreTaxesTool();
|
|
13694
14359
|
registerGetStorePoliciesTool();
|
|
13695
14360
|
registerGetStoreAlertsTool();
|
|
13696
|
-
|
|
13697
|
-
|
|
14361
|
+
registerListModulesTool();
|
|
14362
|
+
registerLoadModuleTool();
|
|
14363
|
+
const totalToolCount = getToolNames().length;
|
|
14364
|
+
const config = getConfig();
|
|
14365
|
+
if (isLazyLoadingEnabled(config)) {
|
|
14366
|
+
applyLazyLoading();
|
|
14367
|
+
const roleResult = applyRolePreset(config);
|
|
14368
|
+
const enabledCount = getRegisteredTools().length;
|
|
14369
|
+
if (roleResult.applied) {
|
|
14370
|
+
log.info(
|
|
14371
|
+
`Role preset '${roleResult.role}' applied: ${enabledCount} tools active (${roleResult.modulesLoaded.length} modules loaded)`
|
|
14372
|
+
);
|
|
14373
|
+
} else {
|
|
14374
|
+
log.info(
|
|
14375
|
+
`Lazy loading enabled: ${enabledCount} core tools active, ${totalToolCount - enabledCount} tools available via modules`
|
|
14376
|
+
);
|
|
14377
|
+
}
|
|
14378
|
+
} else {
|
|
14379
|
+
log.info(`Lazy loading disabled (legacy mode): ${totalToolCount} tools registered`);
|
|
14380
|
+
}
|
|
14381
|
+
}
|
|
14382
|
+
function applyLazyLoading() {
|
|
14383
|
+
const coreTools = new Set(getToolsForModule("core"));
|
|
14384
|
+
const allToolNames = getToolNames();
|
|
14385
|
+
let disabledCount = 0;
|
|
14386
|
+
for (const toolName of allToolNames) {
|
|
14387
|
+
if (!coreTools.has(toolName)) {
|
|
14388
|
+
disableTool(toolName);
|
|
14389
|
+
disabledCount++;
|
|
14390
|
+
}
|
|
14391
|
+
}
|
|
14392
|
+
log.debug(`Lazy loading: disabled ${disabledCount} non-core tools`);
|
|
14393
|
+
}
|
|
14394
|
+
function applyRolePreset(config) {
|
|
14395
|
+
const role = getConfiguredRole(config);
|
|
14396
|
+
if (!role) {
|
|
14397
|
+
return { applied: false, modulesLoaded: [] };
|
|
14398
|
+
}
|
|
14399
|
+
if (!isValidRoleName(role)) {
|
|
14400
|
+
log.warn(
|
|
14401
|
+
`Invalid role preset '${role}'. Valid roles: inventory-manager, product-manager, content-manager, seo-specialist, international-manager, full-access`
|
|
14402
|
+
);
|
|
14403
|
+
return {
|
|
14404
|
+
applied: false,
|
|
14405
|
+
modulesLoaded: [],
|
|
14406
|
+
warning: `Invalid role '${role}' - continuing with core tools only`
|
|
14407
|
+
};
|
|
14408
|
+
}
|
|
14409
|
+
const presetModules = getPresetModules(role);
|
|
14410
|
+
const modulesToLoad = presetModules.filter((m) => m !== "core");
|
|
14411
|
+
if (modulesToLoad.length === 0) {
|
|
14412
|
+
log.debug(`Role preset '${role}' requires only core module`);
|
|
14413
|
+
return { applied: true, role, modulesLoaded: [] };
|
|
14414
|
+
}
|
|
14415
|
+
loadModules(modulesToLoad).then((results) => {
|
|
14416
|
+
const successful = results.filter((r) => r.success).map((r) => r.moduleName);
|
|
14417
|
+
const failed = results.filter((r) => !r.success).map((r) => r.moduleName);
|
|
14418
|
+
if (failed.length > 0) {
|
|
14419
|
+
log.warn(`Role preset '${role}': failed to load modules: ${failed.join(", ")}`);
|
|
14420
|
+
}
|
|
14421
|
+
log.debug(`Role preset '${role}': loaded modules: ${successful.join(", ")}`);
|
|
14422
|
+
});
|
|
14423
|
+
return { applied: true, role, modulesLoaded: modulesToLoad };
|
|
13698
14424
|
}
|
|
13699
14425
|
|
|
13700
14426
|
// src/transports/stdio.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anton.andrusenko/shopify-mcp-admin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
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",
|