@anton.andrusenko/shopify-mcp-admin 1.1.1 → 1.1.3

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 CHANGED
@@ -40,9 +40,25 @@ npx @anton.andrusenko/shopify-mcp-admin
40
40
  npm install -g @anton.andrusenko/shopify-mcp-admin
41
41
  ```
42
42
 
43
- ### Configuration
43
+ ### Setup Wizard (Recommended)
44
44
 
45
- Set your environment variables:
45
+ The easiest way to get started is with the interactive setup wizard:
46
+
47
+ ```bash
48
+ npx @anton.andrusenko/shopify-mcp-admin init
49
+ ```
50
+
51
+ The wizard will:
52
+ 1. **Prompt for your store URL** and validate the format
53
+ 2. **Choose authentication method** — Legacy token or OAuth 2.0
54
+ 3. **Test the connection** — Verifies credentials before saving
55
+ 4. **Select your AI client** — Claude Desktop, Cursor, Windsurf, VS Code, LibreChat, or OpenAI
56
+ 5. **Configure tool loading** — All tools, role preset, or lazy loading
57
+ 6. **Generate and save config** — Creates the appropriate config file for your client
58
+
59
+ ### Manual Configuration
60
+
61
+ Alternatively, set environment variables directly:
46
62
 
47
63
  ```bash
48
64
  export SHOPIFY_STORE_URL=your-store.myshopify.com
@@ -191,6 +207,198 @@ Quit and reopen Claude Desktop. You should see "shopify" in the MCP servers list
191
207
 
192
208
  ---
193
209
 
210
+ ## 💬 LibreChat Integration
211
+
212
+ [LibreChat](https://www.librechat.ai/) is an open-source AI chat interface that supports MCP servers natively. This section provides a complete guide to set up LibreChat with the Shopify MCP server.
213
+
214
+ ### Prerequisites
215
+
216
+ - **Docker Desktop** — [Download](https://www.docker.com/products/docker-desktop) and ensure it's running
217
+ - **Git** — For cloning repositories
218
+ - **LLM API Key** — OpenAI or Anthropic API key for the chat functionality
219
+
220
+ ### Quick Setup (Recommended)
221
+
222
+ Use the included setup script for automated installation:
223
+
224
+ ```bash
225
+ # Clone this repo (if you haven't already)
226
+ git clone https://github.com/AntonAndrusenko/shopify-mcp-admin.git
227
+ cd shopify-mcp-admin
228
+
229
+ # Run the setup script
230
+ ./scripts/setup-librechat.sh
231
+ ```
232
+
233
+ The script will:
234
+ 1. Check Docker is installed and running
235
+ 2. Clone LibreChat to `~/LibreChat`
236
+ 3. Prompt for your Shopify credentials (store URL, access token or OAuth)
237
+ 4. Create `librechat.yaml` with MCP server configuration
238
+ 5. Create `docker-compose.override.yml` to mount the config
239
+ 6. Pull Docker images and start all containers
240
+ 7. Verify the Shopify MCP server loads successfully
241
+
242
+ **Script Options:**
243
+ ```bash
244
+ # Install to a custom directory
245
+ ./scripts/setup-librechat.sh --install-dir /path/to/librechat
246
+
247
+ # Use local development build instead of npm package
248
+ ./scripts/setup-librechat.sh --use-local
249
+
250
+ # Show help
251
+ ./scripts/setup-librechat.sh --help
252
+ ```
253
+
254
+ ### Manual Setup
255
+
256
+ <details>
257
+ <summary>Click to expand manual installation steps</summary>
258
+
259
+ #### Step 1: Clone and Configure LibreChat
260
+
261
+ ```bash
262
+ # Clone LibreChat
263
+ git clone https://github.com/danny-avila/LibreChat.git
264
+ cd LibreChat
265
+
266
+ # Create environment file
267
+ cp .env.example .env
268
+ ```
269
+
270
+ #### Step 2: Create MCP Server Configuration
271
+
272
+ Create `librechat.yaml` in the LibreChat directory:
273
+
274
+ ```yaml
275
+ version: 1.2.1
276
+
277
+ mcpServers:
278
+ shopify:
279
+ type: stdio
280
+ command: npx
281
+ args:
282
+ - -y
283
+ - "@anton.andrusenko/shopify-mcp-admin"
284
+ env:
285
+ SHOPIFY_STORE_URL: "your-store.myshopify.com"
286
+ # Option 1: Access Token
287
+ SHOPIFY_ACCESS_TOKEN: "shpat_xxxxx"
288
+ # Option 2: OAuth (comment out ACCESS_TOKEN, uncomment these)
289
+ # SHOPIFY_CLIENT_ID: "your_client_id"
290
+ # SHOPIFY_CLIENT_SECRET: "your_client_secret"
291
+ LOG_LEVEL: "info"
292
+ serverInstructions: true
293
+ timeout: 30000
294
+ initTimeout: 15000
295
+ ```
296
+
297
+ #### Step 3: Mount Configuration in Docker
298
+
299
+ Create `docker-compose.override.yml`:
300
+
301
+ ```yaml
302
+ services:
303
+ api:
304
+ volumes:
305
+ - type: bind
306
+ source: ./librechat.yaml
307
+ target: /app/librechat.yaml
308
+ ```
309
+
310
+ #### Step 4: Start LibreChat
311
+
312
+ ```bash
313
+ # Pull the latest images
314
+ docker compose pull
315
+
316
+ # Start all services
317
+ docker compose up -d
318
+
319
+ # Verify containers are running
320
+ docker compose ps
321
+ ```
322
+
323
+ #### Step 5: Verify MCP Server Loaded
324
+
325
+ ```bash
326
+ # Check logs for MCP initialization
327
+ docker compose logs api | grep -i "mcp"
328
+
329
+ # You should see:
330
+ # [MCP][shopify] Initialized in: XXXXms
331
+ # MCP servers initialized successfully. Added 79 MCP tools.
332
+ ```
333
+
334
+ </details>
335
+
336
+ ### After Installation: Enable MCP in LibreChat
337
+
338
+ Once LibreChat is running, follow these steps to use the Shopify MCP server:
339
+
340
+ #### 1. Create an Account
341
+ - Open http://localhost:3080 in your browser
342
+ - Click "Sign up" and create an account
343
+ - Log in with your new credentials
344
+
345
+ #### 2. Add an LLM API Key
346
+ - Click your **profile avatar** (bottom-left)
347
+ - Select **Settings**
348
+ - Go to **User Provider Keys**
349
+ - Add your API key:
350
+ - **OpenAI**: Paste your `sk-...` key
351
+ - **Anthropic**: Paste your `sk-ant-...` key
352
+ - Click **Save**
353
+
354
+ #### 3. Select the Shopify MCP Server
355
+ - Start a **New Chat**
356
+ - Look for the **MCP Servers** dropdown in the chat input area (shows a plug icon)
357
+ - Click it and select **"shopify"**
358
+ - You should see a "shopify" badge appear, indicating the MCP server is active
359
+
360
+ #### 4. Test the Integration
361
+ Try these prompts to verify everything works:
362
+
363
+ ```
364
+ "What's my store name and what plan am I on?"
365
+ "List my products"
366
+ "Show me products with low inventory"
367
+ ```
368
+
369
+ ### Troubleshooting
370
+
371
+ | Issue | Solution |
372
+ |-------|----------|
373
+ | MCP server not appearing | Check logs: `docker compose logs api \| grep -i mcp` |
374
+ | "shopify" badge not visible | Refresh the page, or restart: `docker compose restart` |
375
+ | Tools not working | Verify Shopify credentials in `librechat.yaml` |
376
+ | Docker won't start | Ensure Docker Desktop is running |
377
+ | Permission denied on script | Run: `chmod +x ./scripts/setup-librechat.sh` |
378
+
379
+ ### Useful Commands
380
+
381
+ ```bash
382
+ # View LibreChat logs
383
+ cd ~/LibreChat && docker compose logs api -f
384
+
385
+ # View only MCP-related logs
386
+ cd ~/LibreChat && docker compose logs api | grep -i "mcp\|shopify"
387
+
388
+ # Restart LibreChat (after config changes)
389
+ cd ~/LibreChat && docker compose restart
390
+
391
+ # Stop LibreChat
392
+ cd ~/LibreChat && docker compose down
393
+
394
+ # Update LibreChat
395
+ cd ~/LibreChat && git pull && docker compose pull && docker compose up -d
396
+ ```
397
+
398
+ > 📄 **Advanced Configuration:** See `librechat.yaml.example` for multi-store setups, role presets, and HTTP/SSE transport modes.
399
+
400
+ ---
401
+
194
402
  ## 🌐 OpenAI/ChatGPT Integration
195
403
 
196
404
  For OpenAI function calling or ChatGPT plugins, use HTTP transport mode.
package/dist/index.js CHANGED
@@ -5675,7 +5675,7 @@ async function createMarket(input) {
5675
5675
  };
5676
5676
  }
5677
5677
  if (input.enabled !== void 0) {
5678
- graphqlInput.status = input.enabled ? "ACTIVE" : "INACTIVE";
5678
+ graphqlInput.status = input.enabled ? "ACTIVE" : "DRAFT";
5679
5679
  }
5680
5680
  if (input.currencySettings) {
5681
5681
  const currencyInput = {};
@@ -5723,7 +5723,7 @@ async function updateMarket(marketId, input) {
5723
5723
  graphqlInput.handle = input.handle;
5724
5724
  }
5725
5725
  if (input.enabled !== void 0) {
5726
- graphqlInput.status = input.enabled ? "ACTIVE" : "INACTIVE";
5726
+ graphqlInput.status = input.enabled ? "ACTIVE" : "DRAFT";
5727
5727
  }
5728
5728
  if (input.countryCodesToAdd && input.countryCodesToAdd.length > 0) {
5729
5729
  graphqlInput.conditions = graphqlInput.conditions || {};
@@ -5959,7 +5959,7 @@ var inputSchema6 = z8.object({
5959
5959
  'Array of ISO 3166-1 alpha-2 country codes to include in this market. Example: ["CA"] for Canada, ["FR", "DE", "IT"] for multiple European countries'
5960
5960
  ),
5961
5961
  enabled: z8.boolean().optional().default(false).describe(
5962
- "Whether the market should be enabled (status: ACTIVE). Default: false (creates market as INACTIVE/draft). Set to true only when ready to make the market live. \u26A0\uFE0F ACTIVE markets are immediately visible to customers in those regions."
5962
+ "Whether the market should be enabled (status: ACTIVE). Default: false (creates market as DRAFT). Set to true only when ready to make the market live. \u26A0\uFE0F ACTIVE markets are immediately visible to customers in those regions."
5963
5963
  ),
5964
5964
  baseCurrency: z8.string().length(3).optional().describe(
5965
5965
  'Base currency code for the market (ISO 4217). Example: "USD", "EUR", "CAD", "GBP". If not set, uses store default currency.'
@@ -6006,7 +6006,7 @@ function registerCreateMarketTool() {
6006
6006
  {
6007
6007
  name: "create-market",
6008
6008
  title: "Create Market",
6009
- description: 'Create a new market for regional targeting. Markets group one or more regions (countries) for localized shopping experiences. Provide a name (required) and optionally: handle, country codes, currency settings, and enabled status. \u26A0\uFE0F **Note:** Markets are created as INACTIVE by default (enabled=false). Use update-market to activate when ready, or set enabled=true if you want it live immediately. **Currency:** Set baseCurrency (e.g., "CAD") and localCurrencies for multi-currency support. **Typical workflow:** create-market \u2192 create-web-presence (configure SEO URLs) \u2192 enable-shop-locale (add languages) \u2192 update-market (enable when ready). **Prerequisites:** None. Store must have Markets feature enabled on their plan. **Follow-ups:** get-market, update-market, create-web-presence.',
6009
+ description: 'Create a new market for regional targeting. Markets group one or more regions (countries) for localized shopping experiences. Provide a name (required) and optionally: handle, country codes, currency settings, and enabled status. \u26A0\uFE0F **Note:** Markets are created as DRAFT by default (enabled=false). Use update-market to activate when ready, or set enabled=true if you want it live immediately. **Currency:** Set baseCurrency (e.g., "CAD") and localCurrencies for multi-currency support. **Typical workflow:** create-market \u2192 create-web-presence (configure SEO URLs) \u2192 enable-shop-locale (add languages) \u2192 update-market (enable when ready). **Prerequisites:** None. Store must have Markets feature enabled on their plan. **Follow-ups:** get-market, update-market, create-web-presence.',
6010
6010
  inputSchema: inputSchema6,
6011
6011
  outputSchema: outputSchema6,
6012
6012
  // AI Agent Optimization
@@ -9168,7 +9168,7 @@ var outputSchema22 = z24.object({
9168
9168
  name: z24.string().describe("Market name (not shown to customers)"),
9169
9169
  handle: z24.string().describe("URL handle"),
9170
9170
  enabled: z24.boolean().describe("Whether the market is enabled"),
9171
- status: z24.enum(["ACTIVE", "INACTIVE"]).describe("Market status"),
9171
+ status: z24.enum(["ACTIVE", "DRAFT"]).describe("Market status"),
9172
9172
  type: z24.string().describe("Market type (PRIMARY, SINGLE_COUNTRY, MULTI_COUNTRY, CONTINENT)"),
9173
9173
  regions: z24.array(
9174
9174
  z24.object({
@@ -9831,7 +9831,7 @@ var outputSchema30 = z32.object({
9831
9831
  name: z32.string().describe("Market name (not shown to customers)"),
9832
9832
  handle: z32.string().describe("URL handle"),
9833
9833
  enabled: z32.boolean().describe("Whether the market is enabled"),
9834
- status: z32.enum(["ACTIVE", "INACTIVE"]).describe("Market status"),
9834
+ status: z32.enum(["ACTIVE", "DRAFT"]).describe("Market status"),
9835
9835
  type: z32.string().describe("Market type (PRIMARY, SINGLE_COUNTRY, MULTI_COUNTRY, CONTINENT)"),
9836
9836
  regions: z32.array(
9837
9837
  z32.object({
@@ -14691,11 +14691,17 @@ function showHelp() {
14691
14691
 
14692
14692
  Usage:
14693
14693
  shopify-mcp-admin [options]
14694
+ shopify-mcp-admin init Interactive setup wizard
14694
14695
 
14695
14696
  Options:
14696
14697
  --help, -h Show this help message
14697
14698
  --version, -v Show version number
14698
14699
 
14700
+ Commands:
14701
+ init, setup Run the interactive setup wizard
14702
+ Configures credentials and generates config files
14703
+ for Claude Desktop, Cursor, LibreChat, etc.
14704
+
14699
14705
  Environment Variables:
14700
14706
  SHOPIFY_STORE_URL (required) Your Shopify store domain
14701
14707
  SHOPIFY_ACCESS_TOKEN (required) Admin API access token
@@ -14704,7 +14710,8 @@ Environment Variables:
14704
14710
  DEBUG Enable debug logging (1 or true)
14705
14711
 
14706
14712
  Examples:
14707
- npx shopify-mcp-admin
14713
+ npx shopify-mcp-admin init # Setup wizard
14714
+ npx shopify-mcp-admin # Start server (requires env vars)
14708
14715
  TRANSPORT=http PORT=3000 shopify-mcp-admin`);
14709
14716
  process.exit(0);
14710
14717
  }
@@ -14712,6 +14719,11 @@ function showVersion() {
14712
14719
  console.log(getPackageVersion());
14713
14720
  process.exit(0);
14714
14721
  }
14722
+ async function runSetup() {
14723
+ const { runSetupWizard } = await import("./setup-wizard-PVLOC3DU.js");
14724
+ await runSetupWizard();
14725
+ process.exit(0);
14726
+ }
14715
14727
  var args = process.argv.slice(2);
14716
14728
  if (args.includes("--help") || args.includes("-h")) {
14717
14729
  showHelp();
@@ -14719,6 +14731,12 @@ if (args.includes("--help") || args.includes("-h")) {
14719
14731
  if (args.includes("--version") || args.includes("-v")) {
14720
14732
  showVersion();
14721
14733
  }
14734
+ if (args.includes("init") || args.includes("setup") || args.includes("--setup")) {
14735
+ runSetup().catch((error) => {
14736
+ console.error("Setup failed:", error);
14737
+ process.exit(1);
14738
+ });
14739
+ }
14722
14740
  async function main() {
14723
14741
  try {
14724
14742
  const config = getConfig();
@@ -0,0 +1,697 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/setup-wizard.ts
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
+ import { homedir } from "os";
6
+ import { dirname, join } from "path";
7
+ import { confirm, input, password, select } from "@inquirer/prompts";
8
+ var colors = {
9
+ reset: "\x1B[0m",
10
+ bold: "\x1B[1m",
11
+ dim: "\x1B[2m",
12
+ // Shopify-inspired colors
13
+ green: "\x1B[38;5;82m",
14
+ // Shopify green
15
+ darkGreen: "\x1B[38;5;34m",
16
+ cyan: "\x1B[38;5;87m",
17
+ yellow: "\x1B[38;5;226m",
18
+ red: "\x1B[38;5;196m",
19
+ magenta: "\x1B[38;5;207m",
20
+ blue: "\x1B[38;5;75m",
21
+ white: "\x1B[38;5;255m",
22
+ gray: "\x1B[38;5;245m"
23
+ };
24
+ var c = {
25
+ green: (s) => `${colors.green}${s}${colors.reset}`,
26
+ darkGreen: (s) => `${colors.darkGreen}${s}${colors.reset}`,
27
+ cyan: (s) => `${colors.cyan}${s}${colors.reset}`,
28
+ yellow: (s) => `${colors.yellow}${s}${colors.reset}`,
29
+ red: (s) => `${colors.red}${s}${colors.reset}`,
30
+ magenta: (s) => `${colors.magenta}${s}${colors.reset}`,
31
+ blue: (s) => `${colors.blue}${s}${colors.reset}`,
32
+ white: (s) => `${colors.white}${s}${colors.reset}`,
33
+ gray: (s) => `${colors.gray}${s}${colors.reset}`,
34
+ bold: (s) => `${colors.bold}${s}${colors.reset}`,
35
+ dim: (s) => `${colors.dim}${s}${colors.reset}`
36
+ };
37
+ function gradientLine(line) {
38
+ const gradientColors = [
39
+ "\x1B[38;5;82m",
40
+ // green
41
+ "\x1B[38;5;83m",
42
+ "\x1B[38;5;84m",
43
+ "\x1B[38;5;85m",
44
+ "\x1B[38;5;86m",
45
+ "\x1B[38;5;87m"
46
+ // cyan
47
+ ];
48
+ let result = "";
49
+ const chars = [...line];
50
+ for (let i = 0; i < chars.length; i++) {
51
+ const colorIndex = Math.floor(i / chars.length * gradientColors.length);
52
+ result += `${gradientColors[colorIndex]}${chars[i]}`;
53
+ }
54
+ return result + colors.reset;
55
+ }
56
+ var BANNER = `
57
+ ${c.green("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")}
58
+ ${c.green("\u2502")} ${c.green("\u2502")}
59
+ ${c.green("\u2502")} ${gradientLine("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557")} ${c.green("\u2502")}
60
+ ${c.green("\u2502")} ${gradientLine("\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D")} ${c.green("\u2502")}
61
+ ${c.green("\u2502")} ${gradientLine("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2588\u2554\u255D ")} ${c.green("\u2502")}
62
+ ${c.green("\u2502")} ${gradientLine("\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2588\u2588\u2554\u255D ")} ${c.green("\u2502")}
63
+ ${c.green("\u2502")} ${gradientLine("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 ")} ${c.green("\u2502")}
64
+ ${c.green("\u2502")} ${gradientLine("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D ")} ${c.green("\u2502")}
65
+ ${c.green("\u2502")} ${c.green("\u2502")}
66
+ ${c.green("\u2502")} ${c.bold("MCP ADMIN SERVER")} ${c.green("\u2502")}
67
+ ${c.green("\u2502")} ${c.green("\u2502")}
68
+ ${c.green("\u2502")} ${c.cyan("\u{1F6CD}\uFE0F Connect AI Agents to Your Shopify Store")} ${c.green("\u2502")}
69
+ ${c.green("\u2502")} ${c.green("\u2502")}
70
+ ${c.green("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F")}
71
+ `;
72
+ function validateStoreUrl(value) {
73
+ const trimmed = value.trim().toLowerCase();
74
+ const cleaned = trimmed.replace(/^https?:\/\//, "");
75
+ if (!cleaned) {
76
+ return "Store URL is required";
77
+ }
78
+ if (!cleaned.endsWith(".myshopify.com")) {
79
+ return "Must be a valid myshopify.com domain (e.g., your-store.myshopify.com)";
80
+ }
81
+ return true;
82
+ }
83
+ function validateAccessToken(value) {
84
+ const trimmed = value.trim();
85
+ if (!trimmed) {
86
+ return "Access token is required";
87
+ }
88
+ if (!trimmed.startsWith("shpat_")) {
89
+ return 'Access token should start with "shpat_"';
90
+ }
91
+ if (trimmed.length < 20) {
92
+ return "Access token seems too short";
93
+ }
94
+ return true;
95
+ }
96
+ function cleanStoreUrl(url) {
97
+ return url.trim().toLowerCase().replace(/^https?:\/\//, "");
98
+ }
99
+ var Spinner = class {
100
+ frames = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
101
+ frameIndex = 0;
102
+ intervalId = null;
103
+ message;
104
+ constructor(message) {
105
+ this.message = message;
106
+ }
107
+ start() {
108
+ process.stdout.write("\x1B[?25l");
109
+ this.intervalId = setInterval(() => {
110
+ const frame = this.frames[this.frameIndex];
111
+ process.stdout.write(`\r${c.cyan(frame)} ${this.message}`);
112
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
113
+ }, 100);
114
+ }
115
+ succeed(message) {
116
+ this.stop();
117
+ console.log(`\r${c.green("\u2714")} ${message}`);
118
+ }
119
+ fail(message) {
120
+ this.stop();
121
+ console.log(`\r${c.red("\u2716")} ${message}`);
122
+ }
123
+ stop() {
124
+ if (this.intervalId) {
125
+ clearInterval(this.intervalId);
126
+ this.intervalId = null;
127
+ }
128
+ process.stdout.write("\x1B[?25h");
129
+ process.stdout.write("\r\x1B[K");
130
+ }
131
+ };
132
+ async function testConnection(config) {
133
+ const spinner = new Spinner(`Verifying connection to ${config.storeUrl}...`);
134
+ spinner.start();
135
+ try {
136
+ const query = `{
137
+ shop {
138
+ name
139
+ plan { displayName }
140
+ email
141
+ }
142
+ }`;
143
+ let accessToken = config.accessToken;
144
+ if (config.authMethod === "oauth" && config.clientId && config.clientSecret) {
145
+ const tokenResponse = await fetch(`https://${config.storeUrl}/admin/oauth/access_token`, {
146
+ method: "POST",
147
+ headers: { "Content-Type": "application/json" },
148
+ body: JSON.stringify({
149
+ grant_type: "client_credentials",
150
+ client_id: config.clientId,
151
+ client_secret: config.clientSecret
152
+ })
153
+ });
154
+ if (!tokenResponse.ok) {
155
+ spinner.fail("Failed to authenticate with OAuth credentials");
156
+ return null;
157
+ }
158
+ const tokenData = await tokenResponse.json();
159
+ accessToken = tokenData.access_token;
160
+ }
161
+ const response = await fetch(`https://${config.storeUrl}/admin/api/2025-01/graphql.json`, {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/json",
165
+ "X-Shopify-Access-Token": accessToken || ""
166
+ },
167
+ body: JSON.stringify({ query })
168
+ });
169
+ if (!response.ok) {
170
+ if (response.status === 401) {
171
+ spinner.fail("Authentication failed - check your access token");
172
+ } else if (response.status === 403) {
173
+ spinner.fail("Access denied - check API scopes");
174
+ } else {
175
+ spinner.fail(`Connection failed (HTTP ${response.status})`);
176
+ }
177
+ return null;
178
+ }
179
+ const data = await response.json();
180
+ if (data.errors) {
181
+ spinner.fail(`API error: ${data.errors[0]?.message || "Unknown error"}`);
182
+ return null;
183
+ }
184
+ if (!data.data) {
185
+ spinner.fail("Invalid response from Shopify API");
186
+ return null;
187
+ }
188
+ const storeInfo = {
189
+ name: data.data.shop.name,
190
+ plan: data.data.shop.plan.displayName,
191
+ email: data.data.shop.email
192
+ };
193
+ spinner.succeed(`Connected! Store: "${storeInfo.name}" (${storeInfo.plan})`);
194
+ return storeInfo;
195
+ } catch (error) {
196
+ spinner.fail(`Connection error: ${error instanceof Error ? error.message : "Unknown error"}`);
197
+ return null;
198
+ }
199
+ }
200
+ function buildEnvVars(config) {
201
+ const env = {
202
+ SHOPIFY_STORE_URL: config.storeUrl
203
+ };
204
+ if (config.authMethod === "token" && config.accessToken) {
205
+ env.SHOPIFY_ACCESS_TOKEN = config.accessToken;
206
+ } else {
207
+ env.SHOPIFY_CLIENT_ID = config.clientId || "";
208
+ env.SHOPIFY_CLIENT_SECRET = config.clientSecret || "";
209
+ }
210
+ if (config.lazyLoading) {
211
+ env.SHOPIFY_MCP_LAZY_LOADING = "true";
212
+ } else if (config.role) {
213
+ env.SHOPIFY_MCP_ROLE = config.role;
214
+ }
215
+ if (config.transport === "http") {
216
+ env.TRANSPORT = "http";
217
+ env.PORT = String(config.port || 3e3);
218
+ }
219
+ return env;
220
+ }
221
+ function generateJsonConfig(config) {
222
+ const env = buildEnvVars(config);
223
+ const mcpConfig = {
224
+ mcpServers: {
225
+ shopify: {
226
+ command: "npx",
227
+ args: ["-y", "@anton.andrusenko/shopify-mcp-admin"],
228
+ env
229
+ }
230
+ }
231
+ };
232
+ return JSON.stringify(mcpConfig, null, 2);
233
+ }
234
+ function generateLibreChatConfig(config) {
235
+ const env = buildEnvVars(config);
236
+ const envLines = Object.entries(env).map(([key, value]) => ` ${key}: "${value}"`).join("\n");
237
+ return `# LibreChat Configuration for Shopify MCP Admin
238
+ # Generated by shopify-mcp-admin setup wizard
239
+ # Docs: https://github.com/AntonAndrusenko/shopify-mcp-admin
240
+
241
+ version: 1.2.1
242
+ cache: true
243
+
244
+ interface:
245
+ customWelcome: 'Welcome to LibreChat with Shopify MCP! \u{1F6CD}\uFE0F'
246
+
247
+ mcpServers:
248
+ shopify:
249
+ type: ${config.transport === "http" ? "sse" : "stdio"}
250
+ ${config.transport === "http" ? `url: http://localhost:${config.port || 3e3}/sse` : `command: npx
251
+ args:
252
+ - -y
253
+ - "@anton.andrusenko/shopify-mcp-admin"`}
254
+ env:
255
+ ${envLines}
256
+ LOG_LEVEL: "info"
257
+ serverInstructions: true
258
+ timeout: 30000
259
+ initTimeout: 15000
260
+ `;
261
+ }
262
+ function generateEnvFile(config) {
263
+ const env = buildEnvVars(config);
264
+ let content = `# Shopify MCP Admin Configuration
265
+ # Generated by shopify-mcp-admin setup wizard
266
+ # Docs: https://github.com/AntonAndrusenko/shopify-mcp-admin
267
+
268
+ `;
269
+ for (const [key, value] of Object.entries(env)) {
270
+ content += `${key}=${value}
271
+ `;
272
+ }
273
+ return content;
274
+ }
275
+ function generateShellExport(config) {
276
+ const env = buildEnvVars(config);
277
+ let content = `# Shopify MCP Admin - Shell Environment
278
+ # Add to your ~/.bashrc, ~/.zshrc, or run: source <filename>
279
+
280
+ `;
281
+ for (const [key, value] of Object.entries(env)) {
282
+ content += `export ${key}="${value}"
283
+ `;
284
+ }
285
+ content += `
286
+ # Run the server with:
287
+ # npx @anton.andrusenko/shopify-mcp-admin
288
+ `;
289
+ return content;
290
+ }
291
+ function getClaudeDesktopConfigPath() {
292
+ const platform = process.platform;
293
+ if (platform === "darwin") {
294
+ return join(
295
+ homedir(),
296
+ "Library",
297
+ "Application Support",
298
+ "Claude",
299
+ "claude_desktop_config.json"
300
+ );
301
+ }
302
+ if (platform === "win32") {
303
+ return join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
304
+ }
305
+ return join(homedir(), ".config", "claude", "claude_desktop_config.json");
306
+ }
307
+ function getCursorConfigPath() {
308
+ return join(process.cwd(), ".cursor", "mcp.json");
309
+ }
310
+ function getWindsurfConfigPath() {
311
+ const platform = process.platform;
312
+ if (platform === "darwin") {
313
+ return join(homedir(), ".codeium", "windsurf", "mcp_config.json");
314
+ }
315
+ if (platform === "win32") {
316
+ return join(process.env.APPDATA || "", "Codeium", "windsurf", "mcp_config.json");
317
+ }
318
+ return join(homedir(), ".codeium", "windsurf", "mcp_config.json");
319
+ }
320
+ function getVSCodeConfigPath() {
321
+ return join(process.cwd(), ".vscode", "mcp.json");
322
+ }
323
+ function ensureDirectoryExists(filePath) {
324
+ const dir = dirname(filePath);
325
+ if (!existsSync(dir)) {
326
+ mkdirSync(dir, { recursive: true });
327
+ }
328
+ }
329
+ function mergeJsonConfig(existingPath, newConfig) {
330
+ if (!existsSync(existingPath)) {
331
+ return newConfig;
332
+ }
333
+ try {
334
+ const existing = JSON.parse(readFileSync(existingPath, "utf-8"));
335
+ const toMerge = JSON.parse(newConfig);
336
+ existing.mcpServers = {
337
+ ...existing.mcpServers,
338
+ ...toMerge.mcpServers
339
+ };
340
+ return JSON.stringify(existing, null, 2);
341
+ } catch {
342
+ return newConfig;
343
+ }
344
+ }
345
+ function printSuccessBox(client, configPath) {
346
+ const lines = ["", ` ${c.green("\u2728")} ${c.bold("You're all set!")}`, ""];
347
+ lines.push(` ${c.cyan("Next steps:")}`);
348
+ switch (client) {
349
+ case "claude-desktop":
350
+ lines.push(" 1. Restart Claude Desktop");
351
+ lines.push(` 2. Look for "shopify" in the MCP servers list`);
352
+ lines.push(` 3. Try: "List all products in my store"`);
353
+ break;
354
+ case "cursor":
355
+ lines.push(" 1. Restart Cursor");
356
+ lines.push(" 2. The Shopify MCP server will be available");
357
+ lines.push(` 3. Try: "List all products in my Shopify store"`);
358
+ break;
359
+ case "windsurf":
360
+ lines.push(" 1. Restart Windsurf");
361
+ lines.push(` 2. Look for "shopify" in the Cascade MCP list`);
362
+ lines.push(` 3. Try: "List all products in my store"`);
363
+ break;
364
+ case "vscode-copilot":
365
+ lines.push(" 1. Restart VS Code");
366
+ lines.push(` 2. Use Copilot Chat with "shopify" MCP`);
367
+ lines.push(" 3. Try: @shopify list products");
368
+ break;
369
+ case "librechat":
370
+ lines.push(" 1. Copy librechat.yaml to your LibreChat directory");
371
+ lines.push(" 2. Restart LibreChat: docker compose restart");
372
+ lines.push(` 3. Select "shopify" in the MCP servers dropdown`);
373
+ break;
374
+ case "openai-http":
375
+ lines.push(" 1. Start the server: npx @anton.andrusenko/shopify-mcp-admin");
376
+ lines.push(" 2. Server will listen on the configured port");
377
+ lines.push(" 3. Connect your OpenAI integration via HTTP");
378
+ break;
379
+ case "other":
380
+ lines.push(` 1. Source the env file: source ${configPath}`);
381
+ lines.push(" 2. Or copy variables to your shell profile");
382
+ lines.push(" 3. Run: npx @anton.andrusenko/shopify-mcp-admin");
383
+ break;
384
+ }
385
+ lines.push("");
386
+ lines.push(
387
+ ` ${c.gray("\u{1F4DA} Docs:")} ${c.cyan("https://github.com/AntonAndrusenko/shopify-mcp-admin")}`
388
+ );
389
+ lines.push("");
390
+ const maxLength = 65;
391
+ const boxTop = c.green(`\u256D${"\u2500".repeat(maxLength)}\u256E`);
392
+ const boxBottom = c.green(`\u2570${"\u2500".repeat(maxLength)}\u256F`);
393
+ console.log("");
394
+ console.log(boxTop);
395
+ const ansiRegex = /\x1b\[[0-9;]*m/g;
396
+ for (const line of lines) {
397
+ const visibleLength = line.replace(ansiRegex, "").length;
398
+ const padding = maxLength - visibleLength;
399
+ console.log(`${c.green("\u2502")}${line}${" ".repeat(Math.max(0, padding))}${c.green("\u2502")}`);
400
+ }
401
+ console.log(boxBottom);
402
+ console.log("");
403
+ }
404
+ async function runSetupWizard() {
405
+ console.clear();
406
+ console.log(BANNER);
407
+ const config = {
408
+ storeUrl: "",
409
+ authMethod: "token",
410
+ transport: "stdio",
411
+ lazyLoading: false,
412
+ client: "claude-desktop"
413
+ };
414
+ try {
415
+ console.log("");
416
+ const storeUrlInput = await input({
417
+ message: "What is your Shopify store URL?",
418
+ default: "your-store.myshopify.com",
419
+ validate: validateStoreUrl,
420
+ transformer: (value) => cleanStoreUrl(value)
421
+ });
422
+ config.storeUrl = cleanStoreUrl(storeUrlInput);
423
+ console.log("");
424
+ config.authMethod = await select({
425
+ message: "How would you like to authenticate?",
426
+ choices: [
427
+ {
428
+ name: "Access Token (Legacy Custom App)",
429
+ value: "token",
430
+ description: "I have a shpat_xxx token from a Custom App"
431
+ },
432
+ {
433
+ name: "OAuth 2.0 (Dev Dashboard)",
434
+ value: "oauth",
435
+ description: "I have a Client ID and Client Secret"
436
+ }
437
+ ]
438
+ });
439
+ console.log("");
440
+ if (config.authMethod === "token") {
441
+ config.accessToken = await password({
442
+ message: "Paste your access token (shpat_xxx)",
443
+ mask: "\u2022",
444
+ validate: validateAccessToken
445
+ });
446
+ } else {
447
+ config.clientId = await input({
448
+ message: "Enter your Client ID",
449
+ validate: (v) => v.trim().length > 0 || "Client ID is required"
450
+ });
451
+ config.clientSecret = await password({
452
+ message: "Enter your Client Secret",
453
+ mask: "\u2022",
454
+ validate: (v) => v.trim().length > 0 || "Client Secret is required"
455
+ });
456
+ }
457
+ console.log("");
458
+ const storeInfo = await testConnection(config);
459
+ if (!storeInfo) {
460
+ const retry = await confirm({
461
+ message: "Connection failed. Would you like to try different credentials?",
462
+ default: true
463
+ });
464
+ if (retry) {
465
+ return runSetupWizard();
466
+ }
467
+ console.log(c.yellow("\nSetup cancelled. Please check your credentials and try again."));
468
+ process.exit(1);
469
+ }
470
+ console.log("");
471
+ config.client = await select({
472
+ message: "Which AI client will you use?",
473
+ choices: [
474
+ {
475
+ name: "Claude Desktop",
476
+ value: "claude-desktop",
477
+ description: "Anthropic's official Claude app"
478
+ },
479
+ {
480
+ name: "Cursor",
481
+ value: "cursor",
482
+ description: "AI-powered code editor"
483
+ },
484
+ {
485
+ name: "Windsurf",
486
+ value: "windsurf",
487
+ description: "Codeium AI IDE with Cascade"
488
+ },
489
+ {
490
+ name: "VS Code (Copilot)",
491
+ value: "vscode-copilot",
492
+ description: "VS Code with GitHub Copilot MCP"
493
+ },
494
+ {
495
+ name: "LibreChat",
496
+ value: "librechat",
497
+ description: "Open-source chat UI (Docker)"
498
+ },
499
+ {
500
+ name: "OpenAI / HTTP Integration",
501
+ value: "openai-http",
502
+ description: "HTTP server for OpenAI function calling"
503
+ },
504
+ {
505
+ name: "Other / Manual Setup",
506
+ value: "other",
507
+ description: "Generate shell exports for manual configuration"
508
+ }
509
+ ]
510
+ });
511
+ console.log("");
512
+ const loadingStrategy = await select({
513
+ message: "How would you like to load tools?",
514
+ choices: [
515
+ {
516
+ name: `Load all tools at startup ${c.dim("(recommended)")}`,
517
+ value: "all",
518
+ description: "All 79 tools immediately available - best for most users"
519
+ },
520
+ {
521
+ name: `Load by role preset ${c.dim("(optimized)")}`,
522
+ value: "role",
523
+ description: "Load tools based on your workflow - reduces AI token usage"
524
+ },
525
+ {
526
+ name: `Lazy loading ${c.dim("(on-demand)")}`,
527
+ value: "lazy",
528
+ description: '15 core tools at start, load more with "load-module" command'
529
+ }
530
+ ]
531
+ });
532
+ if (loadingStrategy === "lazy") {
533
+ config.lazyLoading = true;
534
+ console.log(
535
+ c.gray(' \u2139\uFE0F Lazy loading: Use "list-modules" and "load-module" tools to add more tools')
536
+ );
537
+ } else if (loadingStrategy === "role") {
538
+ console.log("");
539
+ const roleChoice = await select({
540
+ message: "Select your role preset:",
541
+ choices: [
542
+ {
543
+ name: `Product Manager ${c.dim("(41 tools)")}`,
544
+ value: "product-manager",
545
+ description: "Products, inventory, collections, and metafields"
546
+ },
547
+ {
548
+ name: `Content Manager ${c.dim("(37 tools)")}`,
549
+ value: "content-manager",
550
+ description: "Pages, blogs, articles, and SEO content"
551
+ },
552
+ {
553
+ name: `International Manager ${c.dim("(46 tools)")}`,
554
+ value: "international-manager",
555
+ description: "Markets, locales, and translations"
556
+ },
557
+ {
558
+ name: `SEO Specialist ${c.dim("(38 tools)")}`,
559
+ value: "seo-specialist",
560
+ description: "SEO optimization, redirects, and URL management"
561
+ },
562
+ {
563
+ name: `Inventory Manager ${c.dim("(15 tools)")}`,
564
+ value: "inventory-manager",
565
+ description: "Core product and inventory tools only"
566
+ }
567
+ ]
568
+ });
569
+ config.role = roleChoice;
570
+ }
571
+ if (config.client === "librechat" || config.client === "openai-http" || config.client === "other") {
572
+ console.log("");
573
+ config.transport = await select({
574
+ message: "Which transport mode?",
575
+ default: config.client === "openai-http" ? "http" : "stdio",
576
+ choices: [
577
+ {
578
+ name: `STDIO ${c.dim("(recommended for most clients)")}`,
579
+ value: "stdio",
580
+ description: "Standard input/output communication"
581
+ },
582
+ {
583
+ name: `HTTP ${c.dim("(for web integrations)")}`,
584
+ value: "http",
585
+ description: "HTTP server with SSE support"
586
+ }
587
+ ]
588
+ });
589
+ if (config.transport === "http") {
590
+ const portInput = await input({
591
+ message: "HTTP server port",
592
+ default: "3000",
593
+ validate: (v) => {
594
+ const n = Number.parseInt(v, 10);
595
+ return n > 0 && n < 65536 || "Must be a valid port number";
596
+ }
597
+ });
598
+ config.port = Number.parseInt(portInput, 10);
599
+ }
600
+ }
601
+ if (config.client === "openai-http") {
602
+ config.transport = "http";
603
+ config.port = config.port || 3e3;
604
+ }
605
+ console.log("");
606
+ let configContent;
607
+ let configPath;
608
+ let configDescription;
609
+ switch (config.client) {
610
+ case "claude-desktop": {
611
+ configPath = getClaudeDesktopConfigPath();
612
+ configContent = generateJsonConfig(config);
613
+ configContent = mergeJsonConfig(configPath, configContent);
614
+ configDescription = "Claude Desktop config";
615
+ break;
616
+ }
617
+ case "cursor": {
618
+ configPath = getCursorConfigPath();
619
+ configContent = generateJsonConfig(config);
620
+ configContent = mergeJsonConfig(configPath, configContent);
621
+ configDescription = "Cursor MCP config";
622
+ break;
623
+ }
624
+ case "windsurf": {
625
+ configPath = getWindsurfConfigPath();
626
+ configContent = generateJsonConfig(config);
627
+ configContent = mergeJsonConfig(configPath, configContent);
628
+ configDescription = "Windsurf MCP config";
629
+ break;
630
+ }
631
+ case "vscode-copilot": {
632
+ configPath = getVSCodeConfigPath();
633
+ configContent = generateJsonConfig(config);
634
+ configContent = mergeJsonConfig(configPath, configContent);
635
+ configDescription = "VS Code MCP config";
636
+ break;
637
+ }
638
+ case "librechat": {
639
+ configPath = join(process.cwd(), "librechat.yaml");
640
+ configContent = generateLibreChatConfig(config);
641
+ configDescription = "LibreChat config";
642
+ break;
643
+ }
644
+ case "openai-http": {
645
+ configPath = join(process.cwd(), ".env.shopify-mcp");
646
+ configContent = generateEnvFile(config);
647
+ configDescription = "Environment file (HTTP mode)";
648
+ break;
649
+ }
650
+ default: {
651
+ configPath = join(process.cwd(), ".shopify-mcp.env");
652
+ configContent = generateShellExport(config);
653
+ configDescription = "Shell environment exports";
654
+ break;
655
+ }
656
+ }
657
+ console.log(c.cyan(`
658
+ \u{1F4C4} ${configDescription} preview:
659
+ `));
660
+ console.log(c.gray("\u2500".repeat(60)));
661
+ console.log(c.dim(configContent));
662
+ console.log(c.gray("\u2500".repeat(60)));
663
+ const shouldSave = await confirm({
664
+ message: `Save to ${configPath}?`,
665
+ default: true
666
+ });
667
+ if (shouldSave) {
668
+ ensureDirectoryExists(configPath);
669
+ writeFileSync(configPath, configContent, "utf-8");
670
+ console.log(`
671
+ ${c.green("\u2714")} Configuration saved to: ${c.cyan(configPath)}`);
672
+ } else {
673
+ console.log(`
674
+ ${c.yellow("\u2139")} Configuration not saved. You can copy the preview above.`);
675
+ }
676
+ printSuccessBox(config.client, configPath);
677
+ } catch (error) {
678
+ if (error instanceof Error && error.name === "ExitPromptError") {
679
+ console.log(c.yellow("\n\nSetup cancelled."));
680
+ process.exit(0);
681
+ }
682
+ throw error;
683
+ }
684
+ }
685
+ function isSetupCommand(args) {
686
+ return args.includes("init") || args.includes("setup") || args.includes("--setup");
687
+ }
688
+ if (process.argv[1]?.includes("setup-wizard")) {
689
+ runSetupWizard().catch((error) => {
690
+ console.error(c.red("Setup failed:"), error);
691
+ process.exit(1);
692
+ });
693
+ }
694
+ export {
695
+ isSetupCommand,
696
+ runSetupWizard
697
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anton.andrusenko/shopify-mcp-admin",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "MCP server for Shopify Admin API - enables AI agents to manage Shopify stores with 79 tools for products, inventory, collections, content, SEO, metafields, markets & translations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,7 +11,7 @@
11
11
  "sideEffects": false,
12
12
  "scripts": {
13
13
  "dev": "tsx src/index.ts",
14
- "build": "tsup src/index.ts --format esm --dts",
14
+ "build": "tsup src/index.ts --format esm --dts --clean",
15
15
  "start": "node dist/index.js",
16
16
  "lint": "biome check src/",
17
17
  "lint:fix": "biome check --fix src/",
@@ -24,16 +24,14 @@
24
24
  "inspect": "npx @modelcontextprotocol/inspector node dist/index.js",
25
25
  "inspect:dev": "npx @modelcontextprotocol/inspector npx tsx src/index.ts",
26
26
  "inspect:config": "npx @modelcontextprotocol/inspector --config mcp-inspector.json",
27
- "fast-agent": "fast-agent",
28
- "fast-agent:check": "fast-agent check",
29
- "fast-agent:chat": "fast-agent go --servers shopify-mcp-admin",
30
- "fast-agent:dev": "fast-agent go --servers shopify-mcp-admin-dev",
27
+ "wizard": "npx tsx src/index.ts init",
28
+ "wizard:build": "node dist/index.js init",
29
+ "librechat:setup": "./scripts/setup-librechat.sh",
30
+ "librechat:setup:local": "./scripts/setup-librechat.sh --use-local",
31
31
  "ci": "npm run lint && npm run typecheck && npm run test && npm run build",
32
32
  "version:patch": "npm version patch --no-git-tag-version",
33
33
  "version:minor": "npm version minor --no-git-tag-version",
34
- "version:major": "npm version major --no-git-tag-version",
35
- "release": "npm run ci && npm publish --access public",
36
- "prepublishOnly": "npm run ci"
34
+ "version:major": "npm version major --no-git-tag-version"
37
35
  },
38
36
  "keywords": [
39
37
  "shopify",
@@ -66,6 +64,7 @@
66
64
  "LICENSE"
67
65
  ],
68
66
  "dependencies": {
67
+ "@inquirer/prompts": "^7.5.1",
69
68
  "@modelcontextprotocol/sdk": "^1.22.0",
70
69
  "@shopify/shopify-api": "^11.14.1",
71
70
  "express": "^5.1.0",