@kwonye/mcpx 0.1.74 → 0.1.75

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.
@@ -28,6 +28,9 @@ function getStateRoot() {
28
28
  function getConfigPath() {
29
29
  return path.join(getConfigRoot(), "mcpx", "config.json");
30
30
  }
31
+ function getSkillsDir() {
32
+ return path.join(getConfigRoot(), "mcpx", "skills");
33
+ }
31
34
  function getManagedIndexPath() {
32
35
  return path.join(getDataRoot(), "mcpx", "managed-index.json");
33
36
  }
@@ -216,10 +219,52 @@ function ensureGatewayToken(config, secrets) {
216
219
  return secrets.rotateLocalToken(secretName);
217
220
  }
218
221
 
222
+ // src/core/skills.ts
223
+ import fs3 from "node:fs";
224
+ import path3 from "node:path";
225
+ function listSkills() {
226
+ const skillsDir = getSkillsDir();
227
+ if (!fs3.existsSync(skillsDir)) {
228
+ return [];
229
+ }
230
+ const files = fs3.readdirSync(skillsDir);
231
+ const skills = [];
232
+ for (const file of files) {
233
+ if (file.endsWith(".md")) {
234
+ const id = path3.basename(file, ".md");
235
+ const content = fs3.readFileSync(path3.join(skillsDir, file), "utf-8");
236
+ skills.push({ id, content });
237
+ }
238
+ }
239
+ return skills;
240
+ }
241
+ function getSkill(id) {
242
+ const skillsDir = getSkillsDir();
243
+ const filePath = path3.join(skillsDir, `${id}.md`);
244
+ if (!fs3.existsSync(filePath)) {
245
+ return null;
246
+ }
247
+ const content = fs3.readFileSync(filePath, "utf-8");
248
+ return { id, content };
249
+ }
250
+ function saveSkill(id, content) {
251
+ const skillsDir = getSkillsDir();
252
+ ensureDir(skillsDir);
253
+ const filePath = path3.join(skillsDir, `${id}.md`);
254
+ fs3.writeFileSync(filePath, content, "utf-8");
255
+ }
256
+ function deleteSkill(id) {
257
+ const skillsDir = getSkillsDir();
258
+ const filePath = path3.join(skillsDir, `${id}.md`);
259
+ if (fs3.existsSync(filePath)) {
260
+ fs3.unlinkSync(filePath);
261
+ }
262
+ }
263
+
219
264
  // src/core/secrets.ts
220
265
  import { execFileSync } from "node:child_process";
221
266
  import crypto2 from "node:crypto";
222
- import fs3 from "node:fs";
267
+ import fs4 from "node:fs";
223
268
  function parseSecretRef(ref) {
224
269
  if (!ref.startsWith("secret://")) {
225
270
  return null;
@@ -313,7 +358,7 @@ function readSecretValueFromStdin() {
313
358
  if (process.stdin.isTTY) {
314
359
  throw new Error("No value provided. Use --value or pipe a value via stdin.");
315
360
  }
316
- const content = fs3.readFileSync(process.stdin.fd, "utf8").trim();
361
+ const content = fs4.readFileSync(process.stdin.fd, "utf8").trim();
317
362
  if (content.length === 0) {
318
363
  throw new Error("Empty secret value provided.");
319
364
  }
@@ -372,7 +417,7 @@ async function probeHttpAuthRequirement(spec, secrets, timeoutMs = DEFAULT_AUTH_
372
417
  }
373
418
 
374
419
  // src/adapters/claude.ts
375
- import path3 from "node:path";
420
+ import path4 from "node:path";
376
421
  import { z as z2 } from "zod";
377
422
 
378
423
  // src/adapters/utils/index.ts
@@ -390,6 +435,11 @@ function pruneStaleManagedEntries(managedIndex, clientId, servers, keepEntryName
390
435
  delete servers[name];
391
436
  }
392
437
  }
438
+ for (const name of Object.keys(servers)) {
439
+ if (!keep.has(name) && name.endsWith(" (mcpx)")) {
440
+ delete servers[name];
441
+ }
442
+ }
393
443
  }
394
444
  function ensureManagedEntryWritable(managedIndex, clientId, entryName, existingValue) {
395
445
  if (existingValue === undefined || existingValue === null) {
@@ -511,7 +561,7 @@ var claudeEntrySchema = z2.object({
511
561
  class ClaudeAdapter {
512
562
  id = "claude";
513
563
  detectConfigPath() {
514
- return path3.join(homeDir(), ".claude.json");
564
+ return path4.join(homeDir(), ".claude.json");
515
565
  }
516
566
  supportsHttp() {
517
567
  return true;
@@ -623,7 +673,8 @@ class ClaudeAdapter {
623
673
  }
624
674
 
625
675
  // src/adapters/claude-desktop.ts
626
- import path4 from "node:path";
676
+ import { execFileSync as execFileSync2 } from "node:child_process";
677
+ import path5 from "node:path";
627
678
  import { z as z3 } from "zod";
628
679
  var stringMapSchema2 = z3.record(z3.string(), z3.string());
629
680
  var claudeDesktopEntrySchema = z3.object({
@@ -636,11 +687,20 @@ var claudeDesktopEntrySchema = z3.object({
636
687
  cwd: z3.string().min(1).optional(),
637
688
  disabled: z3.boolean().optional()
638
689
  }).passthrough();
690
+ function buildProxyEntry(entry) {
691
+ const upstreamName = entry.name.replace(/ \(mcpx\)$/, "");
692
+ try {
693
+ const mcpxPath = execFileSync2("which", ["mcpx"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
694
+ if (mcpxPath)
695
+ return { command: mcpxPath, args: ["proxy", upstreamName] };
696
+ } catch {}
697
+ return { command: "npx", args: ["-y", "@kwonye/mcpx@latest", "proxy", upstreamName] };
698
+ }
639
699
 
640
700
  class ClaudeDesktopAdapter {
641
701
  id = "claude-desktop";
642
702
  detectConfigPath() {
643
- return path4.join(homeDir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
703
+ return path5.join(homeDir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
644
704
  }
645
705
  supportsHttp() {
646
706
  return true;
@@ -663,7 +723,7 @@ class ClaudeDesktopAdapter {
663
723
  continue;
664
724
  }
665
725
  const entry = parsed.data;
666
- if ((entry.type === undefined || entry.type === "http") && entry.url && !entry.command) {
726
+ if ((entry.type === undefined || entry.type === "http" || entry.type === "streamable_http") && entry.url && !entry.command) {
667
727
  result.candidates.push({
668
728
  clientId: this.id,
669
729
  configPath,
@@ -707,11 +767,10 @@ class ClaudeDesktopAdapter {
707
767
  try {
708
768
  const raw = readJsonFile(configPath, {});
709
769
  const enabledEntries = options.managedEntries.filter((entry) => entry.enabled);
710
- const serverEntries = Object.fromEntries(enabledEntries.map((entry) => [entry.name, {
711
- type: "http",
712
- url: entry.url,
713
- headers: entry.headers
714
- }]));
770
+ const serverEntries = Object.fromEntries(enabledEntries.map((entry) => {
771
+ const { command, args } = buildProxyEntry(entry);
772
+ return [entry.name, { command, args }];
773
+ }));
715
774
  const enabledManagedNames = enabledEntries.map((entry) => entry.name);
716
775
  const topLevelServers = raw.mcpServers ?? {};
717
776
  removeSourceEntries(topLevelServers, options.sourceEntriesToRemove);
@@ -727,10 +786,10 @@ class ClaudeDesktopAdapter {
727
786
  }
728
787
  raw.mcpServers = topLevelServers;
729
788
  writeJsonAtomic(configPath, raw);
730
- setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(options.managedEntries.map((entry) => [
731
- entry.name,
732
- JSON.stringify({ type: "http", url: entry.url, headers: entry.headers, enabled: entry.enabled })
733
- ])));
789
+ setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(options.managedEntries.map((entry) => {
790
+ const { command, args } = buildProxyEntry(entry);
791
+ return [entry.name, JSON.stringify({ command, args, enabled: entry.enabled })];
792
+ })));
734
793
  return okResult(this.id, configPath);
735
794
  } catch (error) {
736
795
  return errorResult(this.id, configPath, error.message);
@@ -739,8 +798,8 @@ class ClaudeDesktopAdapter {
739
798
  }
740
799
 
741
800
  // src/adapters/codex.ts
742
- import path5 from "node:path";
743
- import fs4 from "node:fs";
801
+ import path6 from "node:path";
802
+ import fs5 from "node:fs";
744
803
  import { parse, stringify } from "@iarna/toml";
745
804
  import { z as z4 } from "zod";
746
805
  var stringMapSchema3 = z4.preprocess((value) => {
@@ -763,7 +822,7 @@ var codexEntrySchema = z4.object({
763
822
  class CodexAdapter {
764
823
  id = "codex";
765
824
  detectConfigPath() {
766
- return path5.join(homeDir(), ".codex", "config.toml");
825
+ return path6.join(homeDir(), ".codex", "config.toml");
767
826
  }
768
827
  supportsHttp() {
769
828
  return true;
@@ -771,10 +830,10 @@ class CodexAdapter {
771
830
  scanForImports(_config, managedIndex) {
772
831
  const configPath = this.detectConfigPath();
773
832
  const result = emptyImportScan(this.id, configPath ?? undefined);
774
- if (!configPath || !fs4.existsSync(configPath)) {
833
+ if (!configPath || !fs5.existsSync(configPath)) {
775
834
  return result;
776
835
  }
777
- const existing = fs4.readFileSync(configPath, "utf8");
836
+ const existing = fs5.readFileSync(configPath, "utf8");
778
837
  const doc = existing.trim().length > 0 ? parse(existing) : {};
779
838
  const mcpServers = doc.mcp_servers ?? {};
780
839
  for (const [name, rawEntry] of Object.entries(mcpServers)) {
@@ -829,7 +888,7 @@ class CodexAdapter {
829
888
  return errorResult(this.id, undefined, "Unable to resolve Codex config.toml path.");
830
889
  }
831
890
  try {
832
- const existing = fs4.existsSync(configPath) ? fs4.readFileSync(configPath, "utf8") : "";
891
+ const existing = fs5.existsSync(configPath) ? fs5.readFileSync(configPath, "utf8") : "";
833
892
  const doc = existing.trim().length > 0 ? parse(existing) : {};
834
893
  const mcpServers = doc.mcp_servers ?? {};
835
894
  removeSourceEntries(mcpServers, options.sourceEntriesToRemove);
@@ -850,10 +909,10 @@ class CodexAdapter {
850
909
  mcpServers[name] = entry;
851
910
  }
852
911
  doc.mcp_servers = mcpServers;
853
- fs4.mkdirSync(path5.dirname(configPath), { recursive: true });
912
+ fs5.mkdirSync(path6.dirname(configPath), { recursive: true });
854
913
  const tmpPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
855
- fs4.writeFileSync(tmpPath, stringify(doc), { mode: 384 });
856
- fs4.renameSync(tmpPath, configPath);
914
+ fs5.writeFileSync(tmpPath, stringify(doc), { mode: 384 });
915
+ fs5.renameSync(tmpPath, configPath);
857
916
  setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(Object.entries(serverEntries).map(([name, entry]) => [name, JSON.stringify(entry)])));
858
917
  return okResult(this.id, configPath);
859
918
  } catch (error) {
@@ -863,7 +922,7 @@ class CodexAdapter {
863
922
  }
864
923
 
865
924
  // src/adapters/cursor.ts
866
- import path6 from "node:path";
925
+ import path7 from "node:path";
867
926
  import { z as z5 } from "zod";
868
927
  var stringMapSchema4 = z5.record(z5.string(), z5.string());
869
928
  var cursorEntrySchema = z5.object({
@@ -880,7 +939,7 @@ var cursorEntrySchema = z5.object({
880
939
  class CursorAdapter {
881
940
  id = "cursor";
882
941
  detectConfigPath() {
883
- return path6.join(homeDir(), "Library", "Application Support", "Cursor", "User", "mcp.json");
942
+ return path7.join(homeDir(), "Library", "Application Support", "Cursor", "User", "mcp.json");
884
943
  }
885
944
  supportsHttp() {
886
945
  return true;
@@ -986,8 +1045,8 @@ class CursorAdapter {
986
1045
  }
987
1046
 
988
1047
  // src/adapters/cline.ts
989
- import path7 from "node:path";
990
- import fs5 from "node:fs";
1048
+ import path8 from "node:path";
1049
+ import fs6 from "node:fs";
991
1050
  import { z as z6 } from "zod";
992
1051
  var stringMapSchema5 = z6.record(z6.string(), z6.string());
993
1052
  var clineEntrySchema = z6.object({
@@ -1002,8 +1061,8 @@ var clineEntrySchema = z6.object({
1002
1061
  }).passthrough();
1003
1062
  function getCandidatePaths() {
1004
1063
  return [
1005
- path7.join(homeDir(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
1006
- path7.join(homeDir(), "Library", "Application Support", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
1064
+ path8.join(homeDir(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
1065
+ path8.join(homeDir(), "Library", "Application Support", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
1007
1066
  ];
1008
1067
  }
1009
1068
 
@@ -1012,7 +1071,7 @@ class ClineAdapter {
1012
1071
  detectConfigPath() {
1013
1072
  const candidates = getCandidatePaths();
1014
1073
  for (const candidate of candidates) {
1015
- if (fs5.existsSync(candidate)) {
1074
+ if (fs6.existsSync(candidate)) {
1016
1075
  return candidate;
1017
1076
  }
1018
1077
  }
@@ -1024,7 +1083,7 @@ class ClineAdapter {
1024
1083
  scanForImports(_config, managedIndex) {
1025
1084
  const configPath = this.detectConfigPath();
1026
1085
  const result = emptyImportScan(this.id, configPath ?? undefined);
1027
- if (!configPath || !fs5.existsSync(configPath)) {
1086
+ if (!configPath || !fs6.existsSync(configPath)) {
1028
1087
  return result;
1029
1088
  }
1030
1089
  const raw = readJsonFile(configPath, {});
@@ -1119,7 +1178,8 @@ class ClineAdapter {
1119
1178
  }
1120
1179
 
1121
1180
  // src/adapters/kiro.ts
1122
- import path8 from "node:path";
1181
+ import fs7 from "node:fs";
1182
+ import path9 from "node:path";
1123
1183
  import { z as z7 } from "zod";
1124
1184
  var stringMapSchema6 = z7.record(z7.string(), z7.string());
1125
1185
  var kiroEntrySchema = z7.object({
@@ -1135,7 +1195,7 @@ var kiroEntrySchema = z7.object({
1135
1195
  class KiroAdapter {
1136
1196
  id = "kiro";
1137
1197
  detectConfigPath() {
1138
- return path8.join(homeDir(), ".kiro", "settings", "mcp.json");
1198
+ return path9.join(homeDir(), ".kiro", "settings", "mcp.json");
1139
1199
  }
1140
1200
  supportsHttp() {
1141
1201
  return true;
@@ -1234,10 +1294,30 @@ class KiroAdapter {
1234
1294
  return errorResult(this.id, configPath, error.message);
1235
1295
  }
1236
1296
  }
1297
+ syncSkills(skills) {
1298
+ const skillsDir = path9.join(homeDir(), ".kiro", "skills");
1299
+ if (!fs7.existsSync(skillsDir)) {
1300
+ fs7.mkdirSync(skillsDir, { recursive: true });
1301
+ }
1302
+ const existingFiles = fs7.readdirSync(skillsDir);
1303
+ const managedIds = new Set(skills.map((s) => s.id));
1304
+ for (const file of existingFiles) {
1305
+ if (file.endsWith(".md")) {
1306
+ const id = path9.basename(file, ".md");
1307
+ if (!managedIds.has(id)) {
1308
+ fs7.unlinkSync(path9.join(skillsDir, file));
1309
+ }
1310
+ }
1311
+ }
1312
+ for (const skill of skills) {
1313
+ const targetPath = path9.join(skillsDir, `${skill.id}.md`);
1314
+ fs7.writeFileSync(targetPath, skill.content, "utf-8");
1315
+ }
1316
+ }
1237
1317
  }
1238
1318
 
1239
1319
  // src/adapters/opencode.ts
1240
- import path9 from "node:path";
1320
+ import path10 from "node:path";
1241
1321
  import { z as z8 } from "zod";
1242
1322
  var stringMapSchema7 = z8.record(z8.string(), z8.string());
1243
1323
  var openCodeEntrySchema = z8.object({
@@ -1254,7 +1334,7 @@ var openCodeEntrySchema = z8.object({
1254
1334
  class OpenCodeAdapter {
1255
1335
  id = "opencode";
1256
1336
  detectConfigPath() {
1257
- return path9.join(homeDir(), ".config", "opencode", "opencode.json");
1337
+ return path10.join(homeDir(), ".config", "opencode", "opencode.json");
1258
1338
  }
1259
1339
  supportsHttp() {
1260
1340
  return true;
@@ -1357,7 +1437,7 @@ class OpenCodeAdapter {
1357
1437
  }
1358
1438
 
1359
1439
  // src/adapters/vscode.ts
1360
- import path10 from "node:path";
1440
+ import path11 from "node:path";
1361
1441
  import { z as z9 } from "zod";
1362
1442
  var stringMapSchema8 = z9.record(z9.string(), z9.string());
1363
1443
  var vscodeEntrySchema = z9.object({
@@ -1374,7 +1454,7 @@ var vscodeEntrySchema = z9.object({
1374
1454
  class VsCodeAdapter {
1375
1455
  id = "vscode";
1376
1456
  detectConfigPath() {
1377
- return path10.join(homeDir(), "Library", "Application Support", "Code", "User", "mcp.json");
1457
+ return path11.join(homeDir(), "Library", "Application Support", "Code", "User", "mcp.json");
1378
1458
  }
1379
1459
  supportsHttp() {
1380
1460
  return true;
@@ -1480,7 +1560,8 @@ class VsCodeAdapter {
1480
1560
  }
1481
1561
 
1482
1562
  // src/adapters/qwen.ts
1483
- import path11 from "node:path";
1563
+ import fs8 from "node:fs";
1564
+ import path12 from "node:path";
1484
1565
  import { z as z10 } from "zod";
1485
1566
  var stringMapSchema9 = z10.record(z10.string(), z10.string());
1486
1567
  var qwenEntrySchema = z10.object({
@@ -1496,7 +1577,7 @@ var qwenEntrySchema = z10.object({
1496
1577
  class QwenAdapter {
1497
1578
  id = "qwen";
1498
1579
  detectConfigPath() {
1499
- return path11.join(homeDir(), ".qwen", "settings.json");
1580
+ return path12.join(homeDir(), ".qwen", "settings.json");
1500
1581
  }
1501
1582
  supportsHttp() {
1502
1583
  return true;
@@ -1592,6 +1673,26 @@ class QwenAdapter {
1592
1673
  return errorResult(this.id, configPath, error.message);
1593
1674
  }
1594
1675
  }
1676
+ syncSkills(skills) {
1677
+ const skillsDir = path12.join(homeDir(), ".qwen", "skills");
1678
+ if (!fs8.existsSync(skillsDir)) {
1679
+ fs8.mkdirSync(skillsDir, { recursive: true });
1680
+ }
1681
+ const existingFiles = fs8.readdirSync(skillsDir);
1682
+ const managedIds = new Set(skills.map((s) => s.id));
1683
+ for (const file of existingFiles) {
1684
+ if (file.endsWith(".md")) {
1685
+ const id = path12.basename(file, ".md");
1686
+ if (!managedIds.has(id)) {
1687
+ fs8.unlinkSync(path12.join(skillsDir, file));
1688
+ }
1689
+ }
1690
+ }
1691
+ for (const skill of skills) {
1692
+ const targetPath = path12.join(skillsDir, `${skill.id}.md`);
1693
+ fs8.writeFileSync(targetPath, skill.content, "utf-8");
1694
+ }
1695
+ }
1595
1696
  }
1596
1697
 
1597
1698
  // src/adapters/index.ts
@@ -1710,8 +1811,14 @@ function syncAllClients(config, secrets, targetClients) {
1710
1811
  const localToken = ensureGatewayToken(config, secrets);
1711
1812
  const gatewayUrl = getGatewayUrl(config);
1712
1813
  const managedEntries = buildManagedEntries(config, gatewayUrl, localToken);
1814
+ const skills = listSkills();
1713
1815
  const results = [];
1714
1816
  for (const adapter of filteredAdapters) {
1817
+ if (adapter.syncSkills) {
1818
+ try {
1819
+ adapter.syncSkills(skills);
1820
+ } catch (error) {}
1821
+ }
1715
1822
  if (!adapter.supportsHttp()) {
1716
1823
  results.push({
1717
1824
  clientId: adapter.id,
@@ -1857,12 +1964,12 @@ function maybePrefixBearer(target, value, raw = false) {
1857
1964
  }
1858
1965
 
1859
1966
  // src/version.ts
1860
- var APP_VERSION = "0.1.74";
1967
+ var APP_VERSION = "0.1.75";
1861
1968
 
1862
1969
  // src/core/update.ts
1863
- import fs6 from "node:fs";
1864
- import path12 from "node:path";
1865
- import { execFileSync as execFileSync2 } from "node:child_process";
1970
+ import fs9 from "node:fs";
1971
+ import path13 from "node:path";
1972
+ import { execFileSync as execFileSync3 } from "node:child_process";
1866
1973
  function compareVersions(a, b) {
1867
1974
  const partsA = a.split(".").map(Number);
1868
1975
  const partsB = b.split(".").map(Number);
@@ -1878,7 +1985,7 @@ function compareVersions(a, b) {
1878
1985
  }
1879
1986
  async function checkForUpdates() {
1880
1987
  try {
1881
- const output = execFileSync2("npm", ["view", "@kwonye/mcpx", "version"], {
1988
+ const output = execFileSync3("npm", ["view", "@kwonye/mcpx", "version"], {
1882
1989
  encoding: "utf8",
1883
1990
  stdio: ["pipe", "pipe", "pipe"]
1884
1991
  }).trim();
@@ -1900,17 +2007,17 @@ async function checkForUpdates() {
1900
2007
  }
1901
2008
  function getStagedUpdate() {
1902
2009
  const stagedPath = getStagedVersionPath();
1903
- if (!fs6.existsSync(stagedPath)) {
2010
+ if (!fs9.existsSync(stagedPath)) {
1904
2011
  return null;
1905
2012
  }
1906
2013
  try {
1907
- const raw = fs6.readFileSync(stagedPath, "utf8");
2014
+ const raw = fs9.readFileSync(stagedPath, "utf8");
1908
2015
  const info = JSON.parse(raw);
1909
2016
  if (!info.version || !info.cliPath) {
1910
2017
  return null;
1911
2018
  }
1912
- const absoluteCliPath = path12.isAbsolute(info.cliPath) ? info.cliPath : path12.join(getUpdatesDir(), info.cliPath);
1913
- if (!fs6.existsSync(absoluteCliPath)) {
2019
+ const absoluteCliPath = path13.isAbsolute(info.cliPath) ? info.cliPath : path13.join(getUpdatesDir(), info.cliPath);
2020
+ if (!fs9.existsSync(absoluteCliPath)) {
1914
2021
  return null;
1915
2022
  }
1916
2023
  return {
@@ -1926,15 +2033,15 @@ function stageUpdate(version, cliPath) {
1926
2033
  ensureDir(updatesDir);
1927
2034
  const stagedInfo = {
1928
2035
  version,
1929
- cliPath: path12.isAbsolute(cliPath) ? path12.relative(updatesDir, cliPath) : cliPath,
2036
+ cliPath: path13.isAbsolute(cliPath) ? path13.relative(updatesDir, cliPath) : cliPath,
1930
2037
  stagedAt: new Date().toISOString()
1931
2038
  };
1932
- fs6.writeFileSync(getStagedVersionPath(), JSON.stringify(stagedInfo, null, 2), "utf8");
2039
+ fs9.writeFileSync(getStagedVersionPath(), JSON.stringify(stagedInfo, null, 2), "utf8");
1933
2040
  }
1934
2041
  function clearStagedUpdate() {
1935
2042
  const stagedPath = getStagedVersionPath();
1936
- if (fs6.existsSync(stagedPath)) {
1937
- fs6.unlinkSync(stagedPath);
2043
+ if (fs9.existsSync(stagedPath)) {
2044
+ fs9.unlinkSync(stagedPath);
1938
2045
  }
1939
2046
  }
1940
2047
  function getStagedCliPath() {
@@ -1943,31 +2050,31 @@ function getStagedCliPath() {
1943
2050
  }
1944
2051
  function removeOldVersions(keepCount = 2) {
1945
2052
  const updatesDir = getUpdatesDir();
1946
- if (!fs6.existsSync(updatesDir)) {
2053
+ if (!fs9.existsSync(updatesDir)) {
1947
2054
  return;
1948
2055
  }
1949
- const entries = fs6.readdirSync(updatesDir, { withFileTypes: true });
2056
+ const entries = fs9.readdirSync(updatesDir, { withFileTypes: true });
1950
2057
  const versionDirs = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith("v")).map((entry) => entry.name).sort((a, b) => compareVersions(b.slice(1), a.slice(1)));
1951
2058
  if (versionDirs.length <= keepCount) {
1952
2059
  return;
1953
2060
  }
1954
2061
  for (const oldVersion of versionDirs.slice(keepCount)) {
1955
- const oldPath = path12.join(updatesDir, oldVersion);
2062
+ const oldPath = path13.join(updatesDir, oldVersion);
1956
2063
  try {
1957
- fs6.rmSync(oldPath, { recursive: true, force: true });
2064
+ fs9.rmSync(oldPath, { recursive: true, force: true });
1958
2065
  } catch {}
1959
2066
  }
1960
2067
  }
1961
2068
 
1962
2069
  // src/core/update-manager.ts
1963
- import fs7 from "node:fs";
1964
- import path13 from "node:path";
2070
+ import fs10 from "node:fs";
2071
+ import path14 from "node:path";
1965
2072
  import { execSync, spawn } from "node:child_process";
1966
2073
  function acquireLock() {
1967
2074
  const lockPath = getUpdateLockPath();
1968
2075
  try {
1969
- ensureDir(path13.dirname(lockPath));
1970
- fs7.writeFileSync(lockPath, `${process.pid}
2076
+ ensureDir(path14.dirname(lockPath));
2077
+ fs10.writeFileSync(lockPath, `${process.pid}
1971
2078
  `, { flag: "wx" });
1972
2079
  return true;
1973
2080
  } catch {
@@ -1977,25 +2084,25 @@ function acquireLock() {
1977
2084
  function releaseLock() {
1978
2085
  const lockPath = getUpdateLockPath();
1979
2086
  try {
1980
- fs7.unlinkSync(lockPath);
2087
+ fs10.unlinkSync(lockPath);
1981
2088
  } catch {}
1982
2089
  }
1983
2090
  function isUpdateInProgress() {
1984
2091
  const lockPath = getUpdateLockPath();
1985
- if (!fs7.existsSync(lockPath)) {
2092
+ if (!fs10.existsSync(lockPath)) {
1986
2093
  return false;
1987
2094
  }
1988
2095
  try {
1989
- const raw = fs7.readFileSync(lockPath, "utf8").trim();
2096
+ const raw = fs10.readFileSync(lockPath, "utf8").trim();
1990
2097
  const pid = Number(raw);
1991
2098
  if (!Number.isFinite(pid) || pid <= 0) {
1992
- fs7.unlinkSync(lockPath);
2099
+ fs10.unlinkSync(lockPath);
1993
2100
  return false;
1994
2101
  }
1995
2102
  process.kill(pid, 0);
1996
2103
  return true;
1997
2104
  } catch {
1998
- fs7.unlinkSync(lockPath);
2105
+ fs10.unlinkSync(lockPath);
1999
2106
  return false;
2000
2107
  }
2001
2108
  }
@@ -2006,8 +2113,8 @@ async function downloadAndStageUpdate() {
2006
2113
  }
2007
2114
  const targetVersion = updateStatus.latestVersion;
2008
2115
  const updatesDir = getUpdatesDir();
2009
- const versionDir = path13.join(updatesDir, `v${targetVersion}`);
2010
- if (fs7.existsSync(versionDir)) {
2116
+ const versionDir = path14.join(updatesDir, `v${targetVersion}`);
2117
+ if (fs10.existsSync(versionDir)) {
2011
2118
  const existingStaged = getStagedUpdate();
2012
2119
  if (existingStaged?.version === targetVersion) {
2013
2120
  return { success: false, error: `Version ${targetVersion} already staged` };
@@ -2019,8 +2126,8 @@ async function downloadAndStageUpdate() {
2019
2126
  cwd: versionDir,
2020
2127
  stdio: ["pipe", "pipe", "pipe"]
2021
2128
  });
2022
- const tarballPath = path13.join(versionDir, `kwonye-mcpx-${targetVersion}.tgz`);
2023
- if (!fs7.existsSync(tarballPath)) {
2129
+ const tarballPath = path14.join(versionDir, `kwonye-mcpx-${targetVersion}.tgz`);
2130
+ if (!fs10.existsSync(tarballPath)) {
2024
2131
  throw new Error("Downloaded tarball not found");
2025
2132
  }
2026
2133
  execSync(`tar -xzf ${tarballPath} --strip-components=1`, {
@@ -2031,8 +2138,8 @@ async function downloadAndStageUpdate() {
2031
2138
  cwd: versionDir,
2032
2139
  stdio: ["pipe", "pipe", "pipe"]
2033
2140
  });
2034
- const cliPath = path13.join(versionDir, "dist", "cli.js");
2035
- if (!fs7.existsSync(cliPath)) {
2141
+ const cliPath = path14.join(versionDir, "dist", "cli.js");
2142
+ if (!fs10.existsSync(cliPath)) {
2036
2143
  throw new Error("CLI entry point not found in downloaded package");
2037
2144
  }
2038
2145
  stageUpdate(targetVersion, cliPath);
@@ -2040,7 +2147,7 @@ async function downloadAndStageUpdate() {
2040
2147
  return { success: true, version: targetVersion };
2041
2148
  } catch (error) {
2042
2149
  try {
2043
- fs7.rmSync(versionDir, { recursive: true, force: true });
2150
+ fs10.rmSync(versionDir, { recursive: true, force: true });
2044
2151
  } catch {}
2045
2152
  return {
2046
2153
  success: false,
@@ -2118,7 +2225,7 @@ function performRollback() {
2118
2225
  }
2119
2226
 
2120
2227
  // src/core/daemon.ts
2121
- import fs8 from "node:fs";
2228
+ import fs11 from "node:fs";
2122
2229
  import net from "node:net";
2123
2230
  import { spawn as spawn2 } from "node:child_process";
2124
2231
 
@@ -3064,10 +3171,10 @@ function createGatewayServer(options) {
3064
3171
 
3065
3172
  // src/core/daemon.ts
3066
3173
  function readPidFromFile(pidPath = getPidPath()) {
3067
- if (!fs8.existsSync(pidPath)) {
3174
+ if (!fs11.existsSync(pidPath)) {
3068
3175
  return null;
3069
3176
  }
3070
- const raw = fs8.readFileSync(pidPath, "utf8").trim();
3177
+ const raw = fs11.readFileSync(pidPath, "utf8").trim();
3071
3178
  if (!raw) {
3072
3179
  return null;
3073
3180
  }
@@ -3136,12 +3243,18 @@ function getDaemonStatus(config) {
3136
3243
  async function startDaemon(config, cliPath, secrets) {
3137
3244
  const existingStatus = getDaemonStatus(config);
3138
3245
  if (existingStatus.running && existingStatus.pid) {
3139
- return {
3140
- started: false,
3141
- pid: existingStatus.pid,
3142
- port: config.gateway.port,
3143
- message: "mcpx daemon already running."
3144
- };
3246
+ const portAvailable = await isPortAvailable(config.gateway.port);
3247
+ if (!portAvailable) {
3248
+ return {
3249
+ started: false,
3250
+ pid: existingStatus.pid,
3251
+ port: config.gateway.port,
3252
+ message: "mcpx daemon already running."
3253
+ };
3254
+ }
3255
+ try {
3256
+ fs11.unlinkSync(getPidPath());
3257
+ } catch {}
3145
3258
  }
3146
3259
  const port = await resolveGatewayPort(config);
3147
3260
  ensureGatewayToken(config, secrets);
@@ -3149,7 +3262,7 @@ async function startDaemon(config, cliPath, secrets) {
3149
3262
  const logPath = getLogPath();
3150
3263
  ensureParentDir(pidPath);
3151
3264
  ensureParentDir(logPath);
3152
- const logFd = fs8.openSync(logPath, "a", 384);
3265
+ const logFd = fs11.openSync(logPath, "a", 384);
3153
3266
  const child = spawn2(process.execPath, [cliPath, "daemon", "run", "--port", String(port)], {
3154
3267
  detached: true,
3155
3268
  stdio: ["ignore", logFd, logFd],
@@ -3159,7 +3272,7 @@ async function startDaemon(config, cliPath, secrets) {
3159
3272
  }
3160
3273
  });
3161
3274
  child.unref();
3162
- fs8.writeFileSync(pidPath, `${child.pid}
3275
+ fs11.writeFileSync(pidPath, `${child.pid}
3163
3276
  `, { mode: 384 });
3164
3277
  startBackgroundUpdateCheck();
3165
3278
  return {
@@ -3182,7 +3295,7 @@ function stopDaemon() {
3182
3295
  process.kill(pid, "SIGTERM");
3183
3296
  } catch {}
3184
3297
  try {
3185
- fs8.unlinkSync(pidPath);
3298
+ fs11.unlinkSync(pidPath);
3186
3299
  } catch {}
3187
3300
  return {
3188
3301
  stopped: true,
@@ -3195,10 +3308,10 @@ async function restartDaemon(config, cliPath, secrets) {
3195
3308
  }
3196
3309
  function readDaemonLogs(maxLines = 200) {
3197
3310
  const logPath = getLogPath();
3198
- if (!fs8.existsSync(logPath)) {
3311
+ if (!fs11.existsSync(logPath)) {
3199
3312
  return "";
3200
3313
  }
3201
- const lines = fs8.readFileSync(logPath, "utf8").split(`
3314
+ const lines = fs11.readFileSync(logPath, "utf8").split(`
3202
3315
  `);
3203
3316
  return lines.slice(Math.max(lines.length - maxLines, 0)).join(`
3204
3317
  `);
@@ -3289,4 +3402,4 @@ function buildStatusReport(config, managedIndex, daemon = getDaemonStatus(config
3289
3402
  };
3290
3403
  }
3291
3404
 
3292
- export { getConfigPath, getManagedIndexPath, defaultConfig, loadConfig, saveConfig, addServer, removeServer, updateServer, setServerEnabled, SecretsManager, readSecretValueFromStdin, probeHttpAuthRequirement, getAdapters, loadManagedIndex, getGatewayUrl, syncAllClients, toSecretRef, secretRefName, defaultAuthSecretName, resolveAuthTarget, applyAuthReference, removeAuthReference, listAuthBindings, maybePrefixBearer, APP_VERSION, compareVersions, checkForUpdates, getStagedUpdate, clearStagedUpdate, getStagedCliPath, startBackgroundUpdateCheck, runBackgroundUpdate, performUpdate, performRollback, getDaemonStatus, startDaemon, stopDaemon, restartDaemon, readDaemonLogs, runDaemonForeground, STATUS_CLIENTS, buildStatusReport };
3405
+ export { getConfigPath, getManagedIndexPath, defaultConfig, loadConfig, saveConfig, addServer, removeServer, updateServer, setServerEnabled, ensureGatewayToken, listSkills, getSkill, saveSkill, deleteSkill, SecretsManager, readSecretValueFromStdin, probeHttpAuthRequirement, getAdapters, loadManagedIndex, getGatewayUrl, syncAllClients, toSecretRef, secretRefName, defaultAuthSecretName, resolveAuthTarget, applyAuthReference, removeAuthReference, listAuthBindings, maybePrefixBearer, APP_VERSION, compareVersions, checkForUpdates, getStagedUpdate, clearStagedUpdate, getStagedCliPath, startBackgroundUpdateCheck, runBackgroundUpdate, performUpdate, performRollback, getDaemonStatus, startDaemon, stopDaemon, restartDaemon, readDaemonLogs, runDaemonForeground, STATUS_CLIENTS, buildStatusReport };