epicmerch-mcp 1.3.19 → 1.3.20
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/package.json +1 -1
- package/src/install.js +80 -5
package/package.json
CHANGED
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
|
-
|
|
288
|
-
|
|
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(
|
|
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.
|
|
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');
|