@kwonye/mcpx 0.1.93 → 0.1.94

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 (47) hide show
  1. package/dist/adapters/cline.d.ts.map +1 -1
  2. package/dist/adapters/codex.d.ts.map +1 -1
  3. package/dist/adapters/kiro.d.ts.map +1 -1
  4. package/dist/adapters/opencode.d.ts.map +1 -1
  5. package/dist/adapters/utils/index.d.ts +11 -3
  6. package/dist/adapters/utils/index.d.ts.map +1 -1
  7. package/dist/{cli-y7ba5qvr.js → cli-154shqmt.js} +126 -68
  8. package/dist/{cli-qvkpwy9w.js → cli-9fmn9y72.js} +36 -17
  9. package/dist/{cli-np0wwh6m.js → cli-csfsg1yf.js} +78 -4
  10. package/dist/{cli-m2645syd.js → cli-mxmgxdm2.js} +127 -69
  11. package/dist/{cli-kwyp9ywh.js → cli-n8vt375e.js} +306 -172
  12. package/dist/{cli-8vxva0cb.js → cli-tk8bggg5.js} +81 -8
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +45 -21
  15. package/dist/{config-2s5nsm2g.js → config-dtc3c79c.js} +6 -2
  16. package/dist/core/config-repair.d.ts.map +1 -1
  17. package/dist/core/config-store.d.ts +3 -0
  18. package/dist/core/config-store.d.ts.map +1 -0
  19. package/dist/core/config.d.ts +13 -1
  20. package/dist/core/config.d.ts.map +1 -1
  21. package/dist/core/daemon.d.ts +1 -1
  22. package/dist/core/daemon.d.ts.map +1 -1
  23. package/dist/core/errors.d.ts +18 -0
  24. package/dist/core/errors.d.ts.map +1 -0
  25. package/dist/core/index.d.ts +5 -2
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/index.js +81 -6
  28. package/dist/core/oauth.d.ts.map +1 -1
  29. package/dist/core/plugin-manager.d.ts.map +1 -1
  30. package/dist/core/plugin-projections.d.ts +6 -2
  31. package/dist/core/plugin-projections.d.ts.map +1 -1
  32. package/dist/core/registry.d.ts +4 -1
  33. package/dist/core/registry.d.ts.map +1 -1
  34. package/dist/core/secrets.d.ts.map +1 -1
  35. package/dist/core/server-auth.d.ts.map +1 -1
  36. package/dist/core/status.d.ts +1 -0
  37. package/dist/core/status.d.ts.map +1 -1
  38. package/dist/core/sync.d.ts +19 -2
  39. package/dist/core/sync.d.ts.map +1 -1
  40. package/dist/gateway/server.d.ts +4 -1
  41. package/dist/gateway/server.d.ts.map +1 -1
  42. package/dist/{plugin-manager-1sc4b6am.js → plugin-manager-1sjwfnah.js} +4 -4
  43. package/dist/{registry-mztfy0cz.js → registry-n12thgf2.js} +1 -1
  44. package/dist/types.d.ts +2 -0
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/version.d.ts +1 -1
  47. package/package.json +1 -1
@@ -1,9 +1,6 @@
1
- import {
2
- saveConfig
3
- } from "./cli-np0wwh6m.js";
4
1
  import {
5
2
  ensureGatewayToken
6
- } from "./cli-8vxva0cb.js";
3
+ } from "./cli-tk8bggg5.js";
7
4
  import {
8
5
  ensureDir,
9
6
  getManagedIndexPath,
@@ -75,10 +72,10 @@ function loadOwnership(targetDir) {
75
72
  }
76
73
  }
77
74
  function saveOwnership(targetDir, manifest) {
75
+ ensureDir(targetDir);
78
76
  fs2.writeFileSync(path2.join(targetDir, OWNERSHIP_MANIFEST), JSON.stringify(manifest, null, 2));
79
77
  }
80
78
  function recordOwned(targetDir, pluginId, paths) {
81
- ensureDir(targetDir);
82
79
  const manifest = loadOwnership(targetDir);
83
80
  manifest.plugins[pluginId] = {
84
81
  pluginId,
@@ -114,16 +111,42 @@ function pruneAllPluginProjections(targetDir) {
114
111
  prunePluginProjections(targetDir, pluginId);
115
112
  }
116
113
  }
114
+ function pruneUnlistedPlugins(targetDir, keepPluginIds) {
115
+ const manifest = loadOwnership(targetDir);
116
+ const keep = new Set(keepPluginIds);
117
+ for (const [pluginId] of Object.entries(manifest.plugins)) {
118
+ if (!keep.has(pluginId)) {
119
+ prunePluginProjections(targetDir, pluginId);
120
+ }
121
+ }
122
+ }
117
123
  function nsName(pluginName, id) {
118
124
  return `${pluginName}__${id}`;
119
125
  }
126
+ function isComponentApproved(plugin, component) {
127
+ const approval = plugin.approvals?.[component];
128
+ if (approval === false)
129
+ return false;
130
+ if (approval === true)
131
+ return true;
132
+ if (component === "hooks")
133
+ return false;
134
+ if (component === "mcpServers")
135
+ return plugin.components.mcpServers;
136
+ return plugin.components[component] ?? false;
137
+ }
120
138
  function syncClaude(plugins) {
121
139
  const targetBase = path2.join(homeDir(), ".claude", "skills");
122
140
  const projectedDirs = [];
123
141
  const unsupported = [];
142
+ const skippedUnapproved = [];
124
143
  for (const plugin of plugins) {
125
144
  if (!plugin.enabled)
126
145
  continue;
146
+ if (!isComponentApproved(plugin, "skills")) {
147
+ skippedUnapproved.push(`${plugin.pluginName}:skills`);
148
+ continue;
149
+ }
127
150
  const targetDir = path2.join(targetBase, plugin.pluginName);
128
151
  ensureDir(targetDir);
129
152
  const mcpJsonSrc = path2.join(plugin.pluginRoot, ".mcp.json");
@@ -156,18 +179,20 @@ function syncCodex(plugins) {
156
179
  const projectedDirs = [];
157
180
  const unsupported = ["hooks", "agents"];
158
181
  for (const plugin of plugins) {
159
- if (!plugin.enabled || !plugin.components.skills)
182
+ if (!plugin.enabled)
160
183
  continue;
161
- ensureDir(targetBase);
162
- const owned = [];
163
- for (const skill of plugin.skills) {
164
- const targetDir = path2.join(targetBase, nsName(plugin.pluginName, skill.id));
165
- ensureDir(targetDir);
166
- copyFileOrDir(skill.path, path2.join(targetDir, "SKILL.md"));
167
- owned.push(nsName(plugin.pluginName, skill.id));
168
- projectedDirs.push(targetDir);
184
+ if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
185
+ ensureDir(targetBase);
186
+ const owned = [];
187
+ for (const skill of plugin.skills) {
188
+ const targetDir = path2.join(targetBase, nsName(plugin.pluginName, skill.id));
189
+ ensureDir(targetDir);
190
+ copyFileOrDir(skill.path, path2.join(targetDir, "SKILL.md"));
191
+ owned.push(nsName(plugin.pluginName, skill.id));
192
+ projectedDirs.push(targetDir);
193
+ }
194
+ recordOwned(targetBase, plugin.pluginId, owned);
169
195
  }
170
- recordOwned(targetBase, plugin.pluginId, owned);
171
196
  }
172
197
  return {
173
198
  clientId: "codex",
@@ -186,7 +211,7 @@ function syncCursor(plugins) {
186
211
  for (const plugin of plugins) {
187
212
  if (!plugin.enabled)
188
213
  continue;
189
- if (plugin.components.skills) {
214
+ if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
190
215
  ensureDir(skillsBase);
191
216
  for (const skill of plugin.skills) {
192
217
  const targetDir = path2.join(skillsBase, nsName(plugin.pluginName, skill.id));
@@ -196,7 +221,7 @@ function syncCursor(plugins) {
196
221
  projectedDirs.push(targetDir);
197
222
  }
198
223
  }
199
- if (plugin.components.commands) {
224
+ if (plugin.components.commands && isComponentApproved(plugin, "commands")) {
200
225
  ensureDir(commandsBase);
201
226
  for (const cmd of plugin.commands) {
202
227
  const targetPath = path2.join(commandsBase, `${nsName(plugin.pluginName, cmd.id)}.md`);
@@ -222,18 +247,20 @@ function syncVsCode(plugins) {
222
247
  const projectedDirs = [];
223
248
  const unsupported = ["hooks", "agents"];
224
249
  for (const plugin of plugins) {
225
- if (!plugin.enabled || !plugin.components.skills)
250
+ if (!plugin.enabled)
226
251
  continue;
227
- ensureDir(skillsBase);
228
- const owned = [];
229
- for (const skill of plugin.skills) {
230
- const targetDir = path2.join(skillsBase, nsName(plugin.pluginName, skill.id));
231
- ensureDir(targetDir);
232
- copyFileOrDir(skill.path, path2.join(targetDir, "SKILL.md"));
233
- owned.push(nsName(plugin.pluginName, skill.id));
234
- projectedDirs.push(targetDir);
252
+ if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
253
+ ensureDir(skillsBase);
254
+ const owned = [];
255
+ for (const skill of plugin.skills) {
256
+ const targetDir = path2.join(skillsBase, nsName(plugin.pluginName, skill.id));
257
+ ensureDir(targetDir);
258
+ copyFileOrDir(skill.path, path2.join(targetDir, "SKILL.md"));
259
+ owned.push(nsName(plugin.pluginName, skill.id));
260
+ projectedDirs.push(targetDir);
261
+ }
262
+ recordOwned(skillsBase, plugin.pluginId, owned);
235
263
  }
236
- recordOwned(skillsBase, plugin.pluginId, owned);
237
264
  }
238
265
  return {
239
266
  clientId: "vscode",
@@ -242,117 +269,146 @@ function syncVsCode(plugins) {
242
269
  unsupported
243
270
  };
244
271
  }
245
- function syncKiro(plugins) {
246
- const skillsBase = path2.join(homeDir(), ".kiro", "skills");
272
+ function syncQwen(plugins) {
273
+ const targetBase = path2.join(homeDir(), ".qwen", "skills");
247
274
  const projectedDirs = [];
248
275
  const unsupported = ["hooks", "agents", "commands"];
249
276
  for (const plugin of plugins) {
250
- if (!plugin.enabled || !plugin.components.skills)
277
+ if (!plugin.enabled)
251
278
  continue;
252
- ensureDir(skillsBase);
253
- const owned = [];
254
- for (const skill of plugin.skills) {
255
- const targetPath = path2.join(skillsBase, `${nsName(plugin.pluginName, skill.id)}.md`);
256
- copyFileOrDir(skill.path, targetPath);
257
- owned.push(`${nsName(plugin.pluginName, skill.id)}.md`);
258
- projectedDirs.push(targetPath);
279
+ if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
280
+ ensureDir(targetBase);
281
+ const owned = [];
282
+ for (const skill of plugin.skills) {
283
+ const targetDir = path2.join(targetBase, nsName(plugin.pluginName, skill.id));
284
+ ensureDir(targetDir);
285
+ copyFileOrDir(skill.path, path2.join(targetDir, "SKILL.md"));
286
+ owned.push(nsName(plugin.pluginName, skill.id));
287
+ projectedDirs.push(targetDir);
288
+ }
289
+ recordOwned(targetBase, plugin.pluginId, owned);
259
290
  }
260
- recordOwned(skillsBase, plugin.pluginId, owned);
261
291
  }
262
292
  return {
263
- clientId: "kiro",
293
+ clientId: "qwen",
264
294
  status: "SYNCED",
265
295
  projectedDirs,
266
296
  unsupported
267
297
  };
268
298
  }
269
- function syncQwen(plugins) {
270
- const skillsBase = path2.join(homeDir(), ".qwen", "skills");
271
- const commandsBase = path2.join(homeDir(), ".qwen", "commands");
299
+ function syncCline(plugins) {
300
+ const targetBase = path2.join(homeDir(), ".config", "cline", "skills");
272
301
  const projectedDirs = [];
273
- const unsupported = ["hooks", "agents"];
274
- const ownedSkills = [];
275
- const ownedCommands = [];
302
+ const unsupported = ["hooks", "agents", "commands"];
276
303
  for (const plugin of plugins) {
277
304
  if (!plugin.enabled)
278
305
  continue;
279
- if (plugin.components.skills) {
280
- ensureDir(skillsBase);
306
+ if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
307
+ ensureDir(targetBase);
308
+ const owned = [];
281
309
  for (const skill of plugin.skills) {
282
- const targetPath = path2.join(skillsBase, `${nsName(plugin.pluginName, skill.id)}.md`);
283
- copyFileOrDir(skill.path, targetPath);
284
- ownedSkills.push(`${nsName(plugin.pluginName, skill.id)}.md`);
285
- projectedDirs.push(targetPath);
310
+ const targetDir = path2.join(targetBase, nsName(plugin.pluginName, skill.id));
311
+ ensureDir(targetDir);
312
+ copyFileOrDir(skill.path, path2.join(targetDir, "SKILL.md"));
313
+ owned.push(nsName(plugin.pluginName, skill.id));
314
+ projectedDirs.push(targetDir);
286
315
  }
316
+ recordOwned(targetBase, plugin.pluginId, owned);
287
317
  }
288
- if (plugin.components.commands) {
289
- ensureDir(commandsBase);
290
- for (const cmd of plugin.commands) {
291
- const targetPath = path2.join(commandsBase, `${nsName(plugin.pluginName, cmd.id)}.md`);
292
- copyFileOrDir(cmd.path, targetPath);
293
- ownedCommands.push(`${nsName(plugin.pluginName, cmd.id)}.md`);
294
- projectedDirs.push(targetPath);
318
+ }
319
+ return {
320
+ clientId: "cline",
321
+ status: "SYNCED",
322
+ projectedDirs,
323
+ unsupported
324
+ };
325
+ }
326
+ function syncKiro(plugins) {
327
+ const targetBase = path2.join(homeDir(), ".kiro", "skills");
328
+ const projectedDirs = [];
329
+ const unsupported = ["hooks", "agents", "commands"];
330
+ for (const plugin of plugins) {
331
+ if (!plugin.enabled)
332
+ continue;
333
+ if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
334
+ ensureDir(targetBase);
335
+ const owned = [];
336
+ for (const skill of plugin.skills) {
337
+ const targetDir = path2.join(targetBase, nsName(plugin.pluginName, skill.id));
338
+ ensureDir(targetDir);
339
+ copyFileOrDir(skill.path, path2.join(targetDir, "SKILL.md"));
340
+ owned.push(nsName(plugin.pluginName, skill.id));
341
+ projectedDirs.push(targetDir);
295
342
  }
343
+ recordOwned(targetBase, plugin.pluginId, owned);
296
344
  }
297
- if (ownedSkills.length > 0)
298
- recordOwned(skillsBase, plugin.pluginId, ownedSkills);
299
- if (ownedCommands.length > 0)
300
- recordOwned(commandsBase, plugin.pluginId, ownedCommands);
301
345
  }
302
346
  return {
303
- clientId: "qwen",
347
+ clientId: "kiro",
304
348
  status: "SYNCED",
305
349
  projectedDirs,
306
350
  unsupported
307
351
  };
308
352
  }
309
- var CLIENT_SYNCERS = {
310
- claude: syncClaude,
311
- "claude-desktop": syncClaudeDesktop,
312
- codex: syncCodex,
313
- cursor: syncCursor,
314
- vscode: syncVsCode,
315
- kiro: syncKiro,
316
- qwen: syncQwen
317
- };
318
353
  function syncPluginsToClient(clientId, plugins) {
319
- const syncer = CLIENT_SYNCERS[clientId];
320
- if (!syncer) {
321
- return {
322
- clientId,
323
- status: "SKIPPED",
324
- projectedDirs: [],
325
- unsupported: ["mcpServers", "skills", "hooks", "agents", "commands"],
326
- error: `No plugin sync implementation for ${clientId}`
327
- };
354
+ const syncMap = {
355
+ claude: syncClaude,
356
+ "claude-desktop": syncClaudeDesktop,
357
+ codex: syncCodex,
358
+ cursor: syncCursor,
359
+ vscode: syncVsCode,
360
+ qwen: syncQwen,
361
+ cline: syncCline,
362
+ kiro: syncKiro
363
+ };
364
+ const syncFn = syncMap[clientId];
365
+ if (!syncFn) {
366
+ return { clientId, status: "SKIPPED", projectedDirs: [], unsupported: [] };
367
+ }
368
+ const targetBaseMap = {
369
+ claude: path2.join(homeDir(), ".claude", "skills"),
370
+ "claude-desktop": "",
371
+ codex: path2.join(homeDir(), ".codex", "skills"),
372
+ cursor: path2.join(homeDir(), ".cursor"),
373
+ vscode: path2.join(homeDir(), ".copilot", "skills"),
374
+ qwen: path2.join(homeDir(), ".qwen", "skills"),
375
+ cline: path2.join(homeDir(), ".config", "cline", "skills"),
376
+ kiro: path2.join(homeDir(), ".kiro", "skills")
377
+ };
378
+ const target = targetBaseMap[clientId];
379
+ if (target) {
380
+ const enabledIds = plugins.filter((p) => p.enabled).map((p) => p.pluginId);
381
+ pruneUnlistedPlugins(target, enabledIds);
328
382
  }
329
- return syncer(plugins);
383
+ return syncFn(plugins);
330
384
  }
331
385
  function copyPluginDir(src, dest, exclude = []) {
386
+ const excludeSet = new Set(exclude);
332
387
  ensureDir(dest);
333
388
  const entries = fs2.readdirSync(src, { withFileTypes: true });
334
389
  for (const entry of entries) {
335
- if (exclude.includes(entry.name))
336
- continue;
337
- if (entry.name.startsWith(".") && entry.name !== ".claude-plugin")
390
+ if (excludeSet.has(entry.name))
338
391
  continue;
339
392
  const srcPath = path2.join(src, entry.name);
340
393
  const destPath = path2.join(dest, entry.name);
341
394
  if (entry.isDirectory()) {
342
395
  copyPluginDir(srcPath, destPath, exclude);
343
- } else {
344
- fs2.mkdirSync(path2.dirname(destPath), { recursive: true });
345
- fs2.copyFileSync(srcPath, destPath);
396
+ } else if (entry.isFile()) {
397
+ try {
398
+ fs2.copyFileSync(srcPath, destPath);
399
+ } catch {}
346
400
  }
347
401
  }
348
402
  }
349
403
  function copyFileOrDir(src, dest) {
350
- ensureDir(path2.dirname(dest));
351
- if (fs2.statSync(src).isDirectory()) {
352
- copyPluginDir(src, dest);
353
- } else {
354
- fs2.copyFileSync(src, dest);
355
- }
404
+ try {
405
+ ensureDir(path2.dirname(dest));
406
+ if (fs2.statSync(src).isDirectory()) {
407
+ copyPluginDir(src, dest);
408
+ } else {
409
+ fs2.copyFileSync(src, dest);
410
+ }
411
+ } catch {}
356
412
  }
357
413
 
358
414
  // src/adapters/claude.ts
@@ -475,17 +531,21 @@ function removeSourceEntries(entries, names) {
475
531
  delete entries[name];
476
532
  }
477
533
  }
478
- function syncDisabledMcpServersArray(raw, managedNames, managedEntries) {
479
- const existingDisabled = (raw.disabledMcpServers ?? []).filter((name) => !managedNames.includes(name));
480
- const disabledManaged = managedEntries.filter((entry) => !entry.enabled).map((entry) => entry.name);
481
- raw.disabledMcpServers = [...existingDisabled, ...disabledManaged];
534
+ function purgeManagedFromDisabledArray(raw, managedNames) {
535
+ const existingDisabled = raw.disabledMcpServers ?? [];
536
+ raw.disabledMcpServers = existingDisabled.filter((name) => !managedNames.includes(name));
537
+ if (Array.isArray(raw.disabledMcpServers) && raw.disabledMcpServers.length === 0) {
538
+ delete raw.disabledMcpServers;
539
+ }
482
540
  }
483
- function syncMcpExcludedArray(raw, managedNames, managedEntries) {
541
+ function purgeManagedFromExcludedArray(raw, managedNames) {
484
542
  const mcp = raw.mcp ?? {};
485
- const existingExcluded = (mcp.excluded ?? []).filter((name) => !managedNames.includes(name));
486
- const disabledManaged = managedEntries.filter((entry) => !entry.enabled).map((entry) => entry.name);
487
- mcp.excluded = [...existingExcluded, ...disabledManaged];
488
- raw.mcp = mcp;
543
+ const existingExcluded = mcp.excluded ?? [];
544
+ mcp.excluded = existingExcluded.filter((name) => !managedNames.includes(name));
545
+ if (Array.isArray(mcp.excluded) && mcp.excluded.length === 0) {
546
+ delete mcp.excluded;
547
+ }
548
+ raw.mcp = Object.keys(mcp).length > 0 ? mcp : undefined;
489
549
  }
490
550
 
491
551
  // src/adapters/claude.ts
@@ -619,7 +679,7 @@ class ClaudeAdapter {
619
679
  topLevelServers[name] = entry;
620
680
  }
621
681
  raw.mcpServers = topLevelServers;
622
- syncDisabledMcpServersArray(raw, managedNames, options.managedEntries);
682
+ purgeManagedFromDisabledArray(raw, managedNames);
623
683
  const scopeByNormalizedPath = new Map;
624
684
  for (const scope of options.projectScopes ?? []) {
625
685
  scopeByNormalizedPath.set(normalizeProjectKey(scope.path), scope.disabledServerNames);
@@ -883,8 +943,7 @@ class CodexAdapter {
883
943
  const mcpServers = doc.mcp_servers ?? {};
884
944
  removeSourceEntries(mcpServers, options.sourceEntriesToRemove);
885
945
  const managedNames = options.managedEntries.map((entry) => entry.name);
886
- const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
887
- enabled: entry.enabled,
946
+ const serverEntries = Object.fromEntries(options.managedEntries.filter((entry) => entry.enabled).map((entry) => [entry.name, {
888
947
  url: entry.url,
889
948
  http_headers: entry.headers
890
949
  }]));
@@ -1162,11 +1221,10 @@ class ClineAdapter {
1162
1221
  };
1163
1222
  removeSourceEntries(servers, options.sourceEntriesToRemove);
1164
1223
  const managedNames = options.managedEntries.map((entry) => entry.name);
1165
- const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
1224
+ const serverEntries = Object.fromEntries(options.managedEntries.filter((entry) => entry.enabled).map((entry) => [entry.name, {
1166
1225
  type: "streamableHttp",
1167
1226
  url: entry.url,
1168
- headers: entry.headers,
1169
- disabled: !entry.enabled
1227
+ headers: entry.headers
1170
1228
  }]));
1171
1229
  for (const name of managedNames) {
1172
1230
  const conflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, servers[name]);
@@ -1286,10 +1344,9 @@ class KiroAdapter {
1286
1344
  };
1287
1345
  removeSourceEntries(servers, options.sourceEntriesToRemove);
1288
1346
  const managedNames = options.managedEntries.map((entry) => entry.name);
1289
- const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
1347
+ const serverEntries = Object.fromEntries(options.managedEntries.filter((entry) => entry.enabled).map((entry) => [entry.name, {
1290
1348
  url: entry.url,
1291
- headers: entry.headers,
1292
- disabled: !entry.enabled
1349
+ headers: entry.headers
1293
1350
  }]));
1294
1351
  for (const name of managedNames) {
1295
1352
  const conflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, servers[name]);
@@ -1431,11 +1488,10 @@ class OpenCodeAdapter {
1431
1488
  };
1432
1489
  removeSourceEntries(mcp, options.sourceEntriesToRemove);
1433
1490
  const managedNames = options.managedEntries.map((entry) => entry.name);
1434
- const serverEntries = Object.fromEntries(options.managedEntries.map((entry) => [entry.name, {
1491
+ const serverEntries = Object.fromEntries(options.managedEntries.filter((entry) => entry.enabled).map((entry) => [entry.name, {
1435
1492
  type: "remote",
1436
1493
  url: entry.url,
1437
- headers: entry.headers,
1438
- enabled: entry.enabled
1494
+ headers: entry.headers
1439
1495
  }]));
1440
1496
  for (const name of managedNames) {
1441
1497
  const conflict = ensureManagedEntryWritable(options.managedIndex, this.id, name, mcp[name]);
@@ -1581,9 +1637,9 @@ class VsCodeAdapter {
1581
1637
  servers
1582
1638
  };
1583
1639
  writeJsonAtomic(configPath, next);
1584
- setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(options.managedEntries.map((entry) => [
1640
+ setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(enabledEntries.map((entry) => [
1585
1641
  entry.name,
1586
- JSON.stringify({ type: "http", url: entry.url, headers: entry.headers, enabled: entry.enabled })
1642
+ JSON.stringify({ type: "http", url: entry.url, headers: entry.headers })
1587
1643
  ])));
1588
1644
  return okResult(this.id, configPath);
1589
1645
  } catch (error) {
@@ -1714,7 +1770,7 @@ class QwenAdapter {
1714
1770
  topLevelServers[name] = entry;
1715
1771
  }
1716
1772
  raw.mcpServers = topLevelServers;
1717
- syncMcpExcludedArray(raw, managedNames, options.managedEntries);
1773
+ purgeManagedFromExcludedArray(raw, managedNames);
1718
1774
  writeJsonAtomic(configPath, raw);
1719
1775
  setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(options.managedEntries.map((entry) => [
1720
1776
  entry.name,
@@ -1795,33 +1851,28 @@ function buildProjectScopes(config) {
1795
1851
  });
1796
1852
  }
1797
1853
  function buildManagedEntries(config, gatewayUrl, localToken) {
1798
- const entries = Object.entries(config.servers);
1799
- return entries.map(([name, spec]) => ({
1854
+ return Object.entries(config.servers).map(([name, spec]) => ({
1800
1855
  name: `${name} (mcpx)`,
1801
1856
  url: `${gatewayUrl}?upstream=${encodeURIComponent(name)}`,
1802
- headers: {
1803
- Authorization: `Bearer ${localToken}`
1804
- },
1857
+ headers: { Authorization: `Bearer ${localToken}` },
1805
1858
  enabled: isServerEnabled(spec)
1806
1859
  }));
1807
1860
  }
1808
1861
  function isClientInternalServerCommand(candidate) {
1809
1862
  return candidate.spec.transport === "stdio" && /\.app\/Contents\//.test(candidate.spec.command ?? "");
1810
1863
  }
1811
- function syncAllClients(config, secrets, targetClients) {
1864
+ function getFilteredAdapters(targetClients) {
1812
1865
  const adapters = getAdapters();
1813
- const managedIndexPath = getManagedIndexPath();
1814
- const managedIndex = loadManagedIndex(managedIndexPath);
1815
- const filteredAdapters = targetClients && targetClients.length > 0 ? adapters.filter((adapter) => targetClients.includes(adapter.id)) : adapters;
1816
- const imports = {
1817
- imported: [],
1818
- duplicates: [],
1819
- skipped: [],
1820
- conflicts: [],
1821
- errors: []
1822
- };
1866
+ if (targetClients && targetClients.length > 0) {
1867
+ return adapters.filter((a) => targetClients.includes(a.id));
1868
+ }
1869
+ return adapters;
1870
+ }
1871
+ function runImportPhase(adapters, config, managedIndex) {
1872
+ const imports = { imported: [], duplicates: [], skipped: [], conflicts: [], errors: [] };
1823
1873
  const sourceEntriesToRemove = new Map;
1824
- for (const adapter of filteredAdapters) {
1874
+ const phaseErrors = [];
1875
+ for (const adapter of adapters) {
1825
1876
  try {
1826
1877
  const scan = adapter.scanForImports(config, managedIndex);
1827
1878
  imports.skipped.push(...scan.skipped);
@@ -1848,7 +1899,7 @@ function syncAllClients(config, secrets, targetClients) {
1848
1899
  if (!sourceEntriesToRemove.has(candidate.clientId)) {
1849
1900
  sourceEntriesToRemove.set(candidate.clientId, new Set);
1850
1901
  }
1851
- sourceEntriesToRemove.get(candidate.clientId)?.add(candidate.sourceEntryName);
1902
+ sourceEntriesToRemove.get(candidate.clientId).add(candidate.sourceEntryName);
1852
1903
  continue;
1853
1904
  }
1854
1905
  if (serverSpecsEqual(existing, candidate.spec)) {
@@ -1861,7 +1912,7 @@ function syncAllClients(config, secrets, targetClients) {
1861
1912
  if (!sourceEntriesToRemove.has(candidate.clientId)) {
1862
1913
  sourceEntriesToRemove.set(candidate.clientId, new Set);
1863
1914
  }
1864
- sourceEntriesToRemove.get(candidate.clientId)?.add(candidate.sourceEntryName);
1915
+ sourceEntriesToRemove.get(candidate.clientId).add(candidate.sourceEntryName);
1865
1916
  continue;
1866
1917
  }
1867
1918
  imports.conflicts.push({
@@ -1878,13 +1929,111 @@ function syncAllClients(config, secrets, targetClients) {
1878
1929
  configPath: adapter.detectConfigPath() ?? undefined,
1879
1930
  message: error instanceof Error ? error.message : String(error)
1880
1931
  });
1932
+ phaseErrors.push({
1933
+ clientId: adapter.id,
1934
+ phase: "import",
1935
+ message: error instanceof Error ? error.message : String(error)
1936
+ });
1937
+ }
1938
+ }
1939
+ return { imports, sourceEntriesToRemove, phaseErrors };
1940
+ }
1941
+ function runSkillsPhase(adapters, skills) {
1942
+ const phaseErrors = [];
1943
+ for (const adapter of adapters) {
1944
+ if (adapter.syncSkills) {
1945
+ try {
1946
+ adapter.syncSkills(skills);
1947
+ } catch (error) {
1948
+ phaseErrors.push({
1949
+ clientId: adapter.id,
1950
+ phase: "skills",
1951
+ message: error instanceof Error ? error.message : String(error)
1952
+ });
1953
+ }
1954
+ }
1955
+ }
1956
+ return phaseErrors;
1957
+ }
1958
+ function runPluginsPhase(adapters, pluginInputs) {
1959
+ const pluginResults = [];
1960
+ const phaseErrors = [];
1961
+ for (const adapter of adapters) {
1962
+ if (adapter.syncPlugins) {
1963
+ try {
1964
+ const result = adapter.syncPlugins(pluginInputs);
1965
+ if (result)
1966
+ pluginResults.push(result);
1967
+ } catch (error) {
1968
+ phaseErrors.push({
1969
+ clientId: adapter.id,
1970
+ phase: "plugins",
1971
+ message: error instanceof Error ? error.message : String(error)
1972
+ });
1973
+ }
1974
+ }
1975
+ }
1976
+ return { pluginResults, phaseErrors };
1977
+ }
1978
+ function runGatewayPhase(adapters, config, managedEntries, managedIndex, managedIndexPath, sourceEntriesToRemove, projectScopes) {
1979
+ const results = [];
1980
+ const phaseErrors = [];
1981
+ for (const adapter of adapters) {
1982
+ if (!adapter.supportsHttp()) {
1983
+ results.push({
1984
+ clientId: adapter.id,
1985
+ status: "UNSUPPORTED_HTTP",
1986
+ message: "Client adapter does not support MCP over HTTP."
1987
+ });
1988
+ continue;
1989
+ }
1990
+ try {
1991
+ const result = adapter.syncGateway(config, {
1992
+ managedEntries,
1993
+ managedIndex,
1994
+ managedIndexPath,
1995
+ sourceEntriesToRemove: Array.from(sourceEntriesToRemove.get(adapter.id) ?? []),
1996
+ projectScopes
1997
+ });
1998
+ results.push(result);
1999
+ } catch (error) {
2000
+ results.push({
2001
+ clientId: adapter.id,
2002
+ status: "ERROR",
2003
+ message: error instanceof Error ? error.message : String(error)
2004
+ });
2005
+ phaseErrors.push({
2006
+ clientId: adapter.id,
2007
+ phase: "gateway",
2008
+ message: error instanceof Error ? error.message : String(error)
2009
+ });
1881
2010
  }
1882
2011
  }
2012
+ return { results, phaseErrors };
2013
+ }
2014
+ function syncAllClients(config, secrets, options) {
2015
+ const opts = Array.isArray(options) ? { targetClients: options, importScan: false } : { importScan: false, ...options };
2016
+ const managedIndexPath = getManagedIndexPath();
2017
+ const managedIndex = loadManagedIndex(managedIndexPath);
2018
+ const adapters = getFilteredAdapters(opts.targetClients);
2019
+ const allPhaseErrors = [];
2020
+ let imports = { imported: [], duplicates: [], skipped: [], conflicts: [], errors: [] };
2021
+ let sourceEntriesToRemove = new Map;
2022
+ if (opts.importScan) {
2023
+ const importResult = runImportPhase(adapters, config, managedIndex);
2024
+ imports = importResult.imports;
2025
+ sourceEntriesToRemove = importResult.sourceEntriesToRemove;
2026
+ allPhaseErrors.push(...importResult.phaseErrors ?? []);
2027
+ }
1883
2028
  const localToken = ensureGatewayToken(config, secrets);
1884
2029
  const gatewayUrl = getGatewayUrl(config);
1885
2030
  const managedEntries = buildManagedEntries(config, gatewayUrl, localToken);
2031
+ const gatewayResult = runGatewayPhase(adapters, config, managedEntries, managedIndex, managedIndexPath, sourceEntriesToRemove, buildProjectScopes(config));
2032
+ const results = gatewayResult.results;
2033
+ allPhaseErrors.push(...gatewayResult.phaseErrors ?? []);
1886
2034
  const skills = listSkills();
1887
- const results = [];
2035
+ const skillsErrors = runSkillsPhase(adapters, skills);
2036
+ allPhaseErrors.push(...skillsErrors ?? []);
1888
2037
  const pluginInputs = Object.values(config.plugins ?? {}).map((p) => ({
1889
2038
  pluginId: p.id,
1890
2039
  pluginName: p.name,
@@ -1898,51 +2047,36 @@ function syncAllClients(config, secrets, targetClients) {
1898
2047
  agents: p.discovered.agents,
1899
2048
  hooks: p.discovered.hooks
1900
2049
  }));
1901
- for (const adapter of filteredAdapters) {
1902
- if (adapter.syncSkills) {
1903
- try {
1904
- adapter.syncSkills(skills);
1905
- } catch (error) {}
1906
- }
1907
- if (adapter.syncPlugins && pluginInputs.length > 0) {
1908
- try {
1909
- adapter.syncPlugins(pluginInputs);
1910
- } catch (error) {}
1911
- }
1912
- if (!adapter.supportsHttp()) {
1913
- results.push({
1914
- clientId: adapter.id,
1915
- status: "UNSUPPORTED_HTTP",
1916
- message: "Client adapter does not support MCP over HTTP."
1917
- });
1918
- continue;
1919
- }
1920
- const result = adapter.syncGateway(config, {
1921
- managedEntries,
1922
- managedIndex,
1923
- managedIndexPath,
1924
- sourceEntriesToRemove: Array.from(sourceEntriesToRemove.get(adapter.id) ?? []),
1925
- projectScopes: buildProjectScopes(config)
1926
- });
1927
- results.push(result);
1928
- }
2050
+ const pluginResult = runPluginsPhase(adapters, pluginInputs);
2051
+ const pluginResults = pluginResult.pluginResults;
2052
+ allPhaseErrors.push(...pluginResult.phaseErrors ?? []);
2053
+ saveManagedIndex(managedIndex, managedIndexPath);
2054
+ const clientStates = {};
1929
2055
  for (const result of results) {
1930
- config.clients[result.clientId] = {
2056
+ clientStates[result.clientId] = {
1931
2057
  status: result.status,
1932
2058
  configPath: result.configPath,
1933
2059
  message: result.message,
1934
2060
  lastSyncAt: new Date().toISOString()
1935
2061
  };
1936
2062
  }
1937
- saveManagedIndex(managedIndex, managedIndexPath);
1938
- saveConfig(config);
1939
- const hasErrors = results.some((result) => result.status === "ERROR") || imports.conflicts.length > 0 || imports.errors.length > 0;
2063
+ const hasErrors = results.some((r) => r.status === "ERROR") || imports.conflicts.length > 0 || imports.errors.length > 0;
1940
2064
  return {
1941
2065
  gatewayUrl,
1942
2066
  imports,
1943
2067
  results,
1944
- hasErrors
2068
+ hasErrors,
2069
+ clientStates,
2070
+ phaseErrors: allPhaseErrors.length > 0 ? allPhaseErrors : undefined,
2071
+ pluginResults: pluginResults.length > 0 ? pluginResults : undefined
1945
2072
  };
1946
2073
  }
2074
+ function persistSyncState(summary, config) {
2075
+ if (!summary.clientStates)
2076
+ return;
2077
+ for (const [clientId, state] of Object.entries(summary.clientStates)) {
2078
+ config.clients[clientId] = state;
2079
+ }
2080
+ }
1947
2081
 
1948
- export { listSkills, getSkill, saveSkill, deleteSkill, prunePluginProjections, pruneAllPluginProjections, syncPluginsToClient, getAdapters, loadManagedIndex, getGatewayUrl, syncAllClients };
2082
+ export { listSkills, getSkill, saveSkill, deleteSkill, prunePluginProjections, pruneAllPluginProjections, syncPluginsToClient, getAdapters, loadManagedIndex, getGatewayUrl, syncAllClients, persistSyncState };