@phidiassj/aiyoperps-mcp-installer 0.6.8 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -147,13 +147,11 @@ async function detectClaudeCode(url) {
147
147
 
148
148
  async function detectClaudeDesktop(url) {
149
149
  const candidates = getClaudeDesktopCandidates();
150
- const configPath = candidates.find(fileExists) ??
151
- candidates.find(candidate => fs.existsSync(path.dirname(candidate))) ??
152
- candidates[0];
153
- const exists = fileExists(configPath);
154
- const parentExists = fs.existsSync(path.dirname(configPath));
155
- const baseConfigRoot = path.dirname(path.dirname(configPath));
156
- const baseRootExists = fs.existsSync(baseConfigRoot);
150
+ const configPaths = selectClaudeDesktopConfigPaths(candidates);
151
+ const primaryPath = configPaths[0];
152
+ const exists = configPaths.some(fileExists);
153
+ const parentExists = configPaths.some(configPath => fs.existsSync(path.dirname(configPath)));
154
+ const baseRootExists = configPaths.some(configPath => fs.existsSync(path.dirname(path.dirname(configPath))));
157
155
  const detected = exists || parentExists || baseRootExists;
158
156
  let supported = baseRootExists;
159
157
  let installed = false;
@@ -167,8 +165,12 @@ async function detectClaudeDesktop(url) {
167
165
 
168
166
  if (exists) {
169
167
  try {
170
- const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
171
- installed = Boolean(parsed?.mcpServers?.[SERVER_NAME]);
168
+ installed = configPaths
169
+ .filter(fileExists)
170
+ .some(configPath => {
171
+ const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
172
+ return Boolean(parsed?.mcpServers?.[SERVER_NAME]);
173
+ });
172
174
  reason = installed ? 'installed' : 'config parsed successfully';
173
175
  } catch (error) {
174
176
  supported = false;
@@ -184,27 +186,37 @@ async function detectClaudeDesktop(url) {
184
186
  supported,
185
187
  installed,
186
188
  reason,
187
- path: configPath,
188
- install: () => installClaudeDesktop(configPath, url),
189
- uninstall: () => uninstallClaudeDesktop(configPath)
189
+ configPaths,
190
+ path: configPaths.join(' | '),
191
+ install: () => installClaudeDesktop(configPaths, url),
192
+ uninstall: () => uninstallClaudeDesktop(configPaths)
190
193
  };
191
194
  }
192
195
 
193
196
  async function detectOpenClaw(url) {
194
- const cli = runCommand('openclaw', ['--version']);
195
- const configPath = resolveOpenClawConfigPath();
196
- const detected = cli.ok || fileExists(configPath);
197
+ const openclawCli = runCommand('openclaw', ['--version']);
198
+ const mcporterCli = runCommand('mcporter', ['--version']);
199
+ const configPath = resolveMcporterHomeConfigPath();
200
+ const detected = openclawCli.ok || fileExists(resolveOpenClawConfigPath());
201
+ const supported = openclawCli.ok && (mcporterCli.ok || Boolean(resolveExecutablePath(process.platform === 'win32' ? 'npx.cmd' : 'npx')));
197
202
 
198
203
  let installed = false;
199
- let reason = cli.ok
200
- ? 'official CLI config helpers available'
201
- : fileExists(configPath)
202
- ? 'config file found but CLI not detected'
203
- : 'openclaw not detected';
204
-
205
- if (cli.ok) {
206
- const getResult = runCommand('openclaw', ['config', 'get', `mcp.servers.${SERVER_NAME}`]);
207
- installed = getResult.ok && Boolean(getResult.stdout.trim());
204
+ let reason = !openclawCli.ok
205
+ ? 'openclaw not detected'
206
+ : mcporterCli.ok
207
+ ? 'mcporter config helpers available'
208
+ : supported
209
+ ? 'mcporter will be used via npx'
210
+ : 'mcporter runtime is unavailable';
211
+
212
+ if (fileExists(configPath)) {
213
+ try {
214
+ const payload = JSON.parse(fs.readFileSync(configPath, 'utf8'));
215
+ installed = Boolean(payload?.mcpServers?.[SERVER_NAME]);
216
+ } catch {
217
+ installed = false;
218
+ }
219
+
208
220
  if (installed) {
209
221
  reason = 'installed';
210
222
  }
@@ -215,10 +227,10 @@ async function detectOpenClaw(url) {
215
227
  name: 'OpenClaw',
216
228
  kind: 'cli',
217
229
  detected,
218
- supported: cli.ok,
230
+ supported,
219
231
  installed,
220
232
  reason,
221
- path: cli.ok ? '~/.openclaw/openclaw.json (CLI-managed)' : configPath,
233
+ path: '~/.mcporter/mcporter.json (mcporter home config)',
222
234
  install: () => installOpenClaw(url),
223
235
  uninstall: () => uninstallOpenClaw()
224
236
  };
@@ -412,7 +424,7 @@ function createInstallAction(host, url) {
412
424
  case 'claude-code':
413
425
  return () => installClaudeCode(url);
414
426
  case 'claude-desktop':
415
- return () => installClaudeDesktop(host.path, url);
427
+ return () => installClaudeDesktop(host.configPaths, url);
416
428
  case 'openclaw':
417
429
  return () => installOpenClaw(url);
418
430
  default:
@@ -463,37 +475,46 @@ function uninstallCodex(configPath) {
463
475
  fs.writeFileSync(configPath, next.trim() ? `${next.replace(/\s*$/, '')}\n` : '', 'utf8');
464
476
  }
465
477
 
466
- function installClaudeDesktop(configPath, url) {
467
- ensureParentDir(configPath);
478
+ function installClaudeDesktop(configPaths, url) {
479
+ const targets = configPaths.filter(fileExists);
480
+ if (targets.length === 0) {
481
+ targets.push(configPaths[0]);
482
+ }
468
483
 
469
- const payload = fileExists(configPath)
470
- ? JSON.parse(fs.readFileSync(configPath, 'utf8'))
471
- : {};
484
+ for (const configPath of targets) {
485
+ ensureParentDir(configPath);
472
486
 
473
- payload.mcpServers ??= {};
474
- payload.mcpServers[SERVER_NAME] = buildJsonServerConfig(url);
487
+ const payload = fileExists(configPath)
488
+ ? JSON.parse(fs.readFileSync(configPath, 'utf8'))
489
+ : {};
475
490
 
476
- backupIfExists(configPath);
477
- fs.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
478
- }
491
+ payload.mcpServers ??= {};
492
+ payload.mcpServers[SERVER_NAME] = buildJsonServerConfig(url);
479
493
 
480
- function uninstallClaudeDesktop(configPath) {
481
- if (!fileExists(configPath)) {
482
- return;
494
+ backupIfExists(configPath);
495
+ fs.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
483
496
  }
497
+ }
484
498
 
485
- const payload = JSON.parse(fs.readFileSync(configPath, 'utf8'));
486
- if (!payload?.mcpServers?.[SERVER_NAME]) {
487
- return;
488
- }
499
+ function uninstallClaudeDesktop(configPaths) {
500
+ for (const configPath of configPaths) {
501
+ if (!fileExists(configPath)) {
502
+ continue;
503
+ }
489
504
 
490
- delete payload.mcpServers[SERVER_NAME];
491
- if (Object.keys(payload.mcpServers).length === 0) {
492
- delete payload.mcpServers;
493
- }
505
+ const payload = JSON.parse(fs.readFileSync(configPath, 'utf8'));
506
+ if (!payload?.mcpServers?.[SERVER_NAME]) {
507
+ continue;
508
+ }
494
509
 
495
- backupIfExists(configPath);
496
- fs.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
510
+ delete payload.mcpServers[SERVER_NAME];
511
+ if (Object.keys(payload.mcpServers).length === 0) {
512
+ delete payload.mcpServers;
513
+ }
514
+
515
+ backupIfExists(configPath);
516
+ fs.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
517
+ }
497
518
  }
498
519
 
499
520
  function installClaudeCode(url) {
@@ -529,17 +550,37 @@ function uninstallClaudeCode() {
529
550
  }
530
551
 
531
552
  function installOpenClaw(url) {
532
- const value = JSON.stringify(buildJsonServerConfig(url));
533
- const result = runCommand('openclaw', ['config', 'set', `mcp.servers.${SERVER_NAME}`, value, '--json']);
553
+ const result = runMcporterCommand([
554
+ 'config',
555
+ 'add',
556
+ SERVER_NAME,
557
+ url,
558
+ '--scope',
559
+ 'home'
560
+ ]);
534
561
  if (!result.ok) {
535
- throw new Error(result.stderr || 'openclaw config set failed');
562
+ throw new Error(result.stderr || 'mcporter config add failed');
536
563
  }
537
564
  }
538
565
 
539
566
  function uninstallOpenClaw() {
540
- const result = runCommand('openclaw', ['config', 'unset', `mcp.servers.${SERVER_NAME}`]);
567
+ if (!fileExists(resolveMcporterHomeConfigPath())) {
568
+ return;
569
+ }
570
+
571
+ const result = runMcporterCommand([
572
+ '--config',
573
+ resolveMcporterHomeConfigPath(),
574
+ 'config',
575
+ 'remove',
576
+ SERVER_NAME
577
+ ]);
578
+ if (result.stderr && /does not exist/i.test(result.stderr)) {
579
+ return;
580
+ }
581
+
541
582
  if (!result.ok) {
542
- throw new Error(result.stderr || 'openclaw config unset failed');
583
+ throw new Error(result.stderr || 'mcporter config remove failed');
543
584
  }
544
585
  }
545
586
 
@@ -1004,7 +1045,8 @@ function getClaudeDesktopCandidates() {
1004
1045
  if (process.platform === 'win32') {
1005
1046
  const appData = env.APPDATA || path.join(env.USERPROFILE || os.homedir(), 'AppData', 'Roaming');
1006
1047
  const localAppData = env.LOCALAPPDATA || path.join(env.USERPROFILE || os.homedir(), 'AppData', 'Local');
1007
- const candidates = [
1048
+ const storeCandidates = [];
1049
+ const roamingCandidates = [
1008
1050
  path.join(appData, 'Claude', 'claude_desktop_config.json'),
1009
1051
  path.join(appData, 'Claude', 'config.json')
1010
1052
  ];
@@ -1018,7 +1060,7 @@ function getClaudeDesktopCandidates() {
1018
1060
  .filter(name => name.toLowerCase().startsWith('claude_'));
1019
1061
 
1020
1062
  for (const packageDir of packageDirs) {
1021
- candidates.push(
1063
+ storeCandidates.push(
1022
1064
  path.join(packagesRoot, packageDir, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json'),
1023
1065
  path.join(packagesRoot, packageDir, 'LocalCache', 'Roaming', 'Claude', 'config.json'));
1024
1066
  }
@@ -1027,7 +1069,7 @@ function getClaudeDesktopCandidates() {
1027
1069
  }
1028
1070
  }
1029
1071
 
1030
- return [...new Set(candidates)];
1072
+ return [...new Set([...storeCandidates, ...roamingCandidates])];
1031
1073
  }
1032
1074
 
1033
1075
  if (process.platform === 'darwin') {
@@ -1043,6 +1085,34 @@ function getClaudeDesktopCandidates() {
1043
1085
  ];
1044
1086
  }
1045
1087
 
1088
+ function selectClaudeDesktopConfigPaths(candidates) {
1089
+ const existingPrimary = candidates.filter(candidate =>
1090
+ fileExists(candidate) &&
1091
+ path.basename(candidate).toLowerCase() === 'claude_desktop_config.json');
1092
+ if (existingPrimary.length > 0) {
1093
+ return existingPrimary;
1094
+ }
1095
+
1096
+ const existingAny = candidates.filter(fileExists);
1097
+ if (existingAny.length > 0) {
1098
+ return existingAny;
1099
+ }
1100
+
1101
+ const creatablePrimary = candidates.find(candidate =>
1102
+ path.basename(candidate).toLowerCase() === 'claude_desktop_config.json' &&
1103
+ fs.existsSync(path.dirname(candidate)));
1104
+ if (creatablePrimary) {
1105
+ return [creatablePrimary];
1106
+ }
1107
+
1108
+ const creatableAny = candidates.find(candidate => fs.existsSync(path.dirname(candidate)));
1109
+ if (creatableAny) {
1110
+ return [creatableAny];
1111
+ }
1112
+
1113
+ return [candidates[0]];
1114
+ }
1115
+
1046
1116
  function resolveOpenClawConfigPath() {
1047
1117
  const explicit = env.OPENCLAW_CONFIG_PATH;
1048
1118
  if (explicit) {
@@ -1053,6 +1123,30 @@ function resolveOpenClawConfigPath() {
1053
1123
  return path.join(stateDir, 'openclaw.json');
1054
1124
  }
1055
1125
 
1126
+ function resolveMcporterHomeConfigPath() {
1127
+ return path.join(os.homedir(), '.mcporter', 'mcporter.json');
1128
+ }
1129
+
1130
+ function runMcporterCommand(args) {
1131
+ const mcporter = resolveExecutablePath(process.platform === 'win32' ? 'mcporter.cmd' : 'mcporter');
1132
+ if (mcporter) {
1133
+ return runCommand(mcporter, args);
1134
+ }
1135
+
1136
+ const npx = resolveExecutablePath(process.platform === 'win32' ? 'npx.cmd' : 'npx');
1137
+ if (!npx) {
1138
+ return {
1139
+ ok: false,
1140
+ status: null,
1141
+ stdout: '',
1142
+ stderr: 'mcporter and npx were not found in PATH',
1143
+ error: null
1144
+ };
1145
+ }
1146
+
1147
+ return runCommand(npx, ['-y', 'mcporter', ...args]);
1148
+ }
1149
+
1056
1150
  function runCommand(command, args) {
1057
1151
  const result = spawnSync(command, args, {
1058
1152
  encoding: 'utf8',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phidiassj/aiyoperps-mcp-installer",
3
- "version": "0.6.8",
3
+ "version": "0.7.0",
4
4
  "description": "Interactive installer for registering AiyoPerps MCP with supported AI agent hosts.",
5
5
  "license": "MIT",
6
6
  "type": "module",