epicmerch-mcp 1.3.19 → 1.3.21

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/src/install.js CHANGED
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFi
3
3
  import { homedir, platform } from 'os';
4
4
  import { join, dirname } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
+ import { execSync } from 'node:child_process';
6
7
 
7
8
  const SERVER_NAME = 'epicmerch';
8
9
  // `npx -y epicmerch-mcp@latest` instead of `npx epicmerch-mcp` so every
@@ -27,6 +28,56 @@ function defaultCommandsDir() {
27
28
  return join(homedir(), '.claude', 'commands');
28
29
  }
29
30
 
31
+ // ── Claude Code CLI registration ────────────────────────────────────────────
32
+ // Claude Code (the `claude` CLI) stores MCP servers in ~/.claude.json at user
33
+ // scope. That file holds ALL of Claude Code's state (projects, history, auth),
34
+ // so rather than editing it directly we drive Claude Code's own `claude mcp add`
35
+ // command — the safe, official path. This is the piece the installer was missing:
36
+ // it configured Claude Desktop / Cursor / Codex but never registered Claude Code,
37
+ // so CLI users had to run `claude mcp add` by hand to see the merchant_* tools.
38
+
39
+ // Is the `claude` CLI on PATH? (execSync uses a shell, so it resolves claude.cmd
40
+ // on Windows too.) Cheap probe; returns false on any error/timeout.
41
+ function claudeCliAvailable() {
42
+ try { execSync('claude --version', { stdio: 'ignore', timeout: 15000 }); return true; }
43
+ catch { return false; }
44
+ }
45
+
46
+ // The exact command we run — and show the user verbatim if `claude` isn't found.
47
+ function claudeAddCommand() {
48
+ return `claude mcp add ${SERVER_NAME} -s user -- ${SERVER_CONFIG.command} ${SERVER_CONFIG.args.join(' ')}`;
49
+ }
50
+
51
+ /**
52
+ * Register the epicmerch MCP with the Claude Code CLI via `claude mcp add`
53
+ * (user scope). Idempotent — checks `claude mcp list` first and tolerates a
54
+ * duplicate-add error. Never edits ~/.claude.json directly.
55
+ * @returns {{ status: 'added'|'exists'|'absent'|'failed', addCmd: string, detail?: string }}
56
+ */
57
+ export function registerClaudeCode({ force = false } = {}) {
58
+ const addCmd = claudeAddCommand();
59
+ if (!claudeCliAvailable()) return { status: 'absent', addCmd };
60
+
61
+ // Already configured? Avoid the duplicate-add error.
62
+ try {
63
+ const list = execSync('claude mcp list', { encoding: 'utf-8', timeout: 15000 });
64
+ const present = new RegExp(`(^|\\s)${SERVER_NAME}\\b`, 'm').test(list);
65
+ if (present && !force) return { status: 'exists', addCmd };
66
+ if (present && force) {
67
+ try { execSync(`claude mcp remove ${SERVER_NAME} -s user`, { stdio: 'ignore', timeout: 15000 }); } catch { /* ignore */ }
68
+ }
69
+ } catch { /* `mcp list` unavailable on this version — fall through; tolerate a dup error below */ }
70
+
71
+ try {
72
+ execSync(addCmd, { stdio: 'ignore', timeout: 30000 });
73
+ return { status: 'added', addCmd };
74
+ } catch (e) {
75
+ const msg = String(e.stderr || e.stdout || e.message || '');
76
+ if (/already (exists|configured)/i.test(msg)) return { status: 'exists', addCmd };
77
+ return { status: 'failed', addCmd, detail: msg.split('\n')[0].slice(0, 200) };
78
+ }
79
+ }
80
+
30
81
  /**
31
82
  * Copy our bundled skill *.md files into ~/.claude/commands/ (or a
32
83
  * specified dir). Skips files that already exist with identical content.
@@ -284,13 +335,20 @@ export function writeConfigFile(configPath, desiredConfig) {
284
335
  */
285
336
  export async function install(opts = {}) {
286
337
  const clients = detectClients().filter((c) => !opts.client || c.client === opts.client);
287
- if (clients.length === 0) {
288
- console.error('No MCP-aware clients detected. Install one of: Claude Desktop, Cursor, or Codex CLI (`npm i -g @openai/codex`).');
338
+ // Claude Code (the `claude` CLI) is registered via its own command, not a config
339
+ // file, so it's NOT in detectClients(). Account for it here so a pure-Claude-Code
340
+ // user (no Desktop / Cursor / Codex) isn't turned away by the guard below.
341
+ const tryClaudeCode = (!opts.client || opts.client === 'claude-code');
342
+ const claudeCodeHere = tryClaudeCode && claudeCliAvailable();
343
+
344
+ if (clients.length === 0 && !claudeCodeHere) {
345
+ console.error('No MCP-aware clients detected. Install one of: Claude Code (`npm i -g @anthropic-ai/claude-code`), Claude Desktop, Cursor, or Codex CLI (`npm i -g @openai/codex`).');
289
346
  return 1;
290
347
  }
291
348
 
292
349
  console.error('Detected clients:');
293
- for (const c of clients) console.error(` ✓ ${c.client.padEnd(8)} → ${c.configPath}`);
350
+ for (const c of clients) console.error(` ✓ ${c.client.padEnd(11)} → ${c.configPath}`);
351
+ if (claudeCodeHere) console.error(` ✓ ${'claude-code'.padEnd(11)} → via \`claude mcp add\` (user scope)`);
294
352
  console.error('');
295
353
 
296
354
  if (opts.dryRun) {
@@ -338,6 +396,21 @@ export async function install(opts = {}) {
338
396
  }
339
397
  }
340
398
 
399
+ // Register with the Claude Code CLI — a separate mechanism from the config-file
400
+ // writers above (it drives `claude mcp add` rather than editing ~/.claude.json).
401
+ // Only acts when `claude` is actually on PATH, so we don't nag Desktop-only users.
402
+ if (claudeCodeHere) {
403
+ const cc = registerClaudeCode({ force: opts.force });
404
+ if (cc.status === 'added') {
405
+ console.error('✓ Added to claude-code (claude mcp add, user scope)');
406
+ } else if (cc.status === 'exists') {
407
+ console.error('⚠ claude-code: entry already exists — skipped. Re-run with --force to refresh.');
408
+ } else {
409
+ console.error(`✗ claude-code: ${cc.detail || 'registration failed'}. Add it manually:`);
410
+ console.error(` ${cc.addCmd}`);
411
+ }
412
+ }
413
+
341
414
  // Also install slash commands (skills) unless explicitly opted out
342
415
  if (!opts.noSkills) {
343
416
  const result = await installSkills({
@@ -367,7 +440,9 @@ export async function install(opts = {}) {
367
440
  console.error('');
368
441
  console.error('Next:');
369
442
  console.error(' 1. Run `npx epicmerch-mcp login` to authenticate (if you haven\'t already).');
370
- console.error(' 2. Restart Claude Desktop / Cursor / VS Code / Codex for the config to take effect.');
443
+ console.error(' 2. Reload your AI client so the MCP loads:');
444
+ console.error(' • Claude Code — start a new `claude` session (or /mcp to reconnect)');
445
+ console.error(' • Claude Desktop / Cursor / VS Code / Codex — fully quit & reopen');
371
446
  return 0;
372
447
  }
373
448
 
@@ -450,7 +525,7 @@ export async function setup(opts = {}) {
450
525
  console.error('');
451
526
  console.error('━━━ ✓ Done — restart your AI client(s) ━━━');
452
527
  console.error('');
453
- console.error('In Claude Desktop / Cursor, try a slash command:');
528
+ console.error('In Claude Code / Claude Desktop / Cursor, try a slash command:');
454
529
  console.error(' /epicmerch full integration walkthrough');
455
530
  console.error(' /epicmerch-payments configure Razorpay or Stripe');
456
531
  console.error(' /epicmerch-migrate migrate a Shopify store');