@fitchmultz/pi-mcp-adapter 8.0.6 → 8.0.7

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 (87) hide show
  1. package/CHANGELOG.md +128 -8
  2. package/OAUTH.md +13 -16
  3. package/README.md +8 -8
  4. package/cli.js +67 -28
  5. package/dist/abort.js +18 -9
  6. package/dist/client-operations.js +4 -2
  7. package/dist/commands.js +77 -43
  8. package/dist/config.js +194 -105
  9. package/dist/connection-runtime.d.ts +2 -2
  10. package/dist/connection-runtime.js +29 -18
  11. package/dist/consent-manager.d.ts +2 -2
  12. package/dist/consent-manager.js +5 -2
  13. package/dist/direct-tools.d.ts +1 -1
  14. package/dist/direct-tools.js +13 -7
  15. package/dist/elicitation-handler.d.ts +2 -3
  16. package/dist/elicitation-handler.js +113 -58
  17. package/dist/gateway-arguments.d.ts +1 -1
  18. package/dist/gateway-arguments.js +90 -23
  19. package/dist/glimpse-ui.js +10 -5
  20. package/dist/host-html-template.d.ts +3 -1
  21. package/dist/host-html-template.js +9 -12
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +264 -119
  24. package/dist/init.d.ts +1 -3
  25. package/dist/init.js +91 -51
  26. package/dist/legacy-migration.d.ts +1 -1
  27. package/dist/legacy-migration.js +17 -12
  28. package/dist/lifecycle.d.ts +3 -3
  29. package/dist/lifecycle.js +33 -17
  30. package/dist/logger.d.ts +2 -2
  31. package/dist/logger.js +4 -2
  32. package/dist/mcp-auth-flow.js +150 -58
  33. package/dist/mcp-auth.d.ts +7 -7
  34. package/dist/mcp-auth.js +189 -138
  35. package/dist/mcp-callback-server.js +19 -8
  36. package/dist/mcp-code.js +168 -54
  37. package/dist/mcp-keyring-helper.cjs +57 -23
  38. package/dist/mcp-oauth-provider.d.ts +6 -7
  39. package/dist/mcp-oauth-provider.js +146 -72
  40. package/dist/mcp-oauth-runtime.js +29 -16
  41. package/dist/mcp-output-guard.js +193 -65
  42. package/dist/mcp-output-preview.js +41 -20
  43. package/dist/mcp-panel.d.ts +9 -9
  44. package/dist/mcp-panel.js +141 -70
  45. package/dist/mcp-script-worker.mjs +135 -43
  46. package/dist/mcp-setup-panel.d.ts +8 -8
  47. package/dist/mcp-setup-panel.js +130 -44
  48. package/dist/mcp-status.js +14 -7
  49. package/dist/mcp-trace.d.ts +10 -5
  50. package/dist/mcp-trace.js +127 -61
  51. package/dist/metadata-cache.d.ts +3 -4
  52. package/dist/metadata-cache.js +83 -43
  53. package/dist/npx-resolver.js +101 -60
  54. package/dist/onboarding-state.js +4 -2
  55. package/dist/plain-result-reader.js +78 -31
  56. package/dist/prompts.js +36 -20
  57. package/dist/proxy-modes.js +670 -193
  58. package/dist/resource-tools.js +30 -9
  59. package/dist/runtime-owner.js +36 -18
  60. package/dist/sampling-handler.d.ts +1 -2
  61. package/dist/sampling-handler.js +41 -21
  62. package/dist/search-ranking.d.ts +1 -1
  63. package/dist/search-ranking.js +67 -34
  64. package/dist/server-manager.d.ts +11 -11
  65. package/dist/server-manager.js +283 -166
  66. package/dist/session-recovery.js +113 -61
  67. package/dist/tool-approval.js +9 -3
  68. package/dist/tool-loader.js +154 -55
  69. package/dist/tool-metadata.d.ts +1 -1
  70. package/dist/tool-metadata.js +51 -23
  71. package/dist/tool-registrar.js +32 -10
  72. package/dist/tool-result-renderer.js +58 -33
  73. package/dist/types.d.ts +2 -2
  74. package/dist/types.js +39 -18
  75. package/dist/ui-message-history.js +33 -13
  76. package/dist/ui-resource-handler.d.ts +3 -3
  77. package/dist/ui-resource-handler.js +37 -21
  78. package/dist/ui-server.js +101 -43
  79. package/dist/ui-session.js +66 -29
  80. package/dist/ui-stream-types.js +14 -4
  81. package/dist/ui-tool-visibility.js +12 -6
  82. package/dist/unix-socket-transport.d.ts +1 -2
  83. package/dist/unix-socket-transport.js +19 -12
  84. package/dist/utils.d.ts +2 -0
  85. package/dist/utils.js +60 -35
  86. package/package.json +26 -2
  87. package/skills/mcp-scripting/SKILL.md +6 -6
@@ -1,6 +1,6 @@
1
1
  import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
2
  import { createPanelKeys } from "./panel-keys.js";
3
- import { KNOWN_SERVER_PRESETS } from "./config.js";
3
+ import { KNOWN_SERVER_PRESETS, } from "./config.js";
4
4
  const DEFAULT_THEME = {
5
5
  border: "2",
6
6
  title: "36",
@@ -18,8 +18,9 @@ function fg(code, text) {
18
18
  return code ? `\x1b[${code}m${text}\x1b[0m` : text;
19
19
  }
20
20
  function wrapText(text, width) {
21
- if (width <= 8)
21
+ if (width <= 8) {
22
22
  return [text];
23
+ }
23
24
  const words = text.split(/\s+/).filter(Boolean);
24
25
  const lines = [];
25
26
  let current = "";
@@ -29,12 +30,14 @@ function wrapText(text, width) {
29
30
  current = candidate;
30
31
  continue;
31
32
  }
32
- if (current)
33
+ if (current) {
33
34
  lines.push(current);
35
+ }
34
36
  current = word;
35
37
  }
36
- if (current)
38
+ if (current) {
37
39
  lines.push(current);
40
+ }
38
41
  return lines.length > 0 ? lines : [""];
39
42
  }
40
43
  export class McpSetupPanel {
@@ -68,8 +71,9 @@ export class McpSetupPanel {
68
71
  this.resetInactivityTimeout();
69
72
  }
70
73
  resetInactivityTimeout() {
71
- if (this.inactivityTimeout)
74
+ if (this.inactivityTimeout) {
72
75
  clearTimeout(this.inactivityTimeout);
76
+ }
73
77
  this.inactivityTimeout = setTimeout(() => {
74
78
  this.cleanup();
75
79
  this.done();
@@ -84,24 +88,61 @@ export class McpSetupPanel {
84
88
  getActions() {
85
89
  const actions = [];
86
90
  if (this.screen === "empty") {
87
- actions.push({ id: "run-setup", label: "Run setup", description: "Inspect detected configs, adopt imports, and scaffold a minimal `.mcp.json`." });
91
+ actions.push({
92
+ id: "run-setup",
93
+ label: "Run setup",
94
+ description: "Inspect detected configs, adopt imports, and scaffold a minimal `.mcp.json`.",
95
+ });
88
96
  }
89
97
  if (this.discovery.imports.length > 0) {
90
- actions.push({ id: "adopt-imports", label: "Adopt detected compatibility imports", description: `Choose which host-specific MCP configs Pi should import into its own override file. ${this.discovery.imports.length} source${this.discovery.imports.length === 1 ? "" : "s"} found.` });
98
+ actions.push({
99
+ id: "adopt-imports",
100
+ label: "Adopt detected compatibility imports",
101
+ description: `Choose which host-specific MCP configs Pi should import into its own override file. ${this.discovery.imports.length} source${this.discovery.imports.length === 1 ? "" : "s"} found.`,
102
+ });
91
103
  }
92
- actions.push({ id: "view-example", label: "View example `.mcp.json`", description: "Preview a working shared MCP config you can paste or adapt." });
104
+ actions.push({
105
+ id: "view-example",
106
+ label: "View example `.mcp.json`",
107
+ description: "Preview a working shared MCP config you can paste or adapt.",
108
+ });
93
109
  if (!this.discovery.sources.some((source) => source.id === "shared-project" && source.exists)) {
94
- actions.push({ id: "scaffold-project", label: "Scaffold project `.mcp.json`", description: "Write a minimal project config using the standard shared MCP file path, then reload Pi." });
110
+ actions.push({
111
+ id: "scaffold-project",
112
+ label: "Scaffold project `.mcp.json`",
113
+ description: "Write a minimal project config using the standard shared MCP file path, then reload Pi.",
114
+ });
95
115
  }
96
- actions.push({ id: "show-precedence", label: "Explain config precedence", description: "Show the read order and where Pi writes compatibility settings." });
116
+ actions.push({
117
+ id: "show-precedence",
118
+ label: "Explain config precedence",
119
+ description: "Show the read order and where Pi writes compatibility settings.",
120
+ });
97
121
  if (this.getDetectedPaths().length > 0) {
98
- actions.push({ id: "open-paths", label: "Open detected config paths", description: "Browse the actual config files that Pi discovered on this machine." });
122
+ actions.push({
123
+ id: "open-paths",
124
+ label: "Open detected config paths",
125
+ description: "Browse the actual config files that Pi discovered on this machine.",
126
+ });
99
127
  }
100
128
  for (const preset of KNOWN_SERVER_PRESETS) {
101
- actions.push({ id: "add-known-server", label: preset.name, description: preset.summary, preset });
129
+ actions.push({
130
+ id: "add-known-server",
131
+ label: preset.name,
132
+ description: preset.summary,
133
+ preset,
134
+ });
102
135
  }
103
- if (!this.discovery.repoPrompt.configured && this.discovery.repoPrompt.executablePath && this.discovery.repoPrompt.targetPath && this.discovery.repoPrompt.entry && this.discovery.repoPrompt.serverName) {
104
- actions.push({ id: "add-repoprompt", label: "Add RepoPrompt to shared MCP config", description: "Write a standard MCP entry for RepoPrompt to the recommended shared target, then reload MCP in-session." });
136
+ if (!this.discovery.repoPrompt.configured &&
137
+ this.discovery.repoPrompt.executablePath &&
138
+ this.discovery.repoPrompt.targetPath &&
139
+ this.discovery.repoPrompt.entry &&
140
+ this.discovery.repoPrompt.serverName) {
141
+ actions.push({
142
+ id: "add-repoprompt",
143
+ label: "Add RepoPrompt to shared MCP config",
144
+ description: "Write a standard MCP entry for RepoPrompt to the recommended shared target, then reload MCP in-session.",
145
+ });
105
146
  }
106
147
  actions.push({ id: "close", label: "Close", description: "Exit the onboarding flow." });
107
148
  return actions;
@@ -119,8 +160,9 @@ export class McpSetupPanel {
119
160
  }
120
161
  handleInput(data) {
121
162
  this.resetInactivityTimeout();
122
- if (!this.busy)
163
+ if (!this.busy) {
123
164
  this.notice = null;
165
+ }
124
166
  if (matchesKey(data, "ctrl+c")) {
125
167
  this.cleanup();
126
168
  this.done();
@@ -136,8 +178,9 @@ export class McpSetupPanel {
136
178
  this.done();
137
179
  return;
138
180
  }
139
- if (this.busy)
181
+ if (this.busy) {
140
182
  return;
183
+ }
141
184
  if (this.screen === "imports") {
142
185
  this.handleImportsInput(data);
143
186
  return;
@@ -159,8 +202,9 @@ export class McpSetupPanel {
159
202
  }
160
203
  if (this.keys.selectConfirm(data)) {
161
204
  const selected = this.getSelectedAction();
162
- if (selected)
205
+ if (selected) {
163
206
  void this.runAction(selected);
207
+ }
164
208
  }
165
209
  }
166
210
  handleImportsInput(data) {
@@ -177,8 +221,9 @@ export class McpSetupPanel {
177
221
  }
178
222
  if (matchesKey(data, "space")) {
179
223
  const current = imports[this.importCursor];
180
- if (!current)
224
+ if (!current) {
181
225
  return;
226
+ }
182
227
  if (this.selectedImports.has(current.kind)) {
183
228
  this.selectedImports.delete(current.kind);
184
229
  }
@@ -206,8 +251,9 @@ export class McpSetupPanel {
206
251
  }
207
252
  if (this.keys.selectConfirm(data)) {
208
253
  const selected = paths[this.pathCursor];
209
- if (!selected)
254
+ if (!selected) {
210
255
  return;
256
+ }
211
257
  void this.runBusy(async () => {
212
258
  await this.callbacks.openPath(selected);
213
259
  this.notice = { text: `Opened ${selected}`, tone: "success" };
@@ -237,7 +283,10 @@ export class McpSetupPanel {
237
283
  await this.runBusy(async () => {
238
284
  const result = await this.callbacks.scaffoldProjectConfig();
239
285
  this.callbacks.markSetupCompleted();
240
- this.notice = { text: `Wrote starter config to ${result.path}. Pi will reload after this panel closes.`, tone: "success" };
286
+ this.notice = {
287
+ text: `Wrote starter config to ${result.path}. Pi will reload after this panel closes.`,
288
+ tone: "success",
289
+ };
241
290
  });
242
291
  return;
243
292
  }
@@ -245,7 +294,10 @@ export class McpSetupPanel {
245
294
  await this.runBusy(async () => {
246
295
  const result = await this.callbacks.addRepoPrompt();
247
296
  this.callbacks.markSetupCompleted();
248
- this.notice = { text: `Added ${result.serverName} to ${result.path}. Pi will reload after this panel closes.`, tone: "success" };
297
+ this.notice = {
298
+ text: `Added ${result.serverName} to ${result.path}. Pi will reload after this panel closes.`,
299
+ tone: "success",
300
+ };
249
301
  });
250
302
  return;
251
303
  }
@@ -254,7 +306,10 @@ export class McpSetupPanel {
254
306
  await this.runBusy(async () => {
255
307
  const result = await this.callbacks.addKnownServer(preset);
256
308
  this.callbacks.markSetupCompleted();
257
- this.notice = { text: `Added ${result.serverName} to ${result.path}. Pi will reload after this panel closes.`, tone: "success" };
309
+ this.notice = {
310
+ text: `Added ${result.serverName} to ${result.path}. Pi will reload after this panel closes.`,
311
+ tone: "success",
312
+ };
258
313
  });
259
314
  return;
260
315
  }
@@ -263,11 +318,16 @@ export class McpSetupPanel {
263
318
  this.done();
264
319
  return;
265
320
  }
266
- this.notice = { text: "Review the details below. Press Enter on an action with a side effect to apply it.", tone: "muted" };
321
+ this.notice = {
322
+ text: "Review the details below. Press Enter on an action with a side effect to apply it.",
323
+ tone: "muted",
324
+ };
267
325
  this.tui.requestRender();
268
326
  }
269
327
  async applySelectedImports() {
270
- const selected = this.discovery.imports.filter((entry) => this.selectedImports.has(entry.kind)).map((entry) => entry.kind);
328
+ const selected = this.discovery.imports
329
+ .filter((entry) => this.selectedImports.has(entry.kind))
330
+ .map((entry) => entry.kind);
271
331
  if (selected.length === 0) {
272
332
  this.notice = { text: "Select at least one compatibility import first.", tone: "warning" };
273
333
  this.tui.requestRender();
@@ -276,9 +336,13 @@ export class McpSetupPanel {
276
336
  await this.runBusy(async () => {
277
337
  const result = await this.callbacks.adoptImports(selected);
278
338
  this.callbacks.markSetupCompleted();
279
- this.notice = result.added.length > 0
280
- ? { text: `Added ${result.added.join(", ")} to ${result.path}. Pi will reload after this panel closes.`, tone: "success" }
281
- : { text: `No changes needed in ${result.path}.`, tone: "muted" };
339
+ this.notice =
340
+ result.added.length > 0
341
+ ? {
342
+ text: `Added ${result.added.join(", ")} to ${result.path}. Pi will reload after this panel closes.`,
343
+ tone: "success",
344
+ }
345
+ : { text: `No changes needed in ${result.path}.`, tone: "muted" };
282
346
  this.screen = this.discovery.hasAnyConfig ? "setup" : "empty";
283
347
  this.actionCursor = 0;
284
348
  });
@@ -317,7 +381,11 @@ export class McpSetupPanel {
317
381
  }
318
382
  lines.push(this.padLine("", innerW));
319
383
  if (this.notice) {
320
- const tone = this.notice.tone === "success" ? this.t.success : this.notice.tone === "warning" ? this.t.warning : this.t.hint;
384
+ const tone = this.notice.tone === "success"
385
+ ? this.t.success
386
+ : this.notice.tone === "warning"
387
+ ? this.t.warning
388
+ : this.t.hint;
321
389
  for (const line of wrapText(this.notice.text, contentW)) {
322
390
  lines.push(this.padLine(fg(tone, line), innerW));
323
391
  }
@@ -348,9 +416,11 @@ export class McpSetupPanel {
348
416
  }
349
417
  for (let index = start; index < end; index++) {
350
418
  const action = actions[index];
351
- if (!action)
419
+ if (!action) {
352
420
  continue;
353
- if (action.id === "add-known-server" && (index === start || actions[index - 1]?.id !== "add-known-server")) {
421
+ }
422
+ if (action.id === "add-known-server" &&
423
+ (index === start || actions[index - 1]?.id !== "add-known-server")) {
354
424
  lines.push(this.padLine(fg(this.t.title, "Add a known server"), innerW));
355
425
  }
356
426
  const selected = index === this.actionCursor;
@@ -361,12 +431,14 @@ export class McpSetupPanel {
361
431
  lines.push(this.padLine(fg(this.t.muted, `… ${actions.length - end} more below`), innerW));
362
432
  }
363
433
  lines.push(this.padLine("", innerW));
364
- const preview = this.previewOrError(width => this.getActionPreview(this.getSelectedAction(), width), innerW);
434
+ const preview = this.previewOrError((width) => this.getActionPreview(this.getSelectedAction(), width), innerW);
365
435
  for (const line of preview) {
366
436
  lines.push(this.padLine(line, innerW));
367
437
  }
368
438
  lines.push(this.padLine("", innerW));
369
- const hint = compact ? "Enter select · Esc back" : "Enter selects, Esc goes back, Ctrl+C closes.";
439
+ const hint = compact
440
+ ? "Enter select · Esc back"
441
+ : "Enter selects, Esc goes back, Ctrl+C closes.";
370
442
  lines.push(this.padLine(fg(this.t.muted, hint), innerW));
371
443
  return lines;
372
444
  }
@@ -376,15 +448,18 @@ export class McpSetupPanel {
376
448
  lines.push(this.padLine("", innerW));
377
449
  for (let index = 0; index < this.discovery.imports.length; index++) {
378
450
  const entry = this.discovery.imports[index];
379
- if (!entry)
451
+ if (!entry) {
380
452
  continue;
453
+ }
381
454
  const selected = this.selectedImports.has(entry.kind) ? "[x]" : "[ ]";
382
455
  const cursor = index === this.importCursor ? fg(this.t.selected, "›") : " ";
383
456
  lines.push(this.padLine(`${cursor} ${selected} ${entry.kind} ${entry.path}`, innerW));
384
457
  }
385
458
  lines.push(this.padLine("", innerW));
386
- const selected = this.discovery.imports.filter((entry) => this.selectedImports.has(entry.kind)).map((entry) => entry.kind);
387
- for (const line of this.previewOrError(width => this.formatWritePreview("Compatibility import write preview", this.callbacks.previewImports(selected), [], width), innerW)) {
459
+ const selected = this.discovery.imports
460
+ .filter((entry) => this.selectedImports.has(entry.kind))
461
+ .map((entry) => entry.kind);
462
+ for (const line of this.previewOrError((width) => this.formatWritePreview("Compatibility import write preview", this.callbacks.previewImports(selected), [], width), innerW)) {
388
463
  lines.push(this.padLine(line, innerW));
389
464
  }
390
465
  return lines;
@@ -397,8 +472,9 @@ export class McpSetupPanel {
397
472
  for (let index = 0; index < paths.length; index++) {
398
473
  const cursor = index === this.pathCursor ? fg(this.t.selected, "›") : " ";
399
474
  const path = paths[index];
400
- if (path !== undefined)
475
+ if (path !== undefined) {
401
476
  lines.push(this.padLine(`${cursor} ${path}`, innerW));
477
+ }
402
478
  }
403
479
  return lines;
404
480
  }
@@ -408,7 +484,8 @@ export class McpSetupPanel {
408
484
  ? "No MCP servers are active right now."
409
485
  : "No MCP config is active yet.");
410
486
  }
411
- if (this.discovery.totalServerCount === 0 && (this.discovery.imports.length > 0 || !!this.discovery.repoPrompt.executablePath)) {
487
+ if (this.discovery.totalServerCount === 0 &&
488
+ (this.discovery.imports.length > 0 || Boolean(this.discovery.repoPrompt.executablePath))) {
412
489
  return fg(this.t.warning, "Pi found MCP-related setup options, but none are active in Pi yet.");
413
490
  }
414
491
  const shared = this.discovery.sources.filter((source) => source.kind === "shared" && source.serverCount > 0).length;
@@ -431,8 +508,9 @@ export class McpSetupPanel {
431
508
  return `Shared MCP files are preferred. Pi-owned files are only for compatibility imports and adapter-specific overrides.${hostNote}${conflictNote}`;
432
509
  }
433
510
  visibleActionRange(total) {
434
- if (total <= COMPACT_ACTION_ROWS)
511
+ if (total <= COMPACT_ACTION_ROWS) {
435
512
  return { start: 0, end: total };
513
+ }
436
514
  const half = Math.floor(COMPACT_ACTION_ROWS / 2);
437
515
  const start = Math.min(Math.max(0, this.actionCursor - half), Math.max(0, total - COMPACT_ACTION_ROWS));
438
516
  return { start, end: Math.min(total, start + COMPACT_ACTION_ROWS) };
@@ -450,7 +528,9 @@ export class McpSetupPanel {
450
528
  "Run setup to adopt host-specific imports, inspect detected paths, and scaffold a minimal `.mcp.json` if needed.",
451
529
  ], previewW);
452
530
  case "adopt-imports":
453
- return this.formatWritePreview("Compatibility import write preview", this.callbacks.previewImports(this.discovery.imports.filter((entry) => this.selectedImports.has(entry.kind)).map((entry) => entry.kind)), [
531
+ return this.formatWritePreview("Compatibility import write preview", this.callbacks.previewImports(this.discovery.imports
532
+ .filter((entry) => this.selectedImports.has(entry.kind))
533
+ .map((entry) => entry.kind)), [
454
534
  `Detected imports: ${this.discovery.imports.map((entry) => `${entry.kind} (${entry.serverCount} servers)`).join(", ")}`,
455
535
  "Selected imports are written into the Pi agent dir config as Pi-owned compatibility state.",
456
536
  ], previewW);
@@ -479,8 +559,10 @@ export class McpSetupPanel {
479
559
  "5. .mcp.json",
480
560
  "6. .pi/fitch-mcp-adapter/mcp.json",
481
561
  `Host discovery: ${this.discovery.hostConfigDiscovery}. Conflicts reported: ${this.discovery.conflicts.length}.`,
482
- ...this.discovery.conflicts.slice(0, 8).map((conflict) => `${conflict.serverName}: ${conflict.sources.map((source) => source.path).join(" -> ")} (winner: ${conflict.winner.path})`),
483
- "Pi writes compatibility imports and adapter-only overrides to Pi-owned files."
562
+ ...this.discovery.conflicts
563
+ .slice(0, 8)
564
+ .map((conflict) => `${conflict.serverName}: ${conflict.sources.map((source) => source.path).join(" -> ")} (winner: ${conflict.winner.path})`),
565
+ "Pi writes compatibility imports and adapter-only overrides to Pi-owned files.",
484
566
  ], previewW);
485
567
  case "open-paths":
486
568
  return this.formatPreview(this.getDetectedPaths().length > 0
@@ -500,8 +582,9 @@ export class McpSetupPanel {
500
582
  }
501
583
  case "add-known-server": {
502
584
  const preset = action.preset;
503
- if (!preset)
585
+ if (!preset) {
504
586
  return this.formatPreview(["Known server preset is unavailable."], previewW);
587
+ }
505
588
  return this.formatWritePreview(`${preset.name} write preview`, this.callbacks.previewKnownServer(preset), [preset.summary], previewW);
506
589
  }
507
590
  case "scaffold-project":
@@ -535,10 +618,13 @@ export class McpSetupPanel {
535
618
  for (const line of intro) {
536
619
  lines.push(...wrapText(line, width));
537
620
  }
538
- if (intro.length > 0)
621
+ if (intro.length > 0) {
539
622
  lines.push("");
623
+ }
540
624
  lines.push(...wrapText(`${title}: ${preview.path}`, width));
541
- lines.push(...wrapText(preview.existed ? "Existing file detected. Showing exact before/after diff." : "New file will be created. Showing exact content diff.", width));
625
+ lines.push(...wrapText(preview.existed
626
+ ? "Existing file detected. Showing exact before/after diff."
627
+ : "New file will be created. Showing exact content diff.", width));
542
628
  lines.push("");
543
629
  const diffLines = preview.diffText.split("\n");
544
630
  const maxLines = 18;
@@ -2,11 +2,13 @@ import { MCP_STATUS_EVENT, MCP_STATUS_SNAPSHOT_VERSION, } from "./types.js";
2
2
  const FAILURE_BACKOFF_MS = 60 * 1000;
3
3
  function getActiveFailureAgeSeconds(state, serverName) {
4
4
  const failedAt = state.failureTracker.get(serverName);
5
- if (!failedAt)
5
+ if (!failedAt) {
6
6
  return undefined;
7
+ }
7
8
  const ageMs = Date.now() - failedAt;
8
- if (ageMs > FAILURE_BACKOFF_MS)
9
+ if (ageMs > FAILURE_BACKOFF_MS) {
9
10
  return undefined;
11
+ }
10
12
  return Math.round(ageMs / 1000);
11
13
  }
12
14
  /** Build a sanitized snapshot without connecting or querying any MCP server. */
@@ -21,10 +23,12 @@ export function createMcpStatusSnapshot(state) {
21
23
  const disabled = definition?.disabled === true;
22
24
  const connection = disabled ? undefined : state.manager.getConnection(name);
23
25
  const metadata = disabled ? undefined : state.toolMetadata.get(name);
24
- const toolCount = metadata?.filter(tool => !tool.resourceUri).length ?? (connection?.status === "connected" ? connection.tools.length : 0);
26
+ const toolCount = metadata?.filter((tool) => !tool.resourceUri).length ??
27
+ (connection?.status === "connected" ? connection.tools.length : 0);
25
28
  const resourceCount = disabled
26
29
  ? undefined
27
- : state.resourceCounts?.get(name) ?? (connection?.status === "connected" ? connection.resources.length : undefined);
30
+ : (state.resourceCounts?.get(name) ??
31
+ (connection?.status === "connected" ? connection.resources.length : undefined));
28
32
  const failedAgoSeconds = disabled ? undefined : getActiveFailureAgeSeconds(state, name);
29
33
  let status = "not-connected";
30
34
  if (disabled) {
@@ -45,8 +49,9 @@ export function createMcpStatusSnapshot(state) {
45
49
  status = "cached";
46
50
  }
47
51
  totalTools += disabled ? 0 : toolCount;
48
- if (!disabled && resourceCount !== undefined)
52
+ if (!disabled && resourceCount !== undefined) {
49
53
  totalResources += resourceCount;
54
+ }
50
55
  servers.push({
51
56
  name,
52
57
  status,
@@ -68,8 +73,9 @@ export function createMcpStatusSnapshot(state) {
68
73
  }
69
74
  export function publishMcpStatusSnapshot(state, snapshot) {
70
75
  const events = state.statusEvents;
71
- if (!events)
76
+ if (!events) {
72
77
  return;
78
+ }
73
79
  try {
74
80
  events.emit(MCP_STATUS_EVENT, snapshot ?? createMcpStatusSnapshot(state));
75
81
  }
@@ -78,8 +84,9 @@ export function publishMcpStatusSnapshot(state, snapshot) {
78
84
  }
79
85
  }
80
86
  export function publishMcpStatusShutdown(events) {
81
- if (!events)
87
+ if (!events) {
82
88
  return;
89
+ }
83
90
  try {
84
91
  events.emit(MCP_STATUS_EVENT, {
85
92
  version: MCP_STATUS_SNAPSHOT_VERSION,
@@ -1,5 +1,4 @@
1
- import type { Transport } from "@modelcontextprotocol/client";
2
- import type { JSONRPCMessage } from "@modelcontextprotocol/client";
1
+ import type { Transport, JSONRPCMessage } from "@modelcontextprotocol/client";
3
2
  export declare const MCP_TRACE_SCHEMA_VERSION = 1;
4
3
  export declare const DEFAULT_MCP_TRACE_MAX_BYTES: number;
5
4
  export declare const DEFAULT_MCP_TRACE_MAX_EVENTS = 10000;
@@ -47,10 +46,16 @@ export interface McpTraceWriterOptions {
47
46
  }
48
47
  /** Redact strings before they can become durable trace metadata. */
49
48
  export declare function redactTraceText(value: string, maxLength?: number): string;
50
- export declare function createMcpTraceEvent(direction: McpTraceDirection, server: string, transport: McpTraceTransport, message: JSONRPCMessage, status: "sent" | "received" | "error", options?: {
49
+ export interface McpTraceEventRequest {
50
+ direction: McpTraceDirection;
51
+ server: string;
52
+ transport: McpTraceTransport;
53
+ message: JSONRPCMessage;
54
+ status: "sent" | "received" | "error";
51
55
  relatedRequestId?: unknown;
52
56
  durationMs?: number;
53
- }): McpTraceEvent;
57
+ }
58
+ export declare function createMcpTraceEvent(request: McpTraceEventRequest): McpTraceEvent;
54
59
  export declare class McpTraceWriter {
55
60
  private readonly maxBytes;
56
61
  private readonly maxEvents;
@@ -77,7 +82,7 @@ export declare class McpTraceWriter {
77
82
  }
78
83
  export declare function createMcpTraceWriter(sessionCwd: string | undefined, settings?: McpTraceSettings, randomSuffix?: string): McpTraceWriter;
79
84
  export interface McpTraceObserver {
80
- record(event: McpTraceEvent): void;
85
+ record: (event: McpTraceEvent) => void;
81
86
  }
82
87
  export declare function isMcpTraceEnabled(definition: {
83
88
  trace?: boolean;