@kwonye/mcpx 0.1.93 → 0.1.95

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.
Files changed (80) hide show
  1. package/dist/adapters/claude-desktop.d.ts.map +1 -1
  2. package/dist/adapters/claude.d.ts +2 -1
  3. package/dist/adapters/claude.d.ts.map +1 -1
  4. package/dist/adapters/cline.d.ts +3 -1
  5. package/dist/adapters/cline.d.ts.map +1 -1
  6. package/dist/adapters/codex.d.ts +2 -1
  7. package/dist/adapters/codex.d.ts.map +1 -1
  8. package/dist/adapters/cursor.d.ts +2 -1
  9. package/dist/adapters/cursor.d.ts.map +1 -1
  10. package/dist/adapters/hermes.d.ts +11 -0
  11. package/dist/adapters/hermes.d.ts.map +1 -0
  12. package/dist/adapters/index.d.ts.map +1 -1
  13. package/dist/adapters/kiro.d.ts.map +1 -1
  14. package/dist/adapters/openclaw.d.ts +11 -0
  15. package/dist/adapters/openclaw.d.ts.map +1 -0
  16. package/dist/adapters/opencode.d.ts +2 -1
  17. package/dist/adapters/opencode.d.ts.map +1 -1
  18. package/dist/adapters/qwen.d.ts.map +1 -1
  19. package/dist/adapters/utils/index.d.ts +18 -4
  20. package/dist/adapters/utils/index.d.ts.map +1 -1
  21. package/dist/adapters/vscode.d.ts +2 -1
  22. package/dist/adapters/vscode.d.ts.map +1 -1
  23. package/dist/{cli-y7ba5qvr.js → cli-28fsxhe3.js} +430 -156
  24. package/dist/{cli-qvkpwy9w.js → cli-njtbb11m.js} +138 -43
  25. package/dist/{cli-8vxva0cb.js → cli-tk8bggg5.js} +81 -8
  26. package/dist/{cli-np0wwh6m.js → cli-v27bt25d.js} +157 -11
  27. package/dist/{cli-kwyp9ywh.js → cli-wxw1t3rr.js} +941 -303
  28. package/dist/cli.d.ts.map +1 -1
  29. package/dist/cli.js +388 -77
  30. package/dist/compat/hermes.d.ts +7 -0
  31. package/dist/compat/hermes.d.ts.map +1 -0
  32. package/dist/compat/index.d.ts.map +1 -1
  33. package/dist/compat/openclaw.d.ts +7 -0
  34. package/dist/compat/openclaw.d.ts.map +1 -0
  35. package/dist/{config-2s5nsm2g.js → config-5p08cb0c.js} +6 -2
  36. package/dist/core/add-command.d.ts.map +1 -1
  37. package/dist/core/config-repair.d.ts.map +1 -1
  38. package/dist/core/config-store.d.ts +33 -0
  39. package/dist/core/config-store.d.ts.map +1 -0
  40. package/dist/core/config.d.ts +13 -1
  41. package/dist/core/config.d.ts.map +1 -1
  42. package/dist/core/daemon.d.ts +5 -1
  43. package/dist/core/daemon.d.ts.map +1 -1
  44. package/dist/core/errors.d.ts +18 -0
  45. package/dist/core/errors.d.ts.map +1 -0
  46. package/dist/core/index.d.ts +6 -3
  47. package/dist/core/index.d.ts.map +1 -1
  48. package/dist/core/index.js +30 -6
  49. package/dist/core/managed-index-lock.d.ts +2 -0
  50. package/dist/core/managed-index-lock.d.ts.map +1 -0
  51. package/dist/core/oauth.d.ts.map +1 -1
  52. package/dist/core/plugin-host.d.ts +2 -0
  53. package/dist/core/plugin-host.d.ts.map +1 -0
  54. package/dist/core/plugin-manager.d.ts +19 -6
  55. package/dist/core/plugin-manager.d.ts.map +1 -1
  56. package/dist/core/plugin-parse.d.ts.map +1 -1
  57. package/dist/core/plugin-projections.d.ts +6 -2
  58. package/dist/core/plugin-projections.d.ts.map +1 -1
  59. package/dist/core/registry.d.ts +4 -1
  60. package/dist/core/registry.d.ts.map +1 -1
  61. package/dist/core/secrets.d.ts.map +1 -1
  62. package/dist/core/server-auth.d.ts.map +1 -1
  63. package/dist/core/skill-projections.d.ts +3 -0
  64. package/dist/core/skill-projections.d.ts.map +1 -0
  65. package/dist/core/status.d.ts +1 -0
  66. package/dist/core/status.d.ts.map +1 -1
  67. package/dist/core/sync.d.ts +19 -2
  68. package/dist/core/sync.d.ts.map +1 -1
  69. package/dist/core/update-manager.d.ts.map +1 -1
  70. package/dist/gateway/server.d.ts +4 -1
  71. package/dist/gateway/server.d.ts.map +1 -1
  72. package/dist/plugin-host-954a7tf7.js +129 -0
  73. package/dist/{plugin-manager-1sc4b6am.js → plugin-manager-m8ar2271.js} +12 -4
  74. package/dist/{registry-mztfy0cz.js → registry-n12thgf2.js} +1 -1
  75. package/dist/types.d.ts +5 -1
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/version.d.ts +1 -1
  78. package/dist/version.d.ts.map +1 -1
  79. package/package.json +3 -2
  80. package/dist/cli-m2645syd.js +0 -2199
package/dist/cli.js CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- APP_VERSION,
4
3
  STATUS_CLIENTS,
5
4
  applyAuthReference,
6
5
  buildStatusReport,
@@ -14,6 +13,8 @@ import {
14
13
  listAuthBindings,
15
14
  mapRegistryToSpec,
16
15
  maybePrefixBearer,
16
+ mutateActiveConfig,
17
+ mutateConfig,
17
18
  performRollback,
18
19
  performUpdate,
19
20
  probeHttpAuthRequirement,
@@ -30,25 +31,28 @@ import {
30
31
  startDaemon,
31
32
  stopDaemon,
32
33
  toSecretRef
33
- } from "./cli-m2645syd.js";
34
+ } from "./cli-28fsxhe3.js";
34
35
  import {
36
+ APP_VERSION,
35
37
  deleteSkill,
36
38
  getAdapters,
37
39
  getGatewayUrl,
38
40
  getSkill,
39
41
  listSkills,
40
42
  loadManagedIndex,
43
+ persistSyncState,
41
44
  saveSkill,
42
45
  syncAllClients
43
- } from "./cli-kwyp9ywh.js";
46
+ } from "./cli-wxw1t3rr.js";
44
47
  import {
48
+ ConfigLoadError,
45
49
  loadConfig,
46
50
  loadMergedConfig,
47
51
  loadProjectConfig,
48
52
  resolveActiveConfig,
49
53
  saveConfig,
50
54
  saveProjectConfig
51
- } from "./cli-np0wwh6m.js";
55
+ } from "./cli-v27bt25d.js";
52
56
  import {
53
57
  SecretsManager,
54
58
  addServer,
@@ -58,7 +62,7 @@ import {
58
62
  removeServer,
59
63
  rotateGatewayToken,
60
64
  setServerEnabled
61
- } from "./cli-8vxva0cb.js";
65
+ } from "./cli-tk8bggg5.js";
62
66
  import {
63
67
  getConfigPath,
64
68
  getManagedIndexPath,
@@ -729,6 +733,203 @@ function parseQwenArgs(argv) {
729
733
  }
730
734
  }
731
735
 
736
+ // src/compat/openclaw.ts
737
+ function parseOpenClawArgs(args) {
738
+ if (args.length === 0) {
739
+ return { error: "Usage: mcpx openclaw mcp add <name> --url <url> | --command <cmd>" };
740
+ }
741
+ let name;
742
+ let url;
743
+ let command;
744
+ let transport;
745
+ const headers = [];
746
+ const envVars = [];
747
+ const commandArgs = [];
748
+ let cwd;
749
+ let hasAuthOAuth = false;
750
+ let hasInclude = false;
751
+ let hasExclude = false;
752
+ let hasTimeout = false;
753
+ let hasConnectTimeout = false;
754
+ for (let i = 0;i < args.length; i++) {
755
+ const arg = args[i];
756
+ if (!name && !arg.startsWith("--")) {
757
+ name = arg;
758
+ continue;
759
+ }
760
+ if (arg === "--url") {
761
+ url = args[++i];
762
+ } else if (arg === "--command") {
763
+ command = args[++i];
764
+ } else if (arg === "--transport") {
765
+ transport = args[++i];
766
+ } else if (arg === "--header") {
767
+ const val = args[++i];
768
+ if (val && !val.startsWith("--")) {
769
+ headers.push(val);
770
+ }
771
+ } else if (arg === "--arg") {
772
+ const val = args[++i];
773
+ if (val && !val.startsWith("--")) {
774
+ commandArgs.push(val);
775
+ }
776
+ } else if (arg === "--env") {
777
+ const val = args[++i];
778
+ if (val && !val.startsWith("--")) {
779
+ envVars.push(val);
780
+ }
781
+ } else if (arg === "--cwd") {
782
+ cwd = args[++i];
783
+ } else if (arg === "--auth") {
784
+ const val = args[++i];
785
+ if (val === "oauth") {
786
+ hasAuthOAuth = true;
787
+ }
788
+ } else if (arg.startsWith("--include")) {
789
+ hasInclude = true;
790
+ } else if (arg.startsWith("--exclude")) {
791
+ hasExclude = true;
792
+ } else if (arg === "--timeout") {
793
+ hasTimeout = true;
794
+ i++;
795
+ } else if (arg === "--connect-timeout") {
796
+ hasConnectTimeout = true;
797
+ i++;
798
+ }
799
+ }
800
+ if (!name) {
801
+ return { error: "Server name is required." };
802
+ }
803
+ if (hasAuthOAuth) {
804
+ return { error: "--auth oauth is not supported. Use `mcpx auth` to configure auth after adding the server." };
805
+ }
806
+ if (hasInclude || hasExclude) {
807
+ return { error: "--include/--exclude are not supported. Use `mcpx add` directly for advanced configuration." };
808
+ }
809
+ if (url && command) {
810
+ return { error: "Cannot combine --url and --command. Specify one transport mode." };
811
+ }
812
+ if (hasTimeout) {
813
+ return { error: "--timeout is not supported. Configure timeouts in the server config after adding." };
814
+ }
815
+ if (hasConnectTimeout) {
816
+ return { error: "--connect-timeout is not supported. Configure timeouts in the server config after adding." };
817
+ }
818
+ if (url) {
819
+ const spec = {
820
+ transport: "http",
821
+ url
822
+ };
823
+ if (headers.length > 0) {
824
+ const headerObj = {};
825
+ for (const h of headers) {
826
+ const colonIndex = h.indexOf(":");
827
+ if (colonIndex > 0) {
828
+ const key = h.slice(0, colonIndex).trim();
829
+ const value = h.slice(colonIndex + 1).trim();
830
+ headerObj[key] = value;
831
+ }
832
+ }
833
+ spec.headers = headerObj;
834
+ }
835
+ return { name, spec };
836
+ }
837
+ if (command) {
838
+ const spec = {
839
+ transport: "stdio",
840
+ command
841
+ };
842
+ if (commandArgs.length > 0) {
843
+ spec.args = commandArgs;
844
+ }
845
+ if (envVars.length > 0) {
846
+ const envObj = {};
847
+ for (const e of envVars) {
848
+ const eqIndex = e.indexOf("=");
849
+ if (eqIndex > 0) {
850
+ envObj[e.slice(0, eqIndex)] = e.slice(eqIndex + 1);
851
+ }
852
+ }
853
+ spec.env = envObj;
854
+ }
855
+ if (cwd) {
856
+ spec.cwd = cwd;
857
+ }
858
+ return { name, spec };
859
+ }
860
+ return { error: "Server name is required." };
861
+ }
862
+
863
+ // src/compat/hermes.ts
864
+ function parseHermesArgs(args) {
865
+ if (args.length === 0) {
866
+ return { error: "Usage: mcpx hermes mcp add <name> --url <url> | --command <cmd>" };
867
+ }
868
+ let name;
869
+ let url;
870
+ let command;
871
+ const envVars = [];
872
+ const commandArgs = [];
873
+ let hasAuth = false;
874
+ let hasPreset = false;
875
+ for (let i = 0;i < args.length; i++) {
876
+ const arg = args[i];
877
+ if (!name && !arg.startsWith("--")) {
878
+ name = arg;
879
+ continue;
880
+ }
881
+ if (arg === "--url") {
882
+ url = args[++i];
883
+ } else if (arg === "--command") {
884
+ command = args[++i];
885
+ } else if (arg === "--args") {
886
+ while (i + 1 < args.length && !args[i + 1].startsWith("--")) {
887
+ commandArgs.push(args[++i]);
888
+ }
889
+ } else if (arg === "--env") {
890
+ const val = args[++i];
891
+ if (val && !val.startsWith("--")) {
892
+ envVars.push(val);
893
+ }
894
+ } else if (arg === "--auth") {
895
+ hasAuth = true;
896
+ } else if (arg === "--preset") {
897
+ hasPreset = true;
898
+ i++;
899
+ }
900
+ }
901
+ if (!name) {
902
+ return { error: "Server name is required." };
903
+ }
904
+ if (hasAuth) {
905
+ return { error: "--auth is not supported. Use `mcpx auth` to configure auth after adding the server." };
906
+ }
907
+ if (hasPreset) {
908
+ return { error: "--preset is not supported. Use `mcpx add` directly for advanced configuration." };
909
+ }
910
+ if (url) {
911
+ return { name, spec: { transport: "http", url } };
912
+ }
913
+ if (command) {
914
+ const spec = { transport: "stdio", command };
915
+ if (commandArgs.length > 0) {
916
+ spec.args = commandArgs;
917
+ }
918
+ if (envVars.length > 0) {
919
+ const envObj = {};
920
+ for (const e of envVars) {
921
+ const eqIndex = e.indexOf("=");
922
+ if (eqIndex > 0) {
923
+ envObj[e.slice(0, eqIndex)] = e.slice(eqIndex + 1);
924
+ }
925
+ }
926
+ spec.env = envObj;
927
+ }
928
+ return { name, spec };
929
+ }
930
+ return { error: "Server name is required." };
931
+ }
932
+
732
933
  // src/compat/unsupported.ts
733
934
  var UNSUPPORTED_CLIENTS = {
734
935
  "cursor-agent": "Cursor MCP management uses a different interface. Use `mcpx add <name> <url|command>` directly to add MCP servers to mcpx.",
@@ -825,11 +1026,68 @@ function parseCompatibilityArgs(argv) {
825
1026
  normalizedArgs: result.normalizedArgs
826
1027
  };
827
1028
  }
1029
+ if (argv[0] === "openclaw" && argv[1] === "mcp" && argv[2] === "add") {
1030
+ const openclawArgs = argv.slice(3);
1031
+ const result = parseOpenClawArgs(openclawArgs);
1032
+ if ("error" in result) {
1033
+ return {
1034
+ client: "openclaw",
1035
+ normalizedArgs: null,
1036
+ error: result.error
1037
+ };
1038
+ }
1039
+ return {
1040
+ client: "openclaw",
1041
+ normalizedArgs: ["--name", result.name, ...buildSpecArgs(result.spec)]
1042
+ };
1043
+ }
1044
+ if (argv[0] === "hermes" && argv[1] === "mcp" && argv[2] === "add") {
1045
+ const hermesArgs = argv.slice(3);
1046
+ const result = parseHermesArgs(hermesArgs);
1047
+ if ("error" in result) {
1048
+ return {
1049
+ client: "hermes",
1050
+ normalizedArgs: null,
1051
+ error: result.error
1052
+ };
1053
+ }
1054
+ return {
1055
+ client: "hermes",
1056
+ normalizedArgs: ["--name", result.name, ...buildSpecArgs(result.spec)]
1057
+ };
1058
+ }
828
1059
  return {
829
1060
  client: null,
830
1061
  normalizedArgs: null
831
1062
  };
832
1063
  }
1064
+ function buildSpecArgs(spec) {
1065
+ const args = [];
1066
+ if (spec.transport === "http") {
1067
+ args.push(spec.url);
1068
+ if (spec.headers) {
1069
+ for (const [k, v] of Object.entries(spec.headers)) {
1070
+ args.push("--header", `${k}=${v}`);
1071
+ }
1072
+ }
1073
+ } else {
1074
+ args.push("--command", spec.command);
1075
+ if (Array.isArray(spec.args) && spec.args.length > 0) {
1076
+ for (const a of spec.args) {
1077
+ args.push("--arg", a);
1078
+ }
1079
+ }
1080
+ if (spec.env) {
1081
+ for (const [k, v] of Object.entries(spec.env)) {
1082
+ args.push("--env", `${k}=${v}`);
1083
+ }
1084
+ }
1085
+ if (spec.cwd) {
1086
+ args.push("--cwd", spec.cwd);
1087
+ }
1088
+ }
1089
+ return args;
1090
+ }
833
1091
 
834
1092
  // src/core/proxy.ts
835
1093
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -1057,6 +1315,10 @@ function printSyncSummary(summary, asJson = false) {
1057
1315
  }
1058
1316
  process.stdout.write(`
1059
1317
  `);
1318
+ if (result.driftedEntries && result.driftedEntries.length > 0) {
1319
+ process.stdout.write(` (note: ${result.driftedEntries.length} manually-edited entr${result.driftedEntries.length === 1 ? "y was" : "ies were"} reset to mcpx-managed values: ${result.driftedEntries.join(", ")})
1320
+ `);
1321
+ }
1060
1322
  }
1061
1323
  }
1062
1324
  async function ensureDaemonIfEnabled(cliPath, secrets) {
@@ -1069,11 +1331,14 @@ async function ensureDaemonIfEnabled(cliPath, secrets) {
1069
1331
  }
1070
1332
  await startDaemon(config, cliPath, secrets);
1071
1333
  }
1072
- async function autoSyncManagedEntries(config, cliPath) {
1334
+ async function autoSyncManagedEntries(cliPath) {
1073
1335
  const secrets = new SecretsManager;
1074
1336
  await ensureDaemonIfEnabled(cliPath, secrets);
1075
- const mergedConfig = loadMergedConfig();
1076
- const summary = syncAllClients(mergedConfig, secrets);
1337
+ const config = loadMergedConfig();
1338
+ const summary = syncAllClients(config, secrets);
1339
+ await mutateConfig((freshConfig) => {
1340
+ persistSyncState(summary, freshConfig);
1341
+ });
1077
1342
  printSyncSummary(summary, false);
1078
1343
  ensureExitCodeForSyncFailures(summary.hasErrors);
1079
1344
  }
@@ -1567,6 +1832,8 @@ async function reconnectGatewayAndSync(cliPath) {
1567
1832
  process.stdout.write(`${restart.message} pid=${restart.pid} port=${restart.port}
1568
1833
  `);
1569
1834
  const summary = syncAllClients(config, secrets);
1835
+ persistSyncState(summary, config);
1836
+ saveConfig(config);
1570
1837
  printSyncSummary(summary, false);
1571
1838
  }
1572
1839
  async function setServerEnabledInteractively(rl, serverName, enabled, cliPath) {
@@ -1595,7 +1862,7 @@ async function setServerEnabledInteractively(rl, serverName, enabled, cliPath) {
1595
1862
  `);
1596
1863
  process.stdout.write(`Auto-syncing managed gateway entries across all supported clients...
1597
1864
  `);
1598
- await autoSyncManagedEntries(loadMergedConfig(), cliPath);
1865
+ await autoSyncManagedEntries(cliPath);
1599
1866
  return true;
1600
1867
  }
1601
1868
  }
@@ -1609,7 +1876,7 @@ async function setServerEnabledInteractively(rl, serverName, enabled, cliPath) {
1609
1876
  `);
1610
1877
  process.stdout.write(`Auto-syncing managed gateway entries across all supported clients...
1611
1878
  `);
1612
- await autoSyncManagedEntries(loadMergedConfig(), cliPath);
1879
+ await autoSyncManagedEntries(cliPath);
1613
1880
  return true;
1614
1881
  }
1615
1882
  function buildServerActionTitle(server) {
@@ -1731,25 +1998,23 @@ async function runInteractiveStatusMenu(cliPath) {
1731
1998
  }
1732
1999
  function registerEnabledCommand(parent, enabled, cliPath) {
1733
2000
  parent.command(enabled ? "enable <name>" : "disable <name>").option("-g, --global", "Force target the global configuration").option("-l, --local", "Force target (or initialize) a local `.mcpx.json` configuration").description(`${enabled ? "Enable" : "Disable"} an upstream MCP server`).action(async (name, options) => {
1734
- const context = resolveActiveConfig({ global: options.global, local: options.local });
1735
- setServerEnabled(context.config, name, enabled);
1736
- context.save();
1737
- if (context.type === "project" && context.projectPath) {
1738
- const globalConfig = loadConfig();
1739
- registerProject(globalConfig, context.projectPath);
1740
- saveConfig(globalConfig);
2001
+ const { type, projectPath } = await mutateActiveConfig({ global: options.global, local: options.local }, (config) => {
2002
+ setServerEnabled(config, name, enabled);
2003
+ });
2004
+ if (type === "project" && projectPath) {
2005
+ await mutateConfig((globalConfig) => {
2006
+ registerProject(globalConfig, projectPath, globalConfig.projects?.[path.resolve(projectPath)]?.name);
2007
+ });
1741
2008
  }
1742
- process.stdout.write(`${enabled ? "Enabled" : "Disabled"} server: ${name} (${context.type} config)
2009
+ process.stdout.write(`${enabled ? "Enabled" : "Disabled"} server: ${name} (${type} config)
1743
2010
  `);
1744
2011
  process.stdout.write(`Auto-syncing managed gateway entries across all supported clients...
1745
2012
  `);
1746
- const mergedConfig = loadMergedConfig();
1747
- await autoSyncManagedEntries(mergedConfig, cliPath);
2013
+ await autoSyncManagedEntries(cliPath);
1748
2014
  });
1749
2015
  }
1750
2016
  function registerAddCommand(parent, cliPath) {
1751
2017
  parent.command("add [values...]").allowUnknownOption(true).option("--transport <transport>", "MCP transport (auto|http|stdio)", "auto").option("--header <header>", "Header entry in KEY=VALUE form", (value, previous = []) => [...previous, value], []).option("--env <env>", "Env entry in KEY=VALUE form for stdio upstreams", (value, previous = []) => [...previous, value], []).option("--cwd <cwd>", "Working directory for stdio upstream").option("--force", "Overwrite existing server with same name").option("-g, --global", "Force saving to global configuration").option("-l, --local", "Force saving to local project configuration").description("Add an upstream MCP server").action(async (values, options) => {
1752
- const context = resolveActiveConfig({ global: options.global, local: options.local });
1753
2018
  const safeValues = values ?? [];
1754
2019
  const secrets = new SecretsManager;
1755
2020
  if (safeValues.length === 1 && safeValues[0]?.includes("/")) {
@@ -1804,46 +2069,46 @@ Value: ` : `${input.name} (${input.kind}): `;
1804
2069
  spec.env = Object.keys(env).length > 0 ? env : undefined;
1805
2070
  }
1806
2071
  }
1807
- if (context.type === "project") {
1808
- const globalConfig = loadConfig();
1809
- registerProject(globalConfig, context.projectPath);
1810
- saveConfig(globalConfig);
1811
- }
1812
- addServer(context.config, shortName, spec, options.force ?? false);
1813
2072
  await maybeAutoConfigureAuthForAddedServer(shortName, spec, secrets);
1814
- context.save();
1815
- process.stdout.write(`Added server: ${shortName} (${spec.transport}) from registry "${registryName}"${context.type === "project" ? ` (project: ${context.projectPath})` : ""}
2073
+ const { type, projectPath } = await mutateActiveConfig({ global: options.global, local: options.local }, (config) => {
2074
+ addServer(config, shortName, spec, options.force ?? false);
2075
+ });
2076
+ if (type === "project" && projectPath) {
2077
+ await mutateConfig((globalConfig) => {
2078
+ registerProject(globalConfig, projectPath, globalConfig.projects?.[path.resolve(projectPath)]?.name);
2079
+ });
2080
+ }
2081
+ process.stdout.write(`Added server: ${shortName} (${spec.transport}) from registry "${registryName}"${type === "project" ? ` (project: ${projectPath})` : ""}
1816
2082
  `);
1817
2083
  } else {
1818
2084
  const parsed = parseAddServerSpec(safeValues, options);
1819
- if (context.type === "project") {
1820
- const globalConfig = loadConfig();
1821
- registerProject(globalConfig, context.projectPath);
1822
- saveConfig(globalConfig);
1823
- }
1824
- addServer(context.config, parsed.name, parsed.spec, options.force ?? false);
1825
2085
  await maybeAutoConfigureAuthForAddedServer(parsed.name, parsed.spec, secrets);
1826
- context.save();
1827
- process.stdout.write(`Added server: ${parsed.name} (${parsed.spec.transport})${context.type === "project" ? ` (project: ${context.projectPath})` : ""}
2086
+ const { type, projectPath } = await mutateActiveConfig({ global: options.global, local: options.local }, (config) => {
2087
+ addServer(config, parsed.name, parsed.spec, options.force ?? false);
2088
+ });
2089
+ if (type === "project" && projectPath) {
2090
+ await mutateConfig((globalConfig) => {
2091
+ registerProject(globalConfig, projectPath, globalConfig.projects?.[path.resolve(projectPath)]?.name);
2092
+ });
2093
+ }
2094
+ process.stdout.write(`Added server: ${parsed.name} (${parsed.spec.transport})${type === "project" ? ` (project: ${projectPath})` : ""}
1828
2095
  `);
1829
2096
  }
1830
2097
  process.stdout.write(`Auto-syncing managed gateway entries across all supported clients...
1831
2098
  `);
1832
- const mergedConfig = loadMergedConfig();
1833
- await autoSyncManagedEntries(mergedConfig, cliPath);
2099
+ await autoSyncManagedEntries(cliPath);
1834
2100
  });
1835
2101
  }
1836
2102
  function registerRemoveCommand(parent, cliPath) {
1837
2103
  parent.command("remove <name>").option("--force", "Do not error if server does not exist").option("-g, --global", "Force target global configuration").option("-l, --local", "Force target local project configuration").description("Remove an upstream MCP server").action(async (name, options) => {
1838
- const context = resolveActiveConfig({ global: options.global, local: options.local });
1839
- removeServer(context.config, name, options.force ?? false);
1840
- context.save();
1841
- process.stdout.write(`Removed server: ${name} ${context.type === "project" ? `from project: ${context.projectPath}` : "globally"}
2104
+ const { type, projectPath } = await mutateActiveConfig({ global: options.global, local: options.local }, (config) => {
2105
+ removeServer(config, name, options.force ?? false);
2106
+ });
2107
+ process.stdout.write(`Removed server: ${name} ${type === "project" ? `from project: ${projectPath}` : "globally"}
1842
2108
  `);
1843
2109
  process.stdout.write(`Auto-syncing managed gateway entries across all supported clients...
1844
2110
  `);
1845
- const mergedConfig = loadMergedConfig();
1846
- await autoSyncManagedEntries(mergedConfig, cliPath);
2111
+ await autoSyncManagedEntries(cliPath);
1847
2112
  });
1848
2113
  }
1849
2114
  function registerListCommand(parent) {
@@ -1914,12 +2179,14 @@ function registerListCommand(parent) {
1914
2179
  });
1915
2180
  }
1916
2181
  function registerSyncCommand(program, cliPath) {
1917
- program.command("sync [clients...]").option("--client <id>", "Limit sync to specific client(s), comma-separated or repeated", (value, prev = []) => [...prev, value], []).option("--json", "Output JSON").description("Sync gateway configuration to supported clients (e.g. `mcpx sync claude`)").action(async (clients, options) => {
1918
- const config = loadMergedConfig();
2182
+ program.command("sync [clients...]").option("--client <id>", "Limit sync to specific client(s), comma-separated or repeated", (value, prev = []) => [...prev, value], []).option("--import", "Also scan clients for unmanaged servers and adopt them into the mcpx catalog", false).option("--json", "Output JSON").description("Sync gateway configuration to supported clients (e.g. `mcpx sync claude`)").action(async (clients, options) => {
2183
+ const config = loadConfig();
1919
2184
  const targetClients = parseClientList([...clients ?? [], ...options.client ?? []]);
1920
2185
  const secrets = new SecretsManager;
1921
2186
  await ensureDaemonIfEnabled(cliPath, secrets);
1922
- const summary = syncAllClients(config, secrets, targetClients);
2187
+ const summary = syncAllClients(config, secrets, { targetClients, importScan: options.import ?? false });
2188
+ persistSyncState(summary, config);
2189
+ saveConfig(config);
1923
2190
  printSyncSummary(summary, options.json ?? false);
1924
2191
  ensureExitCodeForSyncFailures(summary.hasErrors);
1925
2192
  });
@@ -2309,11 +2576,11 @@ function registerProjectCommands(program, cliPath) {
2309
2576
  name: projectName,
2310
2577
  servers: {}
2311
2578
  };
2312
- const { saveProjectConfig: saveProjectConfig2 } = await import("./config-2s5nsm2g.js");
2579
+ const { saveProjectConfig: saveProjectConfig2 } = await import("./config-5p08cb0c.js");
2313
2580
  saveProjectConfig2(localConfig, targetPath);
2314
2581
  process.stdout.write(`Initialized project configuration at: ${targetPath}
2315
2582
  `);
2316
- const { registerProject: registerProject2 } = await import("./registry-mztfy0cz.js");
2583
+ const { registerProject: registerProject2 } = await import("./registry-n12thgf2.js");
2317
2584
  const globalConfig = loadConfig();
2318
2585
  registerProject2(globalConfig, process.cwd(), projectName);
2319
2586
  saveConfig(globalConfig);
@@ -2344,21 +2611,20 @@ function registerProjectCommands(program, cliPath) {
2344
2611
  process.exit(1);
2345
2612
  }
2346
2613
  const entry = globalConfig.projects[resolvedPath];
2347
- const { unregisterProject: unregisterProject2 } = await import("./registry-mztfy0cz.js");
2614
+ const { unregisterProject: unregisterProject2 } = await import("./registry-n12thgf2.js");
2348
2615
  unregisterProject2(globalConfig, resolvedPath);
2349
2616
  saveConfig(globalConfig);
2350
2617
  process.stdout.write(`Unregistered project "${entry.name}" (${resolvedPath}) from the global configuration.
2351
2618
  `);
2352
2619
  process.stdout.write(`Auto-syncing managed gateway entries across all supported clients...
2353
2620
  `);
2354
- const mergedConfig = loadMergedConfig();
2355
- await autoSyncManagedEntries(mergedConfig, cliPath);
2621
+ await autoSyncManagedEntries(cliPath);
2356
2622
  });
2357
2623
  }
2358
2624
  function registerPluginCommands(program, _cliPath) {
2359
2625
  const plugin = program.command("plugin").description("Manage plugins");
2360
2626
  plugin.command("inspect <source>").description("Inspect plugin manifest and components").action(async (source) => {
2361
- const { inspectPlugin } = await import("./plugin-manager-1sc4b6am.js");
2627
+ const { inspectPlugin } = await import("./plugin-manager-m8ar2271.js");
2362
2628
  try {
2363
2629
  const info = await inspectPlugin(source);
2364
2630
  process.stdout.write(JSON.stringify(info, null, 2) + `
@@ -2370,7 +2636,7 @@ function registerPluginCommands(program, _cliPath) {
2370
2636
  }
2371
2637
  });
2372
2638
  plugin.command("install <source>").option("--name <name>", "Plugin name").option("--no-enable", "Don't enable plugin after install").description("Install plugin").action(async (source, options) => {
2373
- const { installPlugin } = await import("./plugin-manager-1sc4b6am.js");
2639
+ const { installPlugin } = await import("./plugin-manager-m8ar2271.js");
2374
2640
  try {
2375
2641
  const p = await installPlugin(source, {
2376
2642
  name: options.name,
@@ -2385,7 +2651,7 @@ function registerPluginCommands(program, _cliPath) {
2385
2651
  }
2386
2652
  });
2387
2653
  plugin.command("approve <name> <component>").description("Approve a plugin component (hooks, mcpServers)").action(async (name, component) => {
2388
- const { approvePluginComponent } = await import("./plugin-manager-1sc4b6am.js");
2654
+ const { approvePluginComponent } = await import("./plugin-manager-m8ar2271.js");
2389
2655
  try {
2390
2656
  await approvePluginComponent(name, component);
2391
2657
  process.stdout.write(`Approved ${component} for plugin ${name}
@@ -2397,7 +2663,7 @@ function registerPluginCommands(program, _cliPath) {
2397
2663
  }
2398
2664
  });
2399
2665
  plugin.command("prepare <name>").description("Install plugin dependencies").action(async (name) => {
2400
- const { preparePlugin } = await import("./plugin-manager-1sc4b6am.js");
2666
+ const { preparePlugin } = await import("./plugin-manager-m8ar2271.js");
2401
2667
  try {
2402
2668
  await preparePlugin(name);
2403
2669
  process.stdout.write(`Plugin ${name} prepared
@@ -2409,7 +2675,7 @@ function registerPluginCommands(program, _cliPath) {
2409
2675
  }
2410
2676
  });
2411
2677
  plugin.command("update <name>").description("Update plugin to latest version").action(async (name) => {
2412
- const { updatePlugin } = await import("./plugin-manager-1sc4b6am.js");
2678
+ const { updatePlugin } = await import("./plugin-manager-m8ar2271.js");
2413
2679
  try {
2414
2680
  const p = await updatePlugin(name);
2415
2681
  process.stdout.write(`Plugin ${name} updated to ${p.version}
@@ -2421,7 +2687,7 @@ function registerPluginCommands(program, _cliPath) {
2421
2687
  }
2422
2688
  });
2423
2689
  plugin.command("uninstall <name>").option("--keep-data", "Keep plugin data after uninstall").description("Uninstall plugin").action(async (name, options) => {
2424
- const { uninstallPlugin } = await import("./plugin-manager-1sc4b6am.js");
2690
+ const { uninstallPlugin } = await import("./plugin-manager-m8ar2271.js");
2425
2691
  try {
2426
2692
  await uninstallPlugin(name, options.keepData || false);
2427
2693
  process.stdout.write(`Plugin ${name} uninstalled
@@ -2432,24 +2698,40 @@ function registerPluginCommands(program, _cliPath) {
2432
2698
  process.exit(1);
2433
2699
  }
2434
2700
  });
2435
- plugin.command("enable <name>").description("Enable plugin").action(async (name) => {
2436
- const { enablePlugin } = await import("./plugin-manager-1sc4b6am.js");
2701
+ plugin.command("enable <name>").option("--project <path>", "Scope this enable to a single registered project instead of globally").description("Enable plugin (globally, or for one project with --project)").action(async (name, options) => {
2437
2702
  try {
2438
- await enablePlugin(name);
2439
- process.stdout.write(`Plugin ${name} enabled
2703
+ if (options.project) {
2704
+ const { setPluginProjectOverride } = await import("./plugin-manager-m8ar2271.js");
2705
+ const resolvedPath = path.resolve(options.project);
2706
+ await setPluginProjectOverride(name, resolvedPath, { enabled: true });
2707
+ process.stdout.write(`Plugin ${name} enabled for project: ${resolvedPath}
2440
2708
  `);
2709
+ } else {
2710
+ const { enablePlugin } = await import("./plugin-manager-m8ar2271.js");
2711
+ await enablePlugin(name);
2712
+ process.stdout.write(`Plugin ${name} enabled
2713
+ `);
2714
+ }
2441
2715
  } catch (e) {
2442
2716
  process.stderr.write(`Error: ${e.message}
2443
2717
  `);
2444
2718
  process.exit(1);
2445
2719
  }
2446
2720
  });
2447
- plugin.command("disable <name>").description("Disable plugin").action(async (name) => {
2448
- const { disablePlugin } = await import("./plugin-manager-1sc4b6am.js");
2721
+ plugin.command("disable <name>").option("--project <path>", "Scope this disable to a single registered project instead of globally").description("Disable plugin (globally, or for one project with --project)").action(async (name, options) => {
2449
2722
  try {
2450
- await disablePlugin(name);
2451
- process.stdout.write(`Plugin ${name} disabled
2723
+ if (options.project) {
2724
+ const { setPluginProjectOverride } = await import("./plugin-manager-m8ar2271.js");
2725
+ const resolvedPath = path.resolve(options.project);
2726
+ await setPluginProjectOverride(name, resolvedPath, { enabled: false });
2727
+ process.stdout.write(`Plugin ${name} disabled for project: ${resolvedPath}
2452
2728
  `);
2729
+ } else {
2730
+ const { disablePlugin } = await import("./plugin-manager-m8ar2271.js");
2731
+ await disablePlugin(name);
2732
+ process.stdout.write(`Plugin ${name} disabled
2733
+ `);
2734
+ }
2453
2735
  } catch (e) {
2454
2736
  process.stderr.write(`Error: ${e.message}
2455
2737
  `);
@@ -2457,7 +2739,7 @@ function registerPluginCommands(program, _cliPath) {
2457
2739
  }
2458
2740
  });
2459
2741
  plugin.command("status [name]").description("Show plugin status").action(async (name) => {
2460
- const { getPluginStatus, listPlugins } = await import("./plugin-manager-1sc4b6am.js");
2742
+ const { getPluginStatus, listPlugins } = await import("./plugin-manager-m8ar2271.js");
2461
2743
  try {
2462
2744
  if (name) {
2463
2745
  const p = await getPluginStatus(name);
@@ -2480,6 +2762,12 @@ function registerPluginCommands(program, _cliPath) {
2480
2762
  }
2481
2763
  });
2482
2764
  }
2765
+ function registerPluginHostCommand(program) {
2766
+ program.command("plugin-host <plugin> <server>").description("Run a plugin MCP server under mcpx policy enforcement").action(async (plugin, server) => {
2767
+ const { runPluginHost } = await import("./plugin-host-954a7tf7.js");
2768
+ runPluginHost(plugin, server);
2769
+ });
2770
+ }
2483
2771
  function registerProxyCommand(program) {
2484
2772
  program.command("proxy <name>").description("Run a stdio MCP proxy bridging to the local mcpx HTTP gateway for the given upstream server").action(async (name) => {
2485
2773
  await runStdioProxy(name);
@@ -2504,14 +2792,19 @@ async function runCli(argv = process.argv) {
2504
2792
  const stagedCliPath = getStagedCliPath();
2505
2793
  const stagedInfo = getStagedUpdate();
2506
2794
  if (stagedCliPath && stagedInfo && stagedCliPath !== argv[1] && shouldUseStagedCli(stagedInfo.version, APP_VERSION)) {
2507
- execFileSync(process.execPath, [stagedCliPath, ...rawArgs], {
2508
- stdio: "inherit",
2509
- env: {
2510
- ...process.env,
2511
- MCPX_USING_STAGED: "1"
2512
- }
2513
- });
2514
- return;
2795
+ try {
2796
+ execFileSync(process.execPath, [stagedCliPath, ...rawArgs], {
2797
+ stdio: "inherit",
2798
+ env: {
2799
+ ...process.env,
2800
+ MCPX_USING_STAGED: "1"
2801
+ }
2802
+ });
2803
+ return;
2804
+ } catch (error) {
2805
+ process.stderr.write(`Warning: staged update v${stagedInfo.version} failed to run (${error.message}). ` + `Falling back to the currently installed version (v${APP_VERSION}). ` + `Run "mcpx update rollback" to clear the broken staged update.
2806
+ `);
2807
+ }
2515
2808
  }
2516
2809
  }
2517
2810
  if (rawArgs.length > 0) {
@@ -2541,6 +2834,7 @@ async function runCli(argv = process.argv) {
2541
2834
  registerAuthCommands(program);
2542
2835
  registerClientsCommands(program);
2543
2836
  registerSkillsCommands(program);
2837
+ registerPluginHostCommand(program);
2544
2838
  registerProxyCommand(program);
2545
2839
  registerMcpCompat(program, cliPath);
2546
2840
  registerUpdateCommand(program);
@@ -2550,8 +2844,25 @@ async function runCli(argv = process.argv) {
2550
2844
  }
2551
2845
  if (process.argv[1] && import.meta.url === pathToFileURL(fs.realpathSync(process.argv[1])).href) {
2552
2846
  runCli().catch((error) => {
2553
- process.stderr.write(`${error.message}
2847
+ if (error instanceof ConfigLoadError) {
2848
+ process.stderr.write(`Config error: ${error.message}
2554
2849
  `);
2850
+ if (error.backupPath) {
2851
+ process.stderr.write(`Backup saved to: ${error.backupPath}
2852
+ `);
2853
+ }
2854
+ if (error.issues && error.issues.length > 0) {
2855
+ for (const issue of error.issues.slice(0, 5)) {
2856
+ process.stderr.write(` - ${issue}
2857
+ `);
2858
+ }
2859
+ }
2860
+ process.stderr.write(`Fix the config file or restore from backup.
2861
+ `);
2862
+ } else {
2863
+ process.stderr.write(`${error.message}
2864
+ `);
2865
+ }
2555
2866
  process.exit(1);
2556
2867
  });
2557
2868
  }