nextclaw 0.12.2 → 0.12.3

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 (30) hide show
  1. package/dist/cli/index.js +395 -85
  2. package/package.json +9 -7
  3. package/ui-dist/assets/{ChannelsList-B-6m17e8.js → ChannelsList-DhvjpZcs.js} +1 -1
  4. package/ui-dist/assets/ChatPage-B8VBaMQm.js +38 -0
  5. package/ui-dist/assets/{DocBrowser-B3Mn7Pr1.js → DocBrowser-LpzGe8An.js} +1 -1
  6. package/ui-dist/assets/{LogoBadge-DunBd0Md.js → LogoBadge-Be4lktJN.js} +1 -1
  7. package/ui-dist/assets/{MarketplacePage-GfyaFoSx.js → MarketplacePage-Cx9AI3_h.js} +2 -2
  8. package/ui-dist/assets/{ModelConfig-BQHpKPOV.js → ModelConfig-DuImUHIX.js} +1 -1
  9. package/ui-dist/assets/{ProvidersList-a_cTcyUa.js → ProvidersList-Ccleg25k.js} +1 -1
  10. package/ui-dist/assets/{RuntimeConfig-ocF1z2ge.js → RuntimeConfig-C6iqpJR_.js} +1 -1
  11. package/ui-dist/assets/{SearchConfig-CuVwE1pL.js → SearchConfig-Dvp1TAXu.js} +1 -1
  12. package/ui-dist/assets/{SecretsConfig-BNNNBC9B.js → SecretsConfig-D5Ymlvt9.js} +2 -2
  13. package/ui-dist/assets/{SessionsConfig-NLh9dU5C.js → SessionsConfig-CIA_jA1P.js} +2 -2
  14. package/ui-dist/assets/{chat-message-D6cd8Vub.js → chat-message-B60Fh9kI.js} +1 -1
  15. package/ui-dist/assets/{index-DhtQmFvo.js → index-BiPDnzv0.js} +3 -3
  16. package/ui-dist/assets/index-C8GsgIUn.css +1 -0
  17. package/ui-dist/assets/{index-DUrka-gK.js → index-CPDASUXh.js} +1 -1
  18. package/ui-dist/assets/{label-UZ6h2eks.js → label-D4fGx6Wb.js} +1 -1
  19. package/ui-dist/assets/{page-layout-C4opEYpL.js → page-layout-twy8gmBE.js} +1 -1
  20. package/ui-dist/assets/{popover-CBwaT0q5.js → popover-DYbYpt1j.js} +1 -1
  21. package/ui-dist/assets/{security-config-C2x64Ori.js → security-config-BcIZ4rpb.js} +1 -1
  22. package/ui-dist/assets/skeleton-DypBy7jp.js +1 -0
  23. package/ui-dist/assets/{switch-CP4z7NJz.js → switch-DqA6r5XR.js} +1 -1
  24. package/ui-dist/assets/{tabs-custom-yiwRX4b7.js → tabs-custom-C6enKKs1.js} +1 -1
  25. package/ui-dist/assets/{useConfirmDialog-DOu_1Piu.js → useConfirmDialog-CHBf5Of7.js} +2 -2
  26. package/ui-dist/assets/{vendor-CAtZFO3x.js → vendor-DKBNiC31.js} +65 -70
  27. package/ui-dist/index.html +3 -3
  28. package/ui-dist/assets/ChatPage-BZ6B6_pW.js +0 -38
  29. package/ui-dist/assets/index-C98Y7H0s.css +0 -1
  30. package/ui-dist/assets/skeleton-C56NStvY.js +0 -1
package/dist/cli/index.js CHANGED
@@ -6,8 +6,8 @@ import { APP_NAME as APP_NAME5, APP_TAGLINE } from "@nextclaw/core";
6
6
 
7
7
  // src/cli/runtime.ts
8
8
  import {
9
- loadConfig as loadConfig8,
10
- saveConfig as saveConfig6,
9
+ loadConfig as loadConfig9,
10
+ saveConfig as saveConfig7,
11
11
  getConfigPath as getConfigPath4,
12
12
  getDataDir as getDataDir8,
13
13
  ConfigSchema as ConfigSchema2,
@@ -2116,15 +2116,230 @@ var ConfigCommands = class {
2116
2116
  }
2117
2117
  };
2118
2118
 
2119
+ // src/cli/commands/mcp.ts
2120
+ import { loadConfig as loadConfig4, saveConfig as saveConfig3 } from "@nextclaw/core";
2121
+ import { McpDoctorService, McpRegistryService, normalizeMcpServerName } from "@nextclaw/mcp";
2122
+ function normalizeOptionalString(value) {
2123
+ if (typeof value !== "string") {
2124
+ return void 0;
2125
+ }
2126
+ const trimmed = value.trim();
2127
+ return trimmed || void 0;
2128
+ }
2129
+ function parsePairs(values, label) {
2130
+ const output = {};
2131
+ for (const raw of values ?? []) {
2132
+ const index = raw.indexOf("=");
2133
+ if (index <= 0) {
2134
+ throw new Error(`Invalid ${label} entry: ${raw}. Expected key=value.`);
2135
+ }
2136
+ const key = raw.slice(0, index).trim();
2137
+ const value = raw.slice(index + 1);
2138
+ if (!key) {
2139
+ throw new Error(`Invalid ${label} entry: ${raw}. Expected key=value.`);
2140
+ }
2141
+ output[key] = value;
2142
+ }
2143
+ return output;
2144
+ }
2145
+ function parseTimeoutMs(value) {
2146
+ if (value === void 0 || value === null || value === "") {
2147
+ return void 0;
2148
+ }
2149
+ const parsed = Number(value);
2150
+ if (!Number.isFinite(parsed) || parsed <= 0) {
2151
+ throw new Error(`Invalid timeout: ${String(value)}`);
2152
+ }
2153
+ return Math.trunc(parsed);
2154
+ }
2155
+ function buildMcpServerDefinition(command, opts) {
2156
+ const transport = (normalizeOptionalString(opts.transport) ?? "stdio").toLowerCase();
2157
+ const disabled = Boolean(opts.disabled);
2158
+ const allAgents = Boolean(opts.allAgents);
2159
+ const explicitAgents = Array.from(
2160
+ new Set(
2161
+ (opts.agent ?? []).map((agentId) => normalizeOptionalString(agentId)).filter((agentId) => Boolean(agentId))
2162
+ )
2163
+ );
2164
+ if (transport === "stdio") {
2165
+ if (command.length === 0) {
2166
+ throw new Error("stdio transport requires a command after --");
2167
+ }
2168
+ return {
2169
+ enabled: !disabled,
2170
+ transport: {
2171
+ type: "stdio",
2172
+ command: command[0],
2173
+ args: command.slice(1),
2174
+ cwd: normalizeOptionalString(opts.cwd),
2175
+ env: parsePairs(opts.env, "env"),
2176
+ stderr: normalizeOptionalString(opts.stderr) ?? "pipe"
2177
+ },
2178
+ scope: {
2179
+ allAgents,
2180
+ agents: explicitAgents
2181
+ },
2182
+ policy: {
2183
+ trust: "explicit",
2184
+ start: "eager"
2185
+ }
2186
+ };
2187
+ }
2188
+ const url = normalizeOptionalString(opts.url);
2189
+ if (!url) {
2190
+ throw new Error(`${transport} transport requires --url`);
2191
+ }
2192
+ const timeoutMs = parseTimeoutMs(opts.timeoutMs);
2193
+ const shared = {
2194
+ enabled: !disabled,
2195
+ scope: {
2196
+ allAgents,
2197
+ agents: explicitAgents
2198
+ },
2199
+ policy: {
2200
+ trust: "explicit",
2201
+ start: "eager"
2202
+ }
2203
+ };
2204
+ if (transport === "http") {
2205
+ return {
2206
+ ...shared,
2207
+ transport: {
2208
+ type: "http",
2209
+ url,
2210
+ headers: parsePairs(opts.header, "header"),
2211
+ timeoutMs: timeoutMs ?? 15e3,
2212
+ verifyTls: !opts.insecure
2213
+ }
2214
+ };
2215
+ }
2216
+ if (transport === "sse") {
2217
+ return {
2218
+ ...shared,
2219
+ transport: {
2220
+ type: "sse",
2221
+ url,
2222
+ headers: parsePairs(opts.header, "header"),
2223
+ timeoutMs: timeoutMs ?? 15e3,
2224
+ verifyTls: !opts.insecure,
2225
+ reconnect: {
2226
+ enabled: true,
2227
+ initialDelayMs: 1e3,
2228
+ maxDelayMs: 3e4
2229
+ }
2230
+ }
2231
+ };
2232
+ }
2233
+ throw new Error(`Unsupported MCP transport: ${transport}`);
2234
+ }
2235
+ var McpCommands = class {
2236
+ mcpList(opts = {}) {
2237
+ const registry = new McpRegistryService({
2238
+ getConfig: () => loadConfig4()
2239
+ });
2240
+ const servers = registry.listServers().map((server) => ({
2241
+ name: server.name,
2242
+ enabled: server.definition.enabled,
2243
+ transport: server.definition.transport.type,
2244
+ scope: server.definition.scope
2245
+ }));
2246
+ if (opts.json) {
2247
+ console.log(JSON.stringify({ servers }, null, 2));
2248
+ return;
2249
+ }
2250
+ if (servers.length === 0) {
2251
+ console.log("No MCP servers configured.");
2252
+ return;
2253
+ }
2254
+ for (const server of servers) {
2255
+ const scope = server.scope.allAgents ? "all-agents" : server.scope.agents.length > 0 ? `agents=${server.scope.agents.join(",")}` : "default-agent";
2256
+ console.log(`${server.enabled ? "ENABLED " : "DISABLED"} ${server.name} (${server.transport}, ${scope})`);
2257
+ }
2258
+ }
2259
+ async mcpAdd(name, command, opts) {
2260
+ const normalizedName = normalizeMcpServerName(name);
2261
+ const config2 = loadConfig4();
2262
+ if (config2.mcp.servers[normalizedName]) {
2263
+ reportUserInputIssue(`MCP server already exists: ${normalizedName}. Use 'mcp list' or remove it first.`);
2264
+ return;
2265
+ }
2266
+ config2.mcp.servers[normalizedName] = buildMcpServerDefinition(command, opts);
2267
+ saveConfig3(config2);
2268
+ console.log(`Added MCP server ${normalizedName}.`);
2269
+ }
2270
+ async mcpRemove(name) {
2271
+ const normalizedName = normalizeMcpServerName(name);
2272
+ const config2 = loadConfig4();
2273
+ if (!config2.mcp.servers[normalizedName]) {
2274
+ reportUserInputIssue(`Unknown MCP server: ${normalizedName}`);
2275
+ return;
2276
+ }
2277
+ delete config2.mcp.servers[normalizedName];
2278
+ saveConfig3(config2);
2279
+ console.log(`Removed MCP server ${normalizedName}.`);
2280
+ }
2281
+ async mcpEnable(name) {
2282
+ await this.toggleEnabled(name, true);
2283
+ }
2284
+ async mcpDisable(name) {
2285
+ await this.toggleEnabled(name, false);
2286
+ }
2287
+ async mcpDoctor(name, opts = {}) {
2288
+ const registry = new McpRegistryService({
2289
+ getConfig: () => loadConfig4()
2290
+ });
2291
+ const doctor = new McpDoctorService({
2292
+ getConfig: () => loadConfig4(),
2293
+ registryService: registry
2294
+ });
2295
+ try {
2296
+ const reports = await doctor.inspect(name);
2297
+ if (opts.json) {
2298
+ console.log(JSON.stringify({ reports }, null, 2));
2299
+ return;
2300
+ }
2301
+ if (reports.length === 0) {
2302
+ console.log("No MCP servers matched.");
2303
+ return;
2304
+ }
2305
+ for (const report of reports) {
2306
+ const status = report.error ? "ERROR" : "OK";
2307
+ const detail = report.error ? ` (${report.error})` : "";
2308
+ console.log(
2309
+ `[${status}] ${report.name} enabled=${report.enabled} transport=${report.transport} tools=${report.toolCount}${detail}`
2310
+ );
2311
+ }
2312
+ } finally {
2313
+ await registry.close();
2314
+ }
2315
+ }
2316
+ async toggleEnabled(name, enabled) {
2317
+ const normalizedName = normalizeMcpServerName(name);
2318
+ const config2 = loadConfig4();
2319
+ const current = config2.mcp.servers[normalizedName];
2320
+ if (!current) {
2321
+ reportUserInputIssue(`Unknown MCP server: ${normalizedName}`);
2322
+ return;
2323
+ }
2324
+ current.enabled = enabled;
2325
+ saveConfig3(config2);
2326
+ console.log(`${enabled ? "Enabled" : "Disabled"} MCP server ${normalizedName}.`);
2327
+ }
2328
+ };
2329
+ function reportUserInputIssue(message) {
2330
+ console.error(message);
2331
+ process.exitCode = 1;
2332
+ }
2333
+
2119
2334
  // src/cli/commands/secrets.ts
2120
2335
  import { readFileSync as readFileSync5 } from "fs";
2121
2336
  import {
2122
2337
  buildReloadPlan as buildReloadPlan2,
2123
2338
  diffConfigPaths as diffConfigPaths2,
2124
2339
  getConfigPath,
2125
- loadConfig as loadConfig4,
2340
+ loadConfig as loadConfig5,
2126
2341
  resolveConfigSecrets,
2127
- saveConfig as saveConfig3
2342
+ saveConfig as saveConfig4
2128
2343
  } from "@nextclaw/core";
2129
2344
  var SECRET_SOURCES = ["env", "file", "exec"];
2130
2345
  function normalizeSecretSource(value) {
@@ -2134,14 +2349,14 @@ function normalizeSecretSource(value) {
2134
2349
  const normalized = value.trim().toLowerCase();
2135
2350
  return SECRET_SOURCES.includes(normalized) ? normalized : null;
2136
2351
  }
2137
- function normalizeOptionalString(value) {
2352
+ function normalizeOptionalString2(value) {
2138
2353
  if (typeof value !== "string") {
2139
2354
  return void 0;
2140
2355
  }
2141
2356
  const trimmed = value.trim();
2142
2357
  return trimmed || void 0;
2143
2358
  }
2144
- function parseTimeoutMs(value) {
2359
+ function parseTimeoutMs2(value) {
2145
2360
  if (value === void 0 || value === null || value === "") {
2146
2361
  return void 0;
2147
2362
  }
@@ -2152,11 +2367,11 @@ function parseTimeoutMs(value) {
2152
2367
  return Math.trunc(parsed);
2153
2368
  }
2154
2369
  function inferProviderAlias(config2, ref) {
2155
- const explicit = normalizeOptionalString(ref.provider);
2370
+ const explicit = normalizeOptionalString2(ref.provider);
2156
2371
  if (explicit) {
2157
2372
  return explicit;
2158
2373
  }
2159
- const defaultAlias = normalizeOptionalString(config2.secrets.defaults[ref.source]);
2374
+ const defaultAlias = normalizeOptionalString2(config2.secrets.defaults[ref.source]);
2160
2375
  if (defaultAlias) {
2161
2376
  return defaultAlias;
2162
2377
  }
@@ -2180,8 +2395,8 @@ function parseRefsPatch(raw) {
2180
2395
  throw new Error(`invalid ref for ${path2}`);
2181
2396
  }
2182
2397
  const source = normalizeSecretSource(value.source);
2183
- const id = normalizeOptionalString(value.id);
2184
- const provider = normalizeOptionalString(value.provider);
2398
+ const id = normalizeOptionalString2(value.id);
2399
+ const provider = normalizeOptionalString2(value.provider);
2185
2400
  if (!source || !id) {
2186
2401
  throw new Error(`invalid ref for ${path2}: source/id is required`);
2187
2402
  }
@@ -2218,7 +2433,7 @@ var SecretsCommands = class {
2218
2433
  this.deps = deps;
2219
2434
  }
2220
2435
  secretsAudit(opts = {}) {
2221
- const config2 = loadConfig4();
2436
+ const config2 = loadConfig5();
2222
2437
  const configPath = getConfigPath();
2223
2438
  const refs = config2.secrets.refs;
2224
2439
  const items = [];
@@ -2282,11 +2497,11 @@ var SecretsCommands = class {
2282
2497
  }
2283
2498
  }
2284
2499
  async secretsConfigure(opts) {
2285
- const alias = normalizeOptionalString(opts.provider);
2500
+ const alias = normalizeOptionalString2(opts.provider);
2286
2501
  if (!alias) {
2287
2502
  throw new Error("provider alias is required");
2288
2503
  }
2289
- const prevConfig = loadConfig4();
2504
+ const prevConfig = loadConfig5();
2290
2505
  const nextConfig = structuredClone(prevConfig);
2291
2506
  const remove = Boolean(opts.remove);
2292
2507
  if (remove) {
@@ -2304,10 +2519,10 @@ var SecretsCommands = class {
2304
2519
  if (source === "env") {
2305
2520
  nextConfig.secrets.providers[alias] = {
2306
2521
  source,
2307
- ...normalizeOptionalString(opts.prefix) ? { prefix: normalizeOptionalString(opts.prefix) } : {}
2522
+ ...normalizeOptionalString2(opts.prefix) ? { prefix: normalizeOptionalString2(opts.prefix) } : {}
2308
2523
  };
2309
2524
  } else if (source === "file") {
2310
- const path2 = normalizeOptionalString(opts.path);
2525
+ const path2 = normalizeOptionalString2(opts.path);
2311
2526
  if (!path2) {
2312
2527
  throw new Error("file source requires --path");
2313
2528
  }
@@ -2317,7 +2532,7 @@ var SecretsCommands = class {
2317
2532
  format: "json"
2318
2533
  };
2319
2534
  } else {
2320
- const command = normalizeOptionalString(opts.command);
2535
+ const command = normalizeOptionalString2(opts.command);
2321
2536
  if (!command) {
2322
2537
  throw new Error("exec source requires --command");
2323
2538
  }
@@ -2325,8 +2540,8 @@ var SecretsCommands = class {
2325
2540
  source,
2326
2541
  command,
2327
2542
  args: Array.isArray(opts.arg) ? opts.arg : [],
2328
- ...normalizeOptionalString(opts.cwd) ? { cwd: normalizeOptionalString(opts.cwd) } : {},
2329
- timeoutMs: parseTimeoutMs(opts.timeoutMs) ?? 5e3
2543
+ ...normalizeOptionalString2(opts.cwd) ? { cwd: normalizeOptionalString2(opts.cwd) } : {},
2544
+ timeoutMs: parseTimeoutMs2(opts.timeoutMs) ?? 5e3
2330
2545
  };
2331
2546
  }
2332
2547
  if (opts.setDefault) {
@@ -2334,7 +2549,7 @@ var SecretsCommands = class {
2334
2549
  }
2335
2550
  }
2336
2551
  resolveConfigSecrets(nextConfig, { configPath: getConfigPath() });
2337
- saveConfig3(nextConfig);
2552
+ saveConfig4(nextConfig);
2338
2553
  await this.requestRestartForConfigDiff({
2339
2554
  prevConfig,
2340
2555
  nextConfig,
@@ -2348,7 +2563,7 @@ var SecretsCommands = class {
2348
2563
  console.log(`Secrets provider ${remove ? "removed" : "configured"}: ${alias}`);
2349
2564
  }
2350
2565
  async secretsApply(opts) {
2351
- const prevConfig = loadConfig4();
2566
+ const prevConfig = loadConfig5();
2352
2567
  const nextConfig = structuredClone(prevConfig);
2353
2568
  if (opts.enable && opts.disable) {
2354
2569
  throw new Error("cannot set --enable and --disable at the same time");
@@ -2384,11 +2599,11 @@ var SecretsCommands = class {
2384
2599
  delete nextConfig.secrets.refs[path2];
2385
2600
  } else {
2386
2601
  const source = normalizeSecretSource(opts.source);
2387
- const id = normalizeOptionalString(opts.id);
2602
+ const id = normalizeOptionalString2(opts.id);
2388
2603
  if (!source || !id) {
2389
2604
  throw new Error("apply single ref requires --source and --id");
2390
2605
  }
2391
- const provider = normalizeOptionalString(opts.provider);
2606
+ const provider = normalizeOptionalString2(opts.provider);
2392
2607
  nextConfig.secrets.refs[path2] = {
2393
2608
  source,
2394
2609
  id,
@@ -2399,7 +2614,7 @@ var SecretsCommands = class {
2399
2614
  throw new Error("--remove requires --path");
2400
2615
  }
2401
2616
  resolveConfigSecrets(nextConfig, { configPath: getConfigPath() });
2402
- saveConfig3(nextConfig);
2617
+ saveConfig4(nextConfig);
2403
2618
  await this.requestRestartForConfigDiff({
2404
2619
  prevConfig,
2405
2620
  nextConfig,
@@ -2413,10 +2628,10 @@ var SecretsCommands = class {
2413
2628
  console.log("Secrets applied.");
2414
2629
  }
2415
2630
  async secretsReload(opts = {}) {
2416
- const config2 = loadConfig4();
2631
+ const config2 = loadConfig5();
2417
2632
  const configPath = getConfigPath();
2418
2633
  resolveConfigSecrets(config2, { configPath });
2419
- saveConfig3(config2);
2634
+ saveConfig4(config2);
2420
2635
  if (opts.json) {
2421
2636
  console.log(JSON.stringify({ ok: true, message: "secrets reload signal emitted" }, null, 2));
2422
2637
  return;
@@ -2441,7 +2656,7 @@ var SecretsCommands = class {
2441
2656
 
2442
2657
  // src/cli/commands/channels.ts
2443
2658
  import { spawnSync as spawnSync2 } from "child_process";
2444
- import { getWorkspacePath as getWorkspacePath3, loadConfig as loadConfig5, saveConfig as saveConfig4 } from "@nextclaw/core";
2659
+ import { getWorkspacePath as getWorkspacePath3, loadConfig as loadConfig6, saveConfig as saveConfig5 } from "@nextclaw/core";
2445
2660
  import { BUILTIN_CHANNEL_PLUGIN_IDS, builtinProviderIds as builtinProviderIds2 } from "@nextclaw/runtime";
2446
2661
  import { buildPluginStatusReport as buildPluginStatusReport3, enablePluginInConfig as enablePluginInConfig2, getPluginChannelBindings } from "@nextclaw/openclaw-compat";
2447
2662
  var CHANNEL_LABELS = {
@@ -2462,7 +2677,7 @@ var ChannelCommands = class {
2462
2677
  this.deps = deps;
2463
2678
  }
2464
2679
  channelsStatus() {
2465
- const config2 = loadConfig5();
2680
+ const config2 = loadConfig6();
2466
2681
  console.log("Channel Status");
2467
2682
  const channelConfig = config2.channels;
2468
2683
  for (const channelId of BUILTIN_CHANNEL_PLUGIN_IDS) {
@@ -2501,7 +2716,7 @@ var ChannelCommands = class {
2501
2716
  console.error("--channel is required");
2502
2717
  process.exit(1);
2503
2718
  }
2504
- const config2 = loadConfig5();
2719
+ const config2 = loadConfig6();
2505
2720
  const workspaceDir = getWorkspacePath3(config2.agents.defaults.workspace);
2506
2721
  const pluginRegistry = loadPluginRegistry(config2, workspaceDir);
2507
2722
  const bindings = getPluginChannelBindings(pluginRegistry);
@@ -2544,7 +2759,7 @@ var ChannelCommands = class {
2544
2759
  }
2545
2760
  let next = mergePluginConfigView(config2, nextView, bindings);
2546
2761
  next = enablePluginInConfig2(next, binding.pluginId);
2547
- saveConfig4(next);
2762
+ saveConfig5(next);
2548
2763
  console.log(`Configured channel "${binding.channelId}" via plugin "${binding.pluginId}".`);
2549
2764
  await this.deps.requestRestart({
2550
2765
  reason: `channel configured via plugin: ${binding.pluginId}`,
@@ -2639,7 +2854,7 @@ import {
2639
2854
  getDataDir as getDataDir4,
2640
2855
  getWorkspacePath as getWorkspacePath4,
2641
2856
  hasSecretRef,
2642
- loadConfig as loadConfig6
2857
+ loadConfig as loadConfig7
2643
2858
  } from "@nextclaw/core";
2644
2859
  import { listBuiltinProviders } from "@nextclaw/runtime";
2645
2860
  var DiagnosticsCommands = class {
@@ -2801,7 +3016,7 @@ var DiagnosticsCommands = class {
2801
3016
  }
2802
3017
  async collectRuntimeStatus(params) {
2803
3018
  const configPath = getConfigPath2();
2804
- const config2 = loadConfig6();
3019
+ const config2 = loadConfig7();
2805
3020
  const workspacePath = getWorkspacePath4(config2.agents.defaults.workspace);
2806
3021
  const serviceStatePath = resolve8(getDataDir4(), "run", "service.json");
2807
3022
  const fixActions = [];
@@ -3306,6 +3521,9 @@ var ConfigReloader = class {
3306
3521
  setReloadPlugins(callback) {
3307
3522
  this.options.reloadPlugins = callback;
3308
3523
  }
3524
+ setReloadMcp(callback) {
3525
+ this.options.reloadMcp = callback;
3526
+ }
3309
3527
  async applyReloadPlan(nextConfig) {
3310
3528
  const changedPaths = diffConfigPaths3(this.currentConfig, nextConfig);
3311
3529
  if (!changedPaths.length) {
@@ -3322,6 +3540,13 @@ var ConfigReloader = class {
3322
3540
  });
3323
3541
  console.log("Config reload: plugins reloaded.");
3324
3542
  }
3543
+ if (plan.reloadMcp) {
3544
+ await this.reloadMcp({
3545
+ config: nextConfig,
3546
+ changedPaths
3547
+ });
3548
+ console.log("Config reload: MCP servers reloaded.");
3549
+ }
3325
3550
  if (plan.restartChannels || reloadPluginsResult?.restartChannels) {
3326
3551
  await this.reloadChannels(nextConfig);
3327
3552
  console.log("Config reload: channels restarted.");
@@ -3423,6 +3648,12 @@ var ConfigReloader = class {
3423
3648
  }
3424
3649
  return await this.options.reloadPlugins(params);
3425
3650
  }
3651
+ async reloadMcp(params) {
3652
+ if (!this.options.reloadMcp) {
3653
+ return;
3654
+ }
3655
+ await this.options.reloadMcp(params);
3656
+ }
3426
3657
  };
3427
3658
 
3428
3659
  // src/cli/missing-provider.ts
@@ -4052,7 +4283,9 @@ var resolveCliSubcommandEntry = (params) => {
4052
4283
  import {
4053
4284
  DisposableStore
4054
4285
  } from "@nextclaw/core";
4286
+ import { McpRegistryService as McpRegistryService2, McpServerLifecycleManager } from "@nextclaw/mcp";
4055
4287
  import { DefaultNcpAgentRuntime } from "@nextclaw/ncp-agent-runtime";
4288
+ import { McpNcpToolRegistryAdapter } from "@nextclaw/ncp-mcp";
4056
4289
  import {
4057
4290
  readAssistantReasoningNormalizationMode,
4058
4291
  readAssistantReasoningNormalizationModeFromMetadata,
@@ -4346,6 +4579,7 @@ var NextclawNcpToolRegistry = class {
4346
4579
  this.tools.clear();
4347
4580
  this.registerDefaultTools(context);
4348
4581
  this.registerExtensionTools(context);
4582
+ this.registerAdditionalTools(context);
4349
4583
  }
4350
4584
  listTools() {
4351
4585
  return [...this.tools.values()];
@@ -4361,7 +4595,10 @@ var NextclawNcpToolRegistry = class {
4361
4595
  }));
4362
4596
  }
4363
4597
  async execute(toolCallId, toolName, args) {
4364
- return this.registry.execute(toolName, toToolParams(args), toolCallId);
4598
+ if (this.registry.has(toolName)) {
4599
+ return this.registry.execute(toolName, toToolParams(args), toolCallId);
4600
+ }
4601
+ return this.tools.get(toolName)?.execute(args);
4365
4602
  }
4366
4603
  registerDefaultTools(context) {
4367
4604
  const allowedDir = context.restrictToWorkspace ? context.workspace : void 0;
@@ -4450,6 +4687,15 @@ var NextclawNcpToolRegistry = class {
4450
4687
  new CoreToolNcpAdapter(tool, async (toolName, args) => this.registry.execute(toolName, toToolParams(args)))
4451
4688
  );
4452
4689
  }
4690
+ registerAdditionalTools(context) {
4691
+ const tools = this.options.getAdditionalTools?.(context) ?? [];
4692
+ for (const tool of tools) {
4693
+ if (this.tools.has(tool.name)) {
4694
+ continue;
4695
+ }
4696
+ this.tools.set(tool.name, tool);
4697
+ }
4698
+ }
4453
4699
  };
4454
4700
  function resolveAgentHandoffDepth(metadata) {
4455
4701
  const rawDepth = Number(metadata.agent_handoff_depth ?? 0);
@@ -4505,7 +4751,7 @@ function resolveRequestedToolNames(metadata) {
4505
4751
  )
4506
4752
  );
4507
4753
  }
4508
- function normalizeOptionalString2(value) {
4754
+ function normalizeOptionalString3(value) {
4509
4755
  return normalizeString(value) ?? void 0;
4510
4756
  }
4511
4757
  function readMetadataModel(metadata) {
@@ -4625,7 +4871,7 @@ var NextclawNcpContextBuilder = class {
4625
4871
  if (inboundModel) {
4626
4872
  session.metadata.preferred_model = inboundModel;
4627
4873
  }
4628
- const effectiveModel = normalizeOptionalString2(session.metadata.preferred_model) ?? profile.model;
4874
+ const effectiveModel = normalizeOptionalString3(session.metadata.preferred_model) ?? profile.model;
4629
4875
  const clearThinking = requestMetadata.clear_thinking === true || requestMetadata.reset_thinking === true;
4630
4876
  if (clearThinking) {
4631
4877
  delete session.metadata.preferred_thinking;
@@ -4642,8 +4888,8 @@ var NextclawNcpContextBuilder = class {
4642
4888
  model: effectiveModel,
4643
4889
  sessionThinkingLevel: parseThinkingLevel(session.metadata.preferred_thinking) ?? null
4644
4890
  });
4645
- const channel = normalizeOptionalString2(requestMetadata.channel) ?? normalizeOptionalString2(session.metadata.last_channel) ?? "ui";
4646
- const chatId = normalizeOptionalString2(requestMetadata.chatId) ?? normalizeOptionalString2(requestMetadata.chat_id) ?? normalizeOptionalString2(session.metadata.last_to) ?? "web-ui";
4891
+ const channel = normalizeOptionalString3(requestMetadata.channel) ?? normalizeOptionalString3(session.metadata.last_channel) ?? "ui";
4892
+ const chatId = normalizeOptionalString3(requestMetadata.chatId) ?? normalizeOptionalString3(requestMetadata.chat_id) ?? normalizeOptionalString3(session.metadata.last_to) ?? "web-ui";
4647
4893
  session.metadata.last_channel = channel;
4648
4894
  session.metadata.last_to = chatId;
4649
4895
  const requestedSkillNames = resolveRequestedSkillNames(requestMetadata);
@@ -5222,6 +5468,21 @@ function buildPluginRuntimeSnapshotKey(extensionRegistry) {
5222
5468
  async function createUiNcpAgent(params) {
5223
5469
  const sessionStore = new NextclawAgentSessionStore(params.sessionManager);
5224
5470
  const runtimeRegistry = new UiNcpRuntimeRegistry();
5471
+ let currentMcpConfig = params.getConfig();
5472
+ const mcpLifecycleManager = new McpServerLifecycleManager({
5473
+ getConfig: () => currentMcpConfig
5474
+ });
5475
+ const mcpRegistryService = new McpRegistryService2({
5476
+ getConfig: () => currentMcpConfig,
5477
+ lifecycleManager: mcpLifecycleManager
5478
+ });
5479
+ const mcpPrewarmResults = await mcpRegistryService.prewarmEnabledServers();
5480
+ for (const result of mcpPrewarmResults) {
5481
+ if (!result.ok) {
5482
+ console.warn(`[mcp] Failed to warm ${result.name}: ${result.error}`);
5483
+ }
5484
+ }
5485
+ const mcpToolRegistryAdapter = new McpNcpToolRegistryAdapter(mcpRegistryService);
5225
5486
  runtimeRegistry.register({
5226
5487
  kind: "native",
5227
5488
  label: "Native",
@@ -5245,7 +5506,10 @@ async function createUiNcpAgent(params) {
5245
5506
  cronService: params.cronService,
5246
5507
  gatewayController: params.gatewayController,
5247
5508
  getConfig: params.getConfig,
5248
- getExtensionRegistry: params.getExtensionRegistry
5509
+ getExtensionRegistry: params.getExtensionRegistry,
5510
+ getAdditionalTools: (context) => mcpToolRegistryAdapter.listToolsForRun({
5511
+ agentId: context.agentId
5512
+ })
5249
5513
  });
5250
5514
  return new DefaultNcpAgentRuntime({
5251
5515
  contextBuilder: new NextclawNcpContextBuilder({
@@ -5314,6 +5578,19 @@ async function createUiNcpAgent(params) {
5314
5578
  applyExtensionRegistry: (extensionRegistry) => {
5315
5579
  activeExtensionRegistry = extensionRegistry;
5316
5580
  syncPluginRuntimeRegistrations(extensionRegistry);
5581
+ },
5582
+ applyMcpConfig: async (config2) => {
5583
+ const previousConfig = currentMcpConfig;
5584
+ currentMcpConfig = config2;
5585
+ const reconcileResult = await mcpRegistryService.reconcileConfig({
5586
+ prevConfig: previousConfig,
5587
+ nextConfig: config2
5588
+ });
5589
+ for (const warmResult of reconcileResult.warmed) {
5590
+ if (!warmResult.ok) {
5591
+ console.warn(`[mcp] Failed to warm ${warmResult.name}: ${warmResult.error}`);
5592
+ }
5593
+ }
5317
5594
  }
5318
5595
  };
5319
5596
  }
@@ -5994,11 +6271,11 @@ var {
5994
6271
  getWorkspacePath: getWorkspacePath8,
5995
6272
  HeartbeatService,
5996
6273
  LiteLLMProvider,
5997
- loadConfig: loadConfig7,
6274
+ loadConfig: loadConfig8,
5998
6275
  MessageBus,
5999
6276
  ProviderManager,
6000
6277
  resolveConfigSecrets: resolveConfigSecrets2,
6001
- saveConfig: saveConfig5,
6278
+ saveConfig: saveConfig6,
6002
6279
  SessionManager,
6003
6280
  parseAgentScopedSessionKey: parseAgentScopedSessionKey3
6004
6281
  } = NextclawCore;
@@ -6019,7 +6296,7 @@ var ServiceCommands = class {
6019
6296
  this.applyLiveConfigReload = null;
6020
6297
  this.liveUiNcpAgent = null;
6021
6298
  const runtimeConfigPath = getConfigPath3();
6022
- const config2 = resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath });
6299
+ const config2 = resolveConfigSecrets2(loadConfig8(), { configPath: runtimeConfigPath });
6023
6300
  const workspace = getWorkspacePath8(config2.agents.defaults.workspace);
6024
6301
  let pluginRegistry = loadPluginRegistry(config2, workspace);
6025
6302
  let extensionRegistry = toExtensionRegistry(pluginRegistry);
@@ -6064,7 +6341,7 @@ var ServiceCommands = class {
6064
6341
  sessionManager,
6065
6342
  providerManager,
6066
6343
  makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
6067
- loadConfig: () => resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
6344
+ loadConfig: () => resolveConfigSecrets2(loadConfig8(), { configPath: runtimeConfigPath }),
6068
6345
  getExtensionChannels: () => extensionRegistry.channels,
6069
6346
  onRestartRequired: (paths) => {
6070
6347
  void this.deps.requestRestart({
@@ -6075,14 +6352,14 @@ var ServiceCommands = class {
6075
6352
  }
6076
6353
  });
6077
6354
  this.applyLiveConfigReload = async () => {
6078
- await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }));
6355
+ await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig8(), { configPath: runtimeConfigPath }));
6079
6356
  };
6080
6357
  const gatewayController = new GatewayControllerImpl({
6081
6358
  reloader,
6082
6359
  cron: cron2,
6083
6360
  sessionManager,
6084
6361
  getConfigPath: getConfigPath3,
6085
- saveConfig: saveConfig5,
6362
+ saveConfig: saveConfig6,
6086
6363
  requestRestart: async (options2) => {
6087
6364
  await this.deps.requestRestart({
6088
6365
  reason: options2?.reason ?? "gateway tool restart",
@@ -6108,7 +6385,7 @@ var ServiceCommands = class {
6108
6385
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
6109
6386
  registry: pluginRegistry,
6110
6387
  channel,
6111
- cfg: resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
6388
+ cfg: resolveConfigSecrets2(loadConfig8(), { configPath: runtimeConfigPath }),
6112
6389
  accountId
6113
6390
  })
6114
6391
  });
@@ -6136,16 +6413,19 @@ var ServiceCommands = class {
6136
6413
  }
6137
6414
  return { restartChannels: result.restartChannels };
6138
6415
  });
6416
+ reloader.setReloadMcp(async ({ config: nextConfig }) => {
6417
+ await this.liveUiNcpAgent?.applyMcpConfig?.(nextConfig);
6418
+ });
6139
6419
  let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
6140
6420
  setPluginRuntimeBridge({
6141
- loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }), pluginChannelBindings),
6421
+ loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig8(), { configPath: runtimeConfigPath }), pluginChannelBindings),
6142
6422
  writeConfigFile: async (nextConfigView) => {
6143
6423
  if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
6144
6424
  throw new Error("plugin runtime writeConfigFile expects an object config");
6145
6425
  }
6146
- const current = loadConfig7();
6426
+ const current = loadConfig8();
6147
6427
  const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
6148
- saveConfig5(next);
6428
+ saveConfig6(next);
6149
6429
  },
6150
6430
  dispatchReplyWithBufferedBlockDispatcher: async ({ ctx, dispatcherOptions }) => {
6151
6431
  const bodyForAgent = typeof ctx.BodyForAgent === "string" ? ctx.BodyForAgent : "";
@@ -6219,12 +6499,12 @@ var ServiceCommands = class {
6219
6499
  providerManager,
6220
6500
  bus,
6221
6501
  gatewayController,
6222
- () => resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
6502
+ () => resolveConfigSecrets2(loadConfig8(), { configPath: runtimeConfigPath }),
6223
6503
  () => extensionRegistry,
6224
6504
  ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
6225
6505
  registry: pluginRegistry,
6226
6506
  channel,
6227
- cfg: resolveConfigSecrets2(loadConfig7(), { configPath: runtimeConfigPath }),
6507
+ cfg: resolveConfigSecrets2(loadConfig8(), { configPath: runtimeConfigPath }),
6228
6508
  accountId
6229
6509
  })
6230
6510
  );
@@ -6233,27 +6513,7 @@ var ServiceCommands = class {
6233
6513
  console.log(`\u2713 Cron: ${cronStatus.jobs} scheduled jobs`);
6234
6514
  }
6235
6515
  console.log("\u2713 Heartbeat: every 30m");
6236
- const configPath = resolve10(getConfigPath3());
6237
- const watcher = chokidar.watch(configPath, {
6238
- ignoreInitial: true,
6239
- awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
6240
- });
6241
- watcher.on("all", (event, changedPath) => {
6242
- if (resolve10(changedPath) !== configPath) {
6243
- return;
6244
- }
6245
- if (event === "add") {
6246
- reloader.scheduleReload("config add");
6247
- return;
6248
- }
6249
- if (event === "change") {
6250
- reloader.scheduleReload("config change");
6251
- return;
6252
- }
6253
- if (event === "unlink") {
6254
- reloader.scheduleReload("config unlink");
6255
- }
6256
- });
6516
+ this.watchConfigFile(reloader);
6257
6517
  await cron2.start();
6258
6518
  await heartbeat.start();
6259
6519
  try {
@@ -6280,6 +6540,29 @@ var ServiceCommands = class {
6280
6540
  const trimmed = value.trim();
6281
6541
  return trimmed || void 0;
6282
6542
  }
6543
+ watchConfigFile(reloader) {
6544
+ const configPath = resolve10(getConfigPath3());
6545
+ const watcher = chokidar.watch(configPath, {
6546
+ ignoreInitial: true,
6547
+ awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
6548
+ });
6549
+ watcher.on("all", (event, changedPath) => {
6550
+ if (resolve10(changedPath) !== configPath) {
6551
+ return;
6552
+ }
6553
+ if (event === "add") {
6554
+ reloader.scheduleReload("config add");
6555
+ return;
6556
+ }
6557
+ if (event === "change") {
6558
+ reloader.scheduleReload("config change");
6559
+ return;
6560
+ }
6561
+ if (event === "unlink") {
6562
+ reloader.scheduleReload("config unlink");
6563
+ }
6564
+ });
6565
+ }
6283
6566
  resolveMostRecentRoutableSessionKey(sessionManager) {
6284
6567
  const sessions = sessionManager.listSessions();
6285
6568
  let best = null;
@@ -6379,7 +6662,7 @@ var ServiceCommands = class {
6379
6662
  });
6380
6663
  }
6381
6664
  async runForeground(options) {
6382
- const config2 = loadConfig7();
6665
+ const config2 = loadConfig8();
6383
6666
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
6384
6667
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
6385
6668
  if (options.open) {
@@ -6392,7 +6675,7 @@ var ServiceCommands = class {
6392
6675
  });
6393
6676
  }
6394
6677
  async startService(options) {
6395
- const config2 = loadConfig7();
6678
+ const config2 = loadConfig8();
6396
6679
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
6397
6680
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
6398
6681
  const apiUrl = `${uiUrl}/api`;
@@ -7038,7 +7321,7 @@ var ServiceCommands = class {
7038
7321
  if (params.kind && params.kind !== "marketplace") {
7039
7322
  throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
7040
7323
  }
7041
- const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
7324
+ const workspace = getWorkspacePath8(loadConfig8().agents.defaults.workspace);
7042
7325
  const args = buildMarketplaceSkillInstallArgs({
7043
7326
  slug: params.slug,
7044
7327
  workspace,
@@ -7074,7 +7357,7 @@ var ServiceCommands = class {
7074
7357
  return { message: result.message };
7075
7358
  }
7076
7359
  async uninstallMarketplaceSkill(slug) {
7077
- const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
7360
+ const workspace = getWorkspacePath8(loadConfig8().agents.defaults.workspace);
7078
7361
  const targetDir = join5(workspace, "skills", slug);
7079
7362
  if (!existsSync9(targetDir)) {
7080
7363
  throw new Error(`Skill not installed in workspace: ${slug}`);
@@ -7085,7 +7368,7 @@ var ServiceCommands = class {
7085
7368
  };
7086
7369
  }
7087
7370
  installBuiltinMarketplaceSkill(slug, force) {
7088
- const workspace = getWorkspacePath8(loadConfig7().agents.defaults.workspace);
7371
+ const workspace = getWorkspacePath8(loadConfig8().agents.defaults.workspace);
7089
7372
  const destination = join5(workspace, "skills", slug);
7090
7373
  const destinationSkillFile = join5(destination, "SKILL.md");
7091
7374
  if (existsSync9(destinationSkillFile) && !force) {
@@ -7359,6 +7642,7 @@ var CliRuntime = class {
7359
7642
  workspaceManager;
7360
7643
  serviceCommands;
7361
7644
  configCommands;
7645
+ mcpCommands;
7362
7646
  secretsCommands;
7363
7647
  pluginCommands;
7364
7648
  channelCommands;
@@ -7373,6 +7657,7 @@ var CliRuntime = class {
7373
7657
  this.configCommands = new ConfigCommands({
7374
7658
  requestRestart: (params) => this.requestRestart(params)
7375
7659
  });
7660
+ this.mcpCommands = new McpCommands();
7376
7661
  this.secretsCommands = new SecretsCommands({
7377
7662
  requestRestart: (params) => this.requestRestart(params)
7378
7663
  });
@@ -7580,10 +7865,10 @@ var CliRuntime = class {
7580
7865
  let createdConfig = false;
7581
7866
  if (!existsSync11(configPath)) {
7582
7867
  const config3 = ConfigSchema2.parse({});
7583
- saveConfig6(config3);
7868
+ saveConfig7(config3);
7584
7869
  createdConfig = true;
7585
7870
  }
7586
- const config2 = loadConfig8();
7871
+ const config2 = loadConfig9();
7587
7872
  const workspaceSetting = config2.agents.defaults.workspace;
7588
7873
  const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join7(getDataDir8(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
7589
7874
  const workspaceExisted = existsSync11(workspacePath);
@@ -7619,7 +7904,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7619
7904
  async login(opts = {}) {
7620
7905
  await this.init({ source: "login", auto: true });
7621
7906
  const configPath = getConfigPath4();
7622
- const config2 = loadConfig8(configPath);
7907
+ const config2 = loadConfig9(configPath);
7623
7908
  const providers = config2.providers;
7624
7909
  const nextclawProvider = providers.nextclaw ?? {
7625
7910
  displayName: "",
@@ -7681,7 +7966,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7681
7966
  nextclawProvider.apiBase = v1Base;
7682
7967
  nextclawProvider.apiKey = token;
7683
7968
  providers.nextclaw = nextclawProvider;
7684
- saveConfig6(config2, configPath);
7969
+ saveConfig7(config2, configPath);
7685
7970
  console.log(`\u2713 Logged in to NextClaw platform (${platformBase})`);
7686
7971
  console.log(`\u2713 Account: ${email} (${role})`);
7687
7972
  console.log(`\u2713 Token saved into providers.nextclaw.apiKey`);
@@ -7776,7 +8061,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7776
8061
  }
7777
8062
  async agent(opts) {
7778
8063
  const configPath = getConfigPath4();
7779
- const config2 = resolveConfigSecrets3(loadConfig8(), { configPath });
8064
+ const config2 = resolveConfigSecrets3(loadConfig9(), { configPath });
7780
8065
  const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
7781
8066
  const pluginRegistry = loadPluginRegistry(config2, workspace);
7782
8067
  const extensionRegistry = toExtensionRegistry(pluginRegistry);
@@ -7784,7 +8069,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7784
8069
  const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
7785
8070
  setPluginRuntimeBridge2({
7786
8071
  loadConfig: () => toPluginConfigView(
7787
- resolveConfigSecrets3(loadConfig8(), { configPath }),
8072
+ resolveConfigSecrets3(loadConfig9(), { configPath }),
7788
8073
  pluginChannelBindings
7789
8074
  ),
7790
8075
  writeConfigFile: async (nextConfigView) => {
@@ -7793,13 +8078,13 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7793
8078
  "plugin runtime writeConfigFile expects an object config"
7794
8079
  );
7795
8080
  }
7796
- const current = loadConfig8();
8081
+ const current = loadConfig9();
7797
8082
  const next = mergePluginConfigView(
7798
8083
  current,
7799
8084
  nextConfigView,
7800
8085
  pluginChannelBindings
7801
8086
  );
7802
- saveConfig6(next);
8087
+ saveConfig7(next);
7803
8088
  }
7804
8089
  });
7805
8090
  try {
@@ -7825,7 +8110,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7825
8110
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
7826
8111
  registry: pluginRegistry,
7827
8112
  channel,
7828
- cfg: resolveConfigSecrets3(loadConfig8(), { configPath }),
8113
+ cfg: resolveConfigSecrets3(loadConfig9(), { configPath }),
7829
8114
  accountId
7830
8115
  })
7831
8116
  });
@@ -7959,6 +8244,24 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
7959
8244
  async configUnset(pathExpr) {
7960
8245
  await this.configCommands.configUnset(pathExpr);
7961
8246
  }
8247
+ mcpList(opts = {}) {
8248
+ this.mcpCommands.mcpList(opts);
8249
+ }
8250
+ async mcpAdd(name, command, opts = {}) {
8251
+ await this.mcpCommands.mcpAdd(name, command, opts);
8252
+ }
8253
+ async mcpRemove(name) {
8254
+ await this.mcpCommands.mcpRemove(name);
8255
+ }
8256
+ async mcpEnable(name) {
8257
+ await this.mcpCommands.mcpEnable(name);
8258
+ }
8259
+ async mcpDisable(name) {
8260
+ await this.mcpCommands.mcpDisable(name);
8261
+ }
8262
+ async mcpDoctor(name, opts = {}) {
8263
+ await this.mcpCommands.mcpDoctor(name, opts);
8264
+ }
7962
8265
  secretsAudit(opts = {}) {
7963
8266
  this.secretsCommands.secretsAudit(opts);
7964
8267
  }
@@ -8002,7 +8305,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8002
8305
  await this.diagnosticsCommands.doctor(opts);
8003
8306
  }
8004
8307
  async skillsInstall(options) {
8005
- const config2 = loadConfig8();
8308
+ const config2 = loadConfig9();
8006
8309
  const workdir = resolveSkillsInstallWorkdir({
8007
8310
  explicitWorkdir: options.workdir,
8008
8311
  configuredWorkspace: config2.agents.defaults.workspace
@@ -8095,6 +8398,13 @@ var config = program.command("config").description("Manage config values");
8095
8398
  config.command("get <path>").description("Get a config value by dot path").option("--json", "Output JSON", false).action((path2, opts) => runtime.configGet(path2, opts));
8096
8399
  config.command("set <path> <value>").description("Set a config value by dot path").option("--json", "Parse value as JSON", false).action((path2, value, opts) => runtime.configSet(path2, value, opts));
8097
8400
  config.command("unset <path>").description("Remove a config value by dot path").action((path2) => runtime.configUnset(path2));
8401
+ var mcp = program.command("mcp").description("Manage MCP servers");
8402
+ mcp.command("list").description("List configured MCP servers").option("--json", "Output JSON", false).action((opts) => runtime.mcpList(opts));
8403
+ mcp.command("add <name> [command...]").description("Add an MCP server (stdio by default, or use --transport http|sse)").allowUnknownOption(true).option("--transport <type>", "Transport type: stdio|http|sse", "stdio").option("--url <url>", "HTTP/SSE endpoint URL").option("--header <key=value>", "Transport header (repeatable)", withRepeatableTag, []).option("--env <key=value>", "stdio env var (repeatable)", withRepeatableTag, []).option("--cwd <dir>", "stdio working directory").option("--timeout-ms <ms>", "HTTP/SSE timeout in milliseconds").option("--stderr <mode>", "stdio stderr handling: inherit|pipe|ignore", "pipe").option("--disabled", "Create the server in disabled state", false).option("--all-agents", "Expose this server to all agents", false).option("--agent <id>", "Expose to an agent id (repeatable)", withRepeatableTag, []).option("--insecure", "Disable TLS verification for HTTP/SSE", false).action(async (name, command, opts) => runtime.mcpAdd(name, command ?? [], opts));
8404
+ mcp.command("remove <name>").description("Remove an MCP server").action(async (name) => runtime.mcpRemove(name));
8405
+ mcp.command("enable <name>").description("Enable an MCP server").action(async (name) => runtime.mcpEnable(name));
8406
+ mcp.command("disable <name>").description("Disable an MCP server").action(async (name) => runtime.mcpDisable(name));
8407
+ mcp.command("doctor [name]").description("Check MCP server connectivity and tool discovery").option("--json", "Output JSON", false).action(async (name, opts) => runtime.mcpDoctor(name, opts));
8098
8408
  var secrets = program.command("secrets").description("Manage secrets refs/providers");
8099
8409
  secrets.command("audit").description("Audit secret refs resolution status").option("--json", "Output JSON", false).option("--strict", "Exit non-zero when unresolved refs exist", false).action((opts) => runtime.secretsAudit(opts));
8100
8410
  secrets.command("configure").description("Configure a secret provider alias").requiredOption("--provider <alias>", "Provider alias").option("--source <source>", "Provider source (env|file|exec)").option("--prefix <prefix>", "Env key prefix (env source)").option("--path <path>", "Secret JSON file path (file source)").option("--command <command>", "Command for exec source").option(