@skein-code/cli 0.3.21 → 0.3.23

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.
package/README.md CHANGED
@@ -57,8 +57,10 @@ inspectable index for code retrieval and no retrieval service dependency.
57
57
  artifact for bounded readback.
58
58
  - **Measurable token economy:** every model request records a privacy-safe
59
59
  receipt for stable, dynamic, history, retrieval, tool-result, and tool-schema
60
- estimates, alongside actual provider usage when available. Receipts retain
61
- only counts and runtime decisions, never prompt or source content.
60
+ estimates, alongside actual provider usage when available. OpenAI, Anthropic,
61
+ and Gemini cache/reasoning counters are normalized into session totals and
62
+ structured events when the provider reports them. Receipts retain only counts
63
+ and runtime decisions, never prompt or source content.
62
64
  - **Reversible work:** Skein snapshots affected files before mutation without
63
65
  touching your Git history.
64
66
  - **Resumable by default:** conversations, tasks, usage, and changed files live
@@ -94,7 +96,7 @@ To build, verify, and install a local package artifact from this checkout:
94
96
 
95
97
  ```bash
96
98
  npm run verify:package -- --output-dir artifacts/package
97
- npm install -g ./artifacts/package/skein-code-cli-0.3.21.tgz
99
+ npm install -g ./artifacts/package/skein-code-cli-0.3.23.tgz
98
100
  ```
99
101
 
100
102
  To install the published package from npm:
@@ -212,6 +214,13 @@ only to the originating session, expire after seven days, and are removed when
212
214
  that session is deleted. A receipt marked `source-truncated` is honest about
213
215
  bytes already omitted by the producing tool; those bytes cannot be recovered.
214
216
 
217
+ Normal chat runs discover MCP servers lazily. Startup exposes only a compact
218
+ `mcp_activate` catalog of configured servers; selecting one connects that
219
+ server, runs remote discovery, and loads at most eight request-relevant schemas
220
+ into the live tool registry. All activated MCP tools remain `network`
221
+ operations. The explicit `skein mcp status` diagnostic still connects
222
+ configured servers eagerly.
223
+
215
224
  At 96 columns and wider, a fresh session uses a two-column welcome surface. The
216
225
  left side keeps Skein's brand, workspace path, and next actions close to the
217
226
  composer; the right side reports the actual model, mode, local index file/chunk
package/dist/cli.js CHANGED
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
220
220
  // package.json
221
221
  var package_default = {
222
222
  name: "@skein-code/cli",
223
- version: "0.3.21",
223
+ version: "0.3.23",
224
224
  description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
225
225
  type: "module",
226
226
  license: "MIT",
@@ -236,6 +236,12 @@ var package_default = {
236
236
  },
237
237
  skein: {
238
238
  releaseNotes: [
239
+ "Normal chat startup now exposes a compact mcp_activate catalog instead of eagerly connecting MCP servers",
240
+ "MCP activation connects one selected server, discovers remote tools, and loads at most eight relevant schemas",
241
+ "Explicit MCP diagnostics can still connect eagerly while all MCP activation and remote calls remain network operations",
242
+ "OpenAI, Anthropic, and Gemini usage now normalizes cached input, cache-write input, and reasoning tokens when reported",
243
+ "Token Ledger, session totals, and structured usage events preserve provider cache and reasoning counts without prompt content",
244
+ "Streaming, non-streaming, explicit-zero, and legacy-session compatibility paths are covered without claiming cache activation",
239
245
  "Failed configured verification output yields bounded current-run diagnostic ranking without persisting paths",
240
246
  "Diagnostic hints cannot create zero-relevance hits and are cleared on success or the next agent run",
241
247
  "Python absolute and relative module imports now contribute deterministic offline graph adjacency",
@@ -1935,6 +1941,7 @@ var agentTeamConfigSchema = z2.object({
1935
1941
  var mcpServerSchema = z2.object({
1936
1942
  enabled: z2.boolean().optional(),
1937
1943
  transport: z2.enum(["stdio", "http"]).optional(),
1944
+ description: z2.string().min(1).max(500).optional(),
1938
1945
  command: z2.string().min(1).max(512).optional(),
1939
1946
  args: z2.array(z2.string().max(4e3)).max(64).optional(),
1940
1947
  cwd: z2.string().max(4e3).optional(),
@@ -5349,10 +5356,7 @@ var AnthropicProvider = class {
5349
5356
  name: block.name ?? "unknown",
5350
5357
  arguments: safeJsonArguments(block.input)
5351
5358
  })),
5352
- usage: {
5353
- ...data.usage?.input_tokens !== void 0 ? { inputTokens: data.usage.input_tokens } : {},
5354
- ...data.usage?.output_tokens !== void 0 ? { outputTokens: data.usage.output_tokens } : {}
5355
- },
5359
+ usage: normalizeAnthropicUsage(data.usage),
5356
5360
  ...data.stop_reason ? { stopReason: data.stop_reason } : {}
5357
5361
  };
5358
5362
  }
@@ -5393,6 +5397,8 @@ var AnthropicProvider = class {
5393
5397
  let content = "";
5394
5398
  let inputTokens;
5395
5399
  let outputTokens;
5400
+ let cachedInputTokens;
5401
+ let cacheWriteInputTokens;
5396
5402
  let stopReason;
5397
5403
  const calls = /* @__PURE__ */ new Map();
5398
5404
  for await (const event of parseServerSentEvents(response)) {
@@ -5402,6 +5408,13 @@ var AnthropicProvider = class {
5402
5408
  }
5403
5409
  if (chunk.usage?.input_tokens !== void 0) inputTokens = chunk.usage.input_tokens;
5404
5410
  if (chunk.usage?.output_tokens !== void 0) outputTokens = chunk.usage.output_tokens;
5411
+ const usage = chunk.usage ?? chunk.message?.usage;
5412
+ if (usage?.cache_read_input_tokens !== void 0) {
5413
+ cachedInputTokens = usage.cache_read_input_tokens;
5414
+ }
5415
+ if (usage?.cache_creation_input_tokens !== void 0) {
5416
+ cacheWriteInputTokens = usage.cache_creation_input_tokens;
5417
+ }
5405
5418
  if (chunk.delta?.stop_reason) stopReason = chunk.delta.stop_reason;
5406
5419
  const index = chunk.index ?? 0;
5407
5420
  if (chunk.type === "content_block_start" && chunk.content_block?.type === "text") {
@@ -5440,7 +5453,9 @@ var AnthropicProvider = class {
5440
5453
  })),
5441
5454
  usage: {
5442
5455
  ...inputTokens !== void 0 ? { inputTokens } : {},
5443
- ...outputTokens !== void 0 ? { outputTokens } : {}
5456
+ ...outputTokens !== void 0 ? { outputTokens } : {},
5457
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
5458
+ ...cacheWriteInputTokens !== void 0 ? { cacheWriteInputTokens } : {}
5444
5459
  },
5445
5460
  ...stopReason ? { stopReason } : {}
5446
5461
  }
@@ -5456,13 +5471,18 @@ function normalizeAnthropicResponse(data) {
5456
5471
  name: block.name ?? "unknown",
5457
5472
  arguments: safeJsonArguments(block.input)
5458
5473
  })),
5459
- usage: {
5460
- ...data.usage?.input_tokens !== void 0 ? { inputTokens: data.usage.input_tokens } : {},
5461
- ...data.usage?.output_tokens !== void 0 ? { outputTokens: data.usage.output_tokens } : {}
5462
- },
5474
+ usage: normalizeAnthropicUsage(data.usage),
5463
5475
  ...data.stop_reason ? { stopReason: data.stop_reason } : {}
5464
5476
  };
5465
5477
  }
5478
+ function normalizeAnthropicUsage(usage) {
5479
+ return {
5480
+ ...usage?.input_tokens !== void 0 ? { inputTokens: usage.input_tokens } : {},
5481
+ ...usage?.output_tokens !== void 0 ? { outputTokens: usage.output_tokens } : {},
5482
+ ...usage?.cache_read_input_tokens !== void 0 ? { cachedInputTokens: usage.cache_read_input_tokens } : {},
5483
+ ...usage?.cache_creation_input_tokens !== void 0 ? { cacheWriteInputTokens: usage.cache_creation_input_tokens } : {}
5484
+ };
5485
+ }
5466
5486
  function toAnthropicMessage(message2) {
5467
5487
  if (message2.role === "tool") {
5468
5488
  return {
@@ -5536,10 +5556,7 @@ var GeminiProvider = class {
5536
5556
  name: part.functionCall?.name ?? "unknown",
5537
5557
  arguments: safeJsonArguments(part.functionCall?.args)
5538
5558
  })),
5539
- usage: {
5540
- ...data.usageMetadata?.promptTokenCount !== void 0 ? { inputTokens: data.usageMetadata.promptTokenCount } : {},
5541
- ...data.usageMetadata?.candidatesTokenCount !== void 0 ? { outputTokens: data.usageMetadata.candidatesTokenCount } : {}
5542
- },
5559
+ usage: normalizeGeminiUsage(data.usageMetadata),
5543
5560
  ...candidate?.finishReason ? { stopReason: candidate.finishReason } : {}
5544
5561
  };
5545
5562
  }
@@ -5579,6 +5596,8 @@ var GeminiProvider = class {
5579
5596
  let content = "";
5580
5597
  let inputTokens;
5581
5598
  let outputTokens;
5599
+ let cachedInputTokens;
5600
+ let reasoningTokens;
5582
5601
  let stopReason;
5583
5602
  const toolCalls = [];
5584
5603
  for await (const event of parseServerSentEvents(response)) {
@@ -5590,6 +5609,12 @@ var GeminiProvider = class {
5590
5609
  if (chunk.usageMetadata?.candidatesTokenCount !== void 0) {
5591
5610
  outputTokens = chunk.usageMetadata.candidatesTokenCount;
5592
5611
  }
5612
+ if (chunk.usageMetadata?.cachedContentTokenCount !== void 0) {
5613
+ cachedInputTokens = chunk.usageMetadata.cachedContentTokenCount;
5614
+ }
5615
+ if (chunk.usageMetadata?.thoughtsTokenCount !== void 0) {
5616
+ reasoningTokens = chunk.usageMetadata.thoughtsTokenCount;
5617
+ }
5593
5618
  const candidate = chunk.candidates?.[0];
5594
5619
  if (candidate?.finishReason) stopReason = candidate.finishReason;
5595
5620
  for (const part of candidate?.content?.parts ?? []) {
@@ -5613,7 +5638,9 @@ var GeminiProvider = class {
5613
5638
  toolCalls,
5614
5639
  usage: {
5615
5640
  ...inputTokens !== void 0 ? { inputTokens } : {},
5616
- ...outputTokens !== void 0 ? { outputTokens } : {}
5641
+ ...outputTokens !== void 0 ? { outputTokens } : {},
5642
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
5643
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
5617
5644
  },
5618
5645
  ...stopReason ? { stopReason } : {}
5619
5646
  }
@@ -5630,13 +5657,18 @@ function normalizeGeminiResponse(data) {
5630
5657
  name: part.functionCall?.name ?? "unknown",
5631
5658
  arguments: safeJsonArguments(part.functionCall?.args)
5632
5659
  })),
5633
- usage: {
5634
- ...data.usageMetadata?.promptTokenCount !== void 0 ? { inputTokens: data.usageMetadata.promptTokenCount } : {},
5635
- ...data.usageMetadata?.candidatesTokenCount !== void 0 ? { outputTokens: data.usageMetadata.candidatesTokenCount } : {}
5636
- },
5660
+ usage: normalizeGeminiUsage(data.usageMetadata),
5637
5661
  ...candidate?.finishReason ? { stopReason: candidate.finishReason } : {}
5638
5662
  };
5639
5663
  }
5664
+ function normalizeGeminiUsage(usage) {
5665
+ return {
5666
+ ...usage?.promptTokenCount !== void 0 ? { inputTokens: usage.promptTokenCount } : {},
5667
+ ...usage?.candidatesTokenCount !== void 0 ? { outputTokens: usage.candidatesTokenCount } : {},
5668
+ ...usage?.cachedContentTokenCount !== void 0 ? { cachedInputTokens: usage.cachedContentTokenCount } : {},
5669
+ ...usage?.thoughtsTokenCount !== void 0 ? { reasoningTokens: usage.thoughtsTokenCount } : {}
5670
+ };
5671
+ }
5640
5672
  function toGeminiMessage(message2) {
5641
5673
  if (message2.role === "tool") {
5642
5674
  return {
@@ -5764,6 +5796,8 @@ var OpenAIProvider = class {
5764
5796
  let content = "";
5765
5797
  let inputTokens;
5766
5798
  let outputTokens;
5799
+ let cachedInputTokens;
5800
+ let reasoningTokens;
5767
5801
  let stopReason;
5768
5802
  const calls = /* @__PURE__ */ new Map();
5769
5803
  for await (const event of parseServerSentEvents(response)) {
@@ -5771,6 +5805,12 @@ var OpenAIProvider = class {
5771
5805
  const chunk = JSON.parse(event.data);
5772
5806
  if (chunk.usage?.prompt_tokens !== void 0) inputTokens = chunk.usage.prompt_tokens;
5773
5807
  if (chunk.usage?.completion_tokens !== void 0) outputTokens = chunk.usage.completion_tokens;
5808
+ if (chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0) {
5809
+ cachedInputTokens = chunk.usage.prompt_tokens_details.cached_tokens;
5810
+ }
5811
+ if (chunk.usage?.completion_tokens_details?.reasoning_tokens !== void 0) {
5812
+ reasoningTokens = chunk.usage.completion_tokens_details.reasoning_tokens;
5813
+ }
5774
5814
  const choice = chunk.choices?.[0];
5775
5815
  if (choice?.finish_reason) stopReason = choice.finish_reason;
5776
5816
  const delta = choice?.delta;
@@ -5798,7 +5838,9 @@ var OpenAIProvider = class {
5798
5838
  })),
5799
5839
  usage: {
5800
5840
  ...inputTokens !== void 0 ? { inputTokens } : {},
5801
- ...outputTokens !== void 0 ? { outputTokens } : {}
5841
+ ...outputTokens !== void 0 ? { outputTokens } : {},
5842
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
5843
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
5802
5844
  },
5803
5845
  ...stopReason ? { stopReason } : {}
5804
5846
  }
@@ -5816,13 +5858,18 @@ function normalizeOpenAIResponse(data) {
5816
5858
  name: call.function?.name ?? "unknown",
5817
5859
  arguments: safeJsonArguments(call.function?.arguments)
5818
5860
  })),
5819
- usage: {
5820
- ...data.usage?.prompt_tokens !== void 0 ? { inputTokens: data.usage.prompt_tokens } : {},
5821
- ...data.usage?.completion_tokens !== void 0 ? { outputTokens: data.usage.completion_tokens } : {}
5822
- },
5861
+ usage: normalizeOpenAIUsage(data.usage),
5823
5862
  ...choice?.finish_reason ? { stopReason: choice.finish_reason } : {}
5824
5863
  };
5825
5864
  }
5865
+ function normalizeOpenAIUsage(usage) {
5866
+ return {
5867
+ ...usage?.prompt_tokens !== void 0 ? { inputTokens: usage.prompt_tokens } : {},
5868
+ ...usage?.completion_tokens !== void 0 ? { outputTokens: usage.completion_tokens } : {},
5869
+ ...usage?.prompt_tokens_details?.cached_tokens !== void 0 ? { cachedInputTokens: usage.prompt_tokens_details.cached_tokens } : {},
5870
+ ...usage?.completion_tokens_details?.reasoning_tokens !== void 0 ? { reasoningTokens: usage.completion_tokens_details.reasoning_tokens } : {}
5871
+ };
5872
+ }
5826
5873
  function toOpenAIMessage(message2) {
5827
5874
  if (message2.role === "tool") {
5828
5875
  return {
@@ -6399,6 +6446,9 @@ var sessionSchema = z5.object({
6399
6446
  outputSource: z5.enum(["actual", "estimated", "mixed", "unknown"]).optional(),
6400
6447
  actualInputTokens: z5.number().nonnegative().optional(),
6401
6448
  actualOutputTokens: z5.number().nonnegative().optional(),
6449
+ actualCachedInputTokens: z5.number().nonnegative().optional(),
6450
+ actualCacheWriteInputTokens: z5.number().nonnegative().optional(),
6451
+ actualReasoningTokens: z5.number().nonnegative().optional(),
6402
6452
  estimatedInputTokens: z5.number().nonnegative().optional(),
6403
6453
  estimatedOutputTokens: z5.number().nonnegative().optional()
6404
6454
  }).strict(),
@@ -6419,7 +6469,10 @@ var sessionSchema = z5.object({
6419
6469
  }).strict(),
6420
6470
  actual: z5.object({
6421
6471
  inputTokens: z5.number().nonnegative().optional(),
6422
- outputTokens: z5.number().nonnegative().optional()
6472
+ outputTokens: z5.number().nonnegative().optional(),
6473
+ cachedInputTokens: z5.number().nonnegative().optional(),
6474
+ cacheWriteInputTokens: z5.number().nonnegative().optional(),
6475
+ reasoningTokens: z5.number().nonnegative().optional()
6423
6476
  }).strict(),
6424
6477
  inputSource: z5.enum(["actual", "estimated"]),
6425
6478
  outputSource: z5.enum(["actual", "estimated"]),
@@ -10830,6 +10883,9 @@ var AgentRunner = class {
10830
10883
  const { inputTokens, outputTokens } = turnUsage;
10831
10884
  const actualInputTokens = validTokenCount(response.usage?.inputTokens);
10832
10885
  const actualOutputTokens = validTokenCount(response.usage?.outputTokens);
10886
+ const actualCachedInputTokens = validTokenCount(response.usage?.cachedInputTokens);
10887
+ const actualCacheWriteInputTokens = validTokenCount(response.usage?.cacheWriteInputTokens);
10888
+ const actualReasoningTokens = validTokenCount(response.usage?.reasoningTokens);
10833
10889
  const receipt = recordTokenLedger(this.session, {
10834
10890
  requestId: userMessage.id,
10835
10891
  turn,
@@ -10840,7 +10896,10 @@ var AgentRunner = class {
10840
10896
  },
10841
10897
  actual: {
10842
10898
  ...actualInputTokens === void 0 ? {} : { inputTokens: actualInputTokens },
10843
- ...actualOutputTokens === void 0 ? {} : { outputTokens: actualOutputTokens }
10899
+ ...actualOutputTokens === void 0 ? {} : { outputTokens: actualOutputTokens },
10900
+ ...actualCachedInputTokens === void 0 ? {} : { cachedInputTokens: actualCachedInputTokens },
10901
+ ...actualCacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens: actualCacheWriteInputTokens },
10902
+ ...actualReasoningTokens === void 0 ? {} : { reasoningTokens: actualReasoningTokens }
10844
10903
  },
10845
10904
  inputSource: actualInputTokens === void 0 ? "estimated" : "actual",
10846
10905
  outputSource: actualOutputTokens === void 0 ? "estimated" : "actual",
@@ -10859,7 +10918,10 @@ var AgentRunner = class {
10859
10918
  outputSource: this.session.usage.outputSource ?? "unknown",
10860
10919
  actual: {
10861
10920
  inputTokens: this.session.usage.actualInputTokens ?? 0,
10862
- outputTokens: this.session.usage.actualOutputTokens ?? 0
10921
+ outputTokens: this.session.usage.actualOutputTokens ?? 0,
10922
+ ...this.session.usage.actualCachedInputTokens === void 0 ? {} : { cachedInputTokens: this.session.usage.actualCachedInputTokens },
10923
+ ...this.session.usage.actualCacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens: this.session.usage.actualCacheWriteInputTokens },
10924
+ ...this.session.usage.actualReasoningTokens === void 0 ? {} : { reasoningTokens: this.session.usage.actualReasoningTokens }
10863
10925
  },
10864
10926
  estimated: {
10865
10927
  inputTokens: this.session.usage.estimatedInputTokens ?? 0,
@@ -11560,6 +11622,18 @@ function recordTokenUsage(session, providerUsage, estimatedInputTokens, estimate
11560
11622
  } else {
11561
11623
  session.usage.estimatedOutputTokens = (session.usage.estimatedOutputTokens ?? 0) + outputTokens;
11562
11624
  }
11625
+ const cachedInputActual = validTokenCount(providerUsage?.cachedInputTokens);
11626
+ const cacheWriteInputActual = validTokenCount(providerUsage?.cacheWriteInputTokens);
11627
+ const reasoningActual = validTokenCount(providerUsage?.reasoningTokens);
11628
+ if (cachedInputActual !== void 0) {
11629
+ session.usage.actualCachedInputTokens = (session.usage.actualCachedInputTokens ?? 0) + cachedInputActual;
11630
+ }
11631
+ if (cacheWriteInputActual !== void 0) {
11632
+ session.usage.actualCacheWriteInputTokens = (session.usage.actualCacheWriteInputTokens ?? 0) + cacheWriteInputActual;
11633
+ }
11634
+ if (reasoningActual !== void 0) {
11635
+ session.usage.actualReasoningTokens = (session.usage.actualReasoningTokens ?? 0) + reasoningActual;
11636
+ }
11563
11637
  session.usage.inputSource = mergeMeasurementSource(
11564
11638
  priorInputSource,
11565
11639
  inputActual === void 0 ? "estimated" : "actual",
@@ -19870,12 +19944,24 @@ var MAX_TOOLS_PER_SERVER = 256;
19870
19944
  var MAX_LIST_PAGES = 16;
19871
19945
  var DEFAULT_CONNECT_TIMEOUT = 12e3;
19872
19946
  var DEFAULT_TOOL_TIMEOUT = 6e4;
19947
+ var LAZY_SCHEMA_LIMIT = 8;
19873
19948
  var McpManager = class {
19874
19949
  constructor(config, options = {}) {
19875
19950
  this.config = config;
19876
19951
  this.options = options;
19877
- for (const [name, server] of Object.entries(config.servers ?? {})) {
19952
+ const entries = Object.entries(config.servers ?? {});
19953
+ for (const [index, [name, server]] of entries.entries()) {
19878
19954
  const transport = server.transport ?? "stdio";
19955
+ if (index >= MAX_SERVERS) {
19956
+ this.statuses.set(name, {
19957
+ name,
19958
+ state: "error",
19959
+ transport,
19960
+ toolCount: 0,
19961
+ error: `MCP server limit exceeded (maximum ${MAX_SERVERS})`
19962
+ });
19963
+ continue;
19964
+ }
19879
19965
  const state = config.enabled === false || server.enabled === false ? "disabled" : "disconnected";
19880
19966
  this.statuses.set(name, { name, state, transport, toolCount: 0 });
19881
19967
  }
@@ -19890,6 +19976,75 @@ var McpManager = class {
19890
19976
  options;
19891
19977
  shutdownController = new AbortController();
19892
19978
  closed = false;
19979
+ /** Compact model-visible catalog; transport and remote discovery stay lazy. */
19980
+ activationTool(registry) {
19981
+ const names = this.activatableServerNames();
19982
+ if (!names.length) return;
19983
+ const catalog = names.map((name) => {
19984
+ const server = this.config.servers[name];
19985
+ const description = sanitizeCatalogText(server?.description) || `${server?.transport ?? "stdio"} MCP server`;
19986
+ return `${name}: ${description}`;
19987
+ }).join("; ");
19988
+ return {
19989
+ definition: {
19990
+ name: "mcp_activate",
19991
+ description: `Connect to one configured MCP server only when the current task needs it, discover its tools, and load at most ${LAZY_SCHEMA_LIMIT} relevant schemas. Available servers: ${catalog}`,
19992
+ category: "network",
19993
+ inputSchema: {
19994
+ type: "object",
19995
+ properties: {
19996
+ server: { type: "string", enum: names, description: "Configured MCP server to activate." },
19997
+ query: { type: "string", minLength: 1, maxLength: 500, description: "Capability needed from that server." }
19998
+ },
19999
+ required: ["server", "query"],
20000
+ additionalProperties: false
20001
+ }
20002
+ },
20003
+ permissionCategories: () => ["network"],
20004
+ execute: async (arguments_, context) => {
20005
+ const server = typeof arguments_.server === "string" ? arguments_.server : "";
20006
+ const query = typeof arguments_.query === "string" ? arguments_.query.trim() : "";
20007
+ if (!names.includes(server)) throw new ToolInputError("MCP server is not available for activation");
20008
+ if (!query || query.length > 500) {
20009
+ throw new ToolInputError("MCP activation query must contain 1 to 500 characters");
20010
+ }
20011
+ const result = await this.activate(server, query, registry, context.signal);
20012
+ if (!result.ok) {
20013
+ return {
20014
+ ok: false,
20015
+ content: `MCP server ${server} could not be activated: ${result.status.error ?? result.status.state}`,
20016
+ metadata: activationMetadata(result)
20017
+ };
20018
+ }
20019
+ const loaded = result.registeredTools.length ? result.registeredTools.join(", ") : "no matching tool schemas";
20020
+ const deferred = result.deferredTools ? ` ${result.deferredTools} additional schemas remain deferred; activate again with a narrower query if needed.` : "";
20021
+ return {
20022
+ content: `Activated MCP server ${server}. Loaded: ${loaded}.${deferred}`,
20023
+ metadata: activationMetadata(result)
20024
+ };
20025
+ }
20026
+ };
20027
+ }
20028
+ /** Connect/discover one server, then register only request-relevant schemas. */
20029
+ async activate(name, query, registry, signal) {
20030
+ const connected = await this.connect(name, signal);
20031
+ if (!connected.ok) {
20032
+ return { ...connected, registeredTools: [], availableTools: 0, deferredTools: 0 };
20033
+ }
20034
+ const connection = this.connections.get(name);
20035
+ if (!connection) {
20036
+ return { ...connected, ok: false, registeredTools: [], availableTools: 0, deferredTools: 0 };
20037
+ }
20038
+ const tools = [...connection.tools.values()];
20039
+ const selected = tools.length <= LAZY_SCHEMA_LIMIT ? tools : selectRelevantTools(tools, query, LAZY_SCHEMA_LIMIT);
20040
+ this.registerSelectedTools(registry, selected);
20041
+ return {
20042
+ ...connected,
20043
+ registeredTools: selected.map((tool) => tool.definition.name),
20044
+ availableTools: tools.length,
20045
+ deferredTools: Math.max(0, tools.length - selected.length)
20046
+ };
20047
+ }
19893
20048
  /** Connect enabled servers with a small concurrency bound. */
19894
20049
  async connectAll(signal) {
19895
20050
  if (this.closed) throw new Error("MCP manager is closed");
@@ -19898,15 +20053,6 @@ var McpManager = class {
19898
20053
  if (this.config.enabled === false) {
19899
20054
  return configuredNames.map((name) => this.resultFor(name, false, 0));
19900
20055
  }
19901
- for (const name of configuredNames.slice(MAX_SERVERS)) {
19902
- const server = this.config.servers[name];
19903
- this.setStatus(name, {
19904
- state: "error",
19905
- transport: server?.transport ?? "stdio",
19906
- toolCount: 0,
19907
- error: `MCP server limit exceeded (maximum ${MAX_SERVERS})`
19908
- });
19909
- }
19910
20056
  const results = [];
19911
20057
  let cursor = 0;
19912
20058
  const worker = async () => {
@@ -19922,6 +20068,10 @@ var McpManager = class {
19922
20068
  /** Connect one configured server. Connection errors are captured in status. */
19923
20069
  async connect(name, signal) {
19924
20070
  if (this.closed) throw new Error("MCP manager is closed");
20071
+ const status = this.statuses.get(name);
20072
+ if (status?.state === "error" && status.error?.includes("server limit exceeded")) {
20073
+ return this.resultFor(name, false, 0);
20074
+ }
19925
20075
  const existing = this.pending.get(name);
19926
20076
  if (existing) return existing;
19927
20077
  const connectionController = new AbortController();
@@ -20006,8 +20156,11 @@ var McpManager = class {
20006
20156
  }
20007
20157
  /** Register connected MCP tools, preserving idempotency for the same adapter. */
20008
20158
  registerTools(registry) {
20159
+ return this.registerSelectedTools(registry, this.tools());
20160
+ }
20161
+ registerSelectedTools(registry, tools) {
20009
20162
  const registered = [];
20010
- for (const tool of this.tools()) {
20163
+ for (const tool of tools) {
20011
20164
  const existing = registry.get(tool.definition.name);
20012
20165
  if (existing) {
20013
20166
  if (existing !== tool) {
@@ -20020,6 +20173,10 @@ var McpManager = class {
20020
20173
  }
20021
20174
  return registered;
20022
20175
  }
20176
+ activatableServerNames() {
20177
+ if (this.config.enabled === false) return [];
20178
+ return [...this.statuses.values()].filter((status) => status.state !== "disabled" && status.state !== "error").map((status) => status.name).sort((left, right) => left.localeCompare(right));
20179
+ }
20023
20180
  async connectInternal(name, signal) {
20024
20181
  const configured = this.config.servers?.[name];
20025
20182
  if (!configured) throw new Error(`Unknown MCP server: ${name}`);
@@ -20271,6 +20428,28 @@ var McpManager = class {
20271
20428
  return { name, ok, status: { ...status }, skippedTools };
20272
20429
  }
20273
20430
  };
20431
+ function activationMetadata(result) {
20432
+ return {
20433
+ mcpServer: result.name,
20434
+ state: result.status.state,
20435
+ availableTools: result.availableTools,
20436
+ loadedTools: result.registeredTools,
20437
+ deferredTools: result.deferredTools,
20438
+ skippedTools: result.skippedTools
20439
+ };
20440
+ }
20441
+ function selectRelevantTools(tools, query, limit) {
20442
+ const terms = new Set(query.toLocaleLowerCase().match(/[\p{L}\p{N}_-]{2,}/gu) ?? []);
20443
+ return tools.map((tool) => {
20444
+ const searchable = `${tool.definition.name.replaceAll("_", " ")} ${tool.definition.description}`.toLocaleLowerCase();
20445
+ let score = 0;
20446
+ for (const term of terms) if (searchable.includes(term)) score += term.length;
20447
+ return { tool, score };
20448
+ }).sort((left, right) => right.score - left.score || left.tool.definition.name.localeCompare(right.tool.definition.name)).slice(0, limit).map(({ tool }) => tool);
20449
+ }
20450
+ function sanitizeCatalogText(value) {
20451
+ return value ? stripAnsi5(value).replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim().slice(0, 200) : "";
20452
+ }
20274
20453
  function requestOptions(timeoutMs, signal) {
20275
20454
  return {
20276
20455
  timeout: timeoutMs,
@@ -20780,7 +20959,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
20780
20959
  throw error;
20781
20960
  }
20782
20961
  }
20783
- async initialize(registry, signal) {
20962
+ async initialize(registry, _signal) {
20784
20963
  if (this.initialized) return;
20785
20964
  if (this.memory) {
20786
20965
  await this.memory.open();
@@ -20792,8 +20971,8 @@ var ExtensionRuntime = class _ExtensionRuntime {
20792
20971
  }
20793
20972
  await this.skills?.discover();
20794
20973
  if (this.mcp) {
20795
- await this.mcp.connectAll(signal);
20796
- this.mcp.registerTools(registry);
20974
+ const activationTool = this.mcp.activationTool(registry);
20975
+ if (activationTool) registry.register(activationTool);
20797
20976
  }
20798
20977
  await this.profiles?.discover();
20799
20978
  if (this.config.agents?.enabled && this.profiles && this.options.provider && this.options.contextEngine) {