@fitchmultz/pi-mcp-adapter 8.0.6 → 8.0.8

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 +142 -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 +28 -2
  87. package/skills/mcp-scripting/SKILL.md +6 -6
@@ -1,4 +1,4 @@
1
- import { isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.js";
1
+ import { isServerDisabled, isToolAllowed, resolveToolPrefix, } from "./types.js";
2
2
  import { executeCall, prepareMcpConnection } from "./proxy-modes.js";
3
3
  import { paginate } from "./search-ranking.js";
4
4
  import { throwIfAborted } from "./abort.js";
@@ -7,20 +7,41 @@ export async function executeResourceList(state, server, limit = 12, offset = 0,
7
7
  throwIfAborted(combineAbortSignals(state.owner?.signal, signal));
8
8
  const definition = state.config.mcpServers[server];
9
9
  if (!definition || isServerDisabled(definition)) {
10
- return { content: [{ type: "text", text: `Server "${server}" is ${definition ? "disabled" : "not configured"}.` }], details: { mode: "resources", server, error: definition ? "server_disabled" : "server_not_found" } };
10
+ return {
11
+ content: [
12
+ {
13
+ type: "text",
14
+ text: `Server "${server}" is ${definition ? "disabled" : "not configured"}.`,
15
+ },
16
+ ],
17
+ details: {
18
+ mode: "resources",
19
+ server,
20
+ error: definition ? "server_disabled" : "server_not_found",
21
+ },
22
+ };
11
23
  }
12
- if (!state.toolMetadata.has(server) || state.manager.getConnection(server)?.status === "connected") {
24
+ if (!state.toolMetadata.has(server) ||
25
+ state.manager.getConnection(server)?.status === "connected") {
13
26
  const prepared = await prepareMcpConnection(state, server, signal);
14
- if (prepared.error)
27
+ if (prepared.error) {
15
28
  return prepared.error;
29
+ }
16
30
  }
17
31
  const prefix = resolveToolPrefix(definition, state.config.settings?.toolPrefix);
18
32
  const page = paginate((state.toolMetadata.get(server) ?? [])
19
- .filter(tool => tool.resourceUri !== undefined && definition.exposeResources !== false
20
- && isToolAllowed(tool.originalName, server, prefix, definition.includeTools, definition.excludeTools))
21
- .map(tool => tool.resourceDescriptor ?? { uri: tool.resourceUri, description: tool.description }), offset, limit);
22
- return { content: [{ type: "text", text: JSON.stringify({ server, ...page }, null, 2) }], details: { mode: "resources", server, ...page } };
33
+ .filter((tool) => tool.resourceUri !== undefined &&
34
+ definition.exposeResources !== false &&
35
+ isToolAllowed(tool.originalName, server, prefix, definition.includeTools, definition.excludeTools))
36
+ .map((tool) => tool.resourceDescriptor ?? { uri: tool.resourceUri, description: tool.description }), offset, limit);
37
+ return {
38
+ content: [{ type: "text", text: JSON.stringify({ server, ...page }, null, 2) }],
39
+ details: { mode: "resources", server, ...page },
40
+ };
23
41
  }
24
42
  export async function executeResourceRead(state, server, uri, signal, identity = {}, beforeDispatch, raw = false) {
25
- return executeCall(state, uri, {}, server, undefined, signal, identity, beforeDispatch, { resourceUri: uri, raw });
43
+ return executeCall(state, uri, {}, server, undefined, signal, identity, beforeDispatch, {
44
+ resourceUri: uri,
45
+ raw,
46
+ });
26
47
  }
@@ -7,11 +7,13 @@ export function createMcpRuntimeOwner() {
7
7
  // promises through settlement, without cancelling, serializing, or replaying callback work.
8
8
  const callbacks = { beforeExecute: 0, onToolCall: 0 };
9
9
  const pendingCallback = () => {
10
- if (callbacks.beforeExecute)
10
+ if (callbacks.beforeExecute) {
11
11
  return "MCP beforeExecute callback is pending";
12
- if (callbacks.onToolCall)
12
+ }
13
+ if (callbacks.onToolCall) {
13
14
  return "MCP onToolCall callback is pending";
14
- return undefined;
15
+ }
16
+ return;
15
17
  };
16
18
  const reportCleanupFailure = (error, late) => {
17
19
  console.error(`MCP: ${late ? "late " : ""}runtime cleanup failed: ${formatTerminalError(error)}`);
@@ -30,23 +32,30 @@ export function createMcpRuntimeOwner() {
30
32
  }
31
33
  },
32
34
  isActive: () => !controller.signal.aborted,
33
- addCleanup: cleanup => {
35
+ addCleanup: (cleanup) => {
34
36
  if (controller.signal.aborted) {
35
- void Promise.resolve().then(cleanup).catch(error => reportCleanupFailure(error, true));
37
+ void Promise.resolve()
38
+ .then(cleanup)
39
+ .catch((error) => reportCleanupFailure(error, true));
36
40
  return;
37
41
  }
38
42
  cleanups.push(cleanup);
39
43
  },
40
44
  stop: (reason = "MCP extension runtime stopped") => {
41
- if (stopPromise)
45
+ if (stopPromise) {
42
46
  return stopPromise;
47
+ }
43
48
  controller.abort(new Error(reason));
44
- const pendingCleanups = cleanups.splice(0).reverse().map(cleanup => Promise.resolve().then(cleanup));
45
- stopPromise = Promise.allSettled(pendingCleanups).then(results => {
46
- const failures = results.flatMap(result => result.status === "rejected" ? [result.reason] : []);
49
+ const pendingCleanups = cleanups
50
+ .splice(0)
51
+ .reverse()
52
+ .map((cleanup) => Promise.resolve().then(cleanup));
53
+ stopPromise = Promise.allSettled(pendingCleanups).then((results) => {
54
+ const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
47
55
  const callback = pendingCallback();
48
- if (callback)
56
+ if (callback) {
49
57
  failures.push(new Error(callback));
58
+ }
50
59
  if (failures.length > 0) {
51
60
  const aggregate = new AggregateError(failures, "MCP runtime cleanup failed");
52
61
  console.error(`MCP: runtime cleanup failed: ${formatTerminalError(aggregate)}`);
@@ -55,15 +64,19 @@ export function createMcpRuntimeOwner() {
55
64
  });
56
65
  return stopPromise;
57
66
  },
58
- throwIfInactive: () => { controller.signal.throwIfAborted(); },
67
+ throwIfInactive: () => {
68
+ controller.signal.throwIfAborted();
69
+ },
59
70
  };
60
71
  }
61
72
  export function combineAbortSignals(...signals) {
62
73
  const active = signals.filter((signal) => signal !== undefined);
63
- if (active.length === 0)
74
+ if (active.length === 0) {
64
75
  return undefined;
65
- if (active.length === 1)
76
+ }
77
+ if (active.length === 1) {
66
78
  return active[0];
79
+ }
67
80
  return AbortSignal.any(active);
68
81
  }
69
82
  /** Fence session-bound UI calls after the owning extension runtime stops. */
@@ -75,17 +88,20 @@ export function createOwnedUi(ui, owner) {
75
88
  }
76
89
  const object = value;
77
90
  const existing = proxies.get(object);
78
- if (existing)
91
+ if (existing) {
79
92
  return existing;
93
+ }
80
94
  const proxy = new Proxy(object, {
81
95
  get(target, property, receiver) {
82
- if (!owner.isActive())
96
+ if (!owner.isActive()) {
83
97
  return undefined;
98
+ }
84
99
  const member = Reflect.get(target, property, receiver);
85
100
  if (typeof member === "function") {
86
101
  return (...args) => {
87
- if (!owner.isActive())
102
+ if (!owner.isActive()) {
88
103
  return undefined;
104
+ }
89
105
  return Reflect.apply(member, target, args);
90
106
  };
91
107
  }
@@ -98,7 +114,9 @@ export function createOwnedUi(ui, owner) {
98
114
  return wrap(ui);
99
115
  }
100
116
  export function isAbortError(error, signal) {
101
- if (signal?.aborted)
117
+ if (signal?.aborted) {
102
118
  return true;
103
- return error instanceof Error && (error.name === "AbortError" || error.message === "MCP extension runtime stopped");
119
+ }
120
+ return (error instanceof Error &&
121
+ (error.name === "AbortError" || error.message === "MCP extension runtime stopped"));
104
122
  }
@@ -1,7 +1,6 @@
1
1
  import type { Api, Model } from "@earendil-works/pi-ai";
2
2
  import type { ExtensionUIContext, ModelRegistry } from "@earendil-works/pi-coding-agent";
3
- import type { Client } from "@modelcontextprotocol/client";
4
- import { type CreateMessageRequest, type CreateMessageResult } from "@modelcontextprotocol/client";
3
+ import { type Client, type CreateMessageRequest, type CreateMessageResult } from "@modelcontextprotocol/client";
5
4
  export interface SamplingHandlerOptions {
6
5
  serverName: string;
7
6
  autoApprove: boolean;
@@ -1,9 +1,9 @@
1
1
  import { truncateAtWord } from "./utils.js";
2
2
  import { throwIfAborted } from "./abort.js";
3
3
  import {} from "@modelcontextprotocol/client";
4
- export function registerSamplingHandler(client, options, own = run => run()) {
4
+ export function registerSamplingHandler(client, options, own = (run) => run()) {
5
5
  // Own the handler's real promise, not the SDK caller's cancellable response facade.
6
- client.setRequestHandler("sampling/createMessage", request => own(() => handleSamplingRequest(options, request)));
6
+ client.setRequestHandler("sampling/createMessage", (request) => own(() => handleSamplingRequest(options, request)));
7
7
  }
8
8
  export async function handleSamplingRequest(options, request) {
9
9
  const params = request.params;
@@ -31,13 +31,16 @@ export async function handleSamplingRequest(options, request) {
31
31
  throwIfAborted(signal);
32
32
  const supportsTemperature = model.compat && "supportsTemperature" in model.compat
33
33
  ? model.compat.supportsTemperature !== false
34
- : !(model.id === "gpt-6-astra" && (model.api === "openai-responses" || model.api === "openai-codex-responses"));
34
+ : !(model.id === "gpt-6-astra" &&
35
+ (model.api === "openai-responses" || model.api === "openai-codex-responses"));
35
36
  const result = await options.modelRegistry.complete(model, {
36
37
  ...(params.systemPrompt !== undefined ? { systemPrompt: params.systemPrompt } : {}),
37
38
  messages,
38
39
  }, {
39
40
  maxTokens: params.maxTokens,
40
- ...(supportsTemperature && params.temperature !== undefined ? { temperature: params.temperature } : {}),
41
+ ...(supportsTemperature && params.temperature !== undefined
42
+ ? { temperature: params.temperature }
43
+ : {}),
41
44
  ...(params.metadata !== undefined ? { metadata: params.metadata } : {}),
42
45
  ...(signal ? { signal } : {}),
43
46
  });
@@ -47,7 +50,9 @@ export async function handleSamplingRequest(options, request) {
47
50
  return converted;
48
51
  }
49
52
  function formatRequestApproval(serverName, modelName, systemPrompt, messages) {
50
- const lines = [`${serverName} wants to sample ${messages.length} message${messages.length === 1 ? "" : "s"} with ${modelName}.`];
53
+ const lines = [
54
+ `${serverName} wants to sample ${messages.length} message${messages.length === 1 ? "" : "s"} with ${modelName}.`,
55
+ ];
51
56
  if (systemPrompt) {
52
57
  lines.push(`System: ${truncateAtWord(systemPrompt, 400)}`);
53
58
  }
@@ -61,27 +66,35 @@ function formatResponseApproval(serverName, response) {
61
66
  return `${serverName} will receive this response from ${response.model}:\n\n${truncateAtWord(text, 1000)}`;
62
67
  }
63
68
  function messageText(message) {
64
- if (typeof message.content === "string")
69
+ if (typeof message.content === "string") {
65
70
  return message.content;
66
- return message.content.map((block) => {
67
- if (block.type === "text")
71
+ }
72
+ return message.content
73
+ .map((block) => {
74
+ if (block.type === "text") {
68
75
  return block.text;
69
- if (block.type === "image")
76
+ }
77
+ if (block.type === "image") {
70
78
  return `[image: ${block.mimeType}]`;
71
- if (block.type === "thinking")
79
+ }
80
+ if (block.type === "thinking") {
72
81
  return "[thinking]";
73
- if (block.type === "toolCall")
82
+ }
83
+ if (block.type === "toolCall") {
74
84
  return `[tool call: ${block.name}]`;
85
+ }
75
86
  return "[content]";
76
- }).join("\n");
87
+ })
88
+ .join("\n");
77
89
  }
78
90
  async function resolveSamplingModel(options, modelPreferences) {
79
91
  const candidates = [];
80
92
  const availableModels = options.modelRegistry.getAvailable();
81
93
  for (const hint of modelPreferences?.hints ?? []) {
82
94
  const normalizedHint = hint.name?.trim().toLowerCase();
83
- if (!normalizedHint)
95
+ if (!normalizedHint) {
84
96
  continue;
97
+ }
85
98
  for (const model of availableModels) {
86
99
  const searchableNames = [`${model.provider}/${model.id}`, model.id, model.name];
87
100
  if (searchableNames.some((name) => name.toLowerCase().includes(normalizedHint))) {
@@ -90,8 +103,9 @@ async function resolveSamplingModel(options, modelPreferences) {
90
103
  }
91
104
  }
92
105
  const currentModel = options.getCurrentModel();
93
- if (currentModel)
106
+ if (currentModel) {
94
107
  addSamplingCandidate(candidates, currentModel);
108
+ }
95
109
  for (const model of availableModels) {
96
110
  addSamplingCandidate(candidates, model);
97
111
  }
@@ -101,7 +115,7 @@ async function resolveSamplingModel(options, modelPreferences) {
101
115
  throwIfAborted(signal);
102
116
  const auth = await options.modelRegistry.getApiKeyAndHeaders(model);
103
117
  throwIfAborted(signal);
104
- if (auth.ok === false) {
118
+ if (!auth.ok) {
105
119
  errors.push(`${model.provider}/${model.id}: ${auth.error}`);
106
120
  continue;
107
121
  }
@@ -118,8 +132,9 @@ function addSamplingCandidate(candidates, model) {
118
132
  }
119
133
  }
120
134
  async function confirmSampling(options, title, message) {
121
- if (options.autoApprove)
135
+ if (options.autoApprove) {
122
136
  return;
137
+ }
123
138
  if (!options.ui) {
124
139
  throw new Error("MCP sampling requires interactive approval. Set settings.samplingAutoApprove to true to allow it without UI.");
125
140
  }
@@ -169,10 +184,12 @@ function convertAssistantResult(message) {
169
184
  }
170
185
  const text = message.content
171
186
  .map((block) => {
172
- if (block.type === "text")
187
+ if (block.type === "text") {
173
188
  return block.text;
174
- if (block.type === "thinking")
189
+ }
190
+ if (block.type === "thinking") {
175
191
  return undefined;
192
+ }
176
193
  throw new Error(`MCP sampling result ${block.type} content is not supported`);
177
194
  })
178
195
  .filter((value) => value !== undefined)
@@ -189,12 +206,15 @@ function convertAssistantResult(message) {
189
206
  };
190
207
  }
191
208
  function mapStopReason(reason) {
192
- if (reason === "stop")
209
+ if (reason === "stop") {
193
210
  return "endTurn";
194
- if (reason === "length")
211
+ }
212
+ if (reason === "length") {
195
213
  return "maxTokens";
196
- if (reason === "toolUse")
214
+ }
215
+ if (reason === "toolUse") {
197
216
  return "toolUse";
217
+ }
198
218
  return reason;
199
219
  }
200
220
  function zeroUsage() {
@@ -1,5 +1,5 @@
1
1
  import type { McpExtensionState } from "./state.ts";
2
- import type { ToolMetadata } from "./types.ts";
2
+ import { type ToolMetadata } from "./types.ts";
3
3
  export declare const MAX_PAGE_SIZE = 100;
4
4
  export declare const MAX_TOOL_NAME_LENGTH = 512;
5
5
  export interface RankedToolMatch {
@@ -1,5 +1,5 @@
1
1
  import MiniSearch, {} from "minisearch";
2
- import { getServerPrefix, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.js";
2
+ import { getServerPrefix, isServerDisabled, isToolAllowed, resolveToolPrefix, } from "./types.js";
3
3
  export const MAX_PAGE_SIZE = 100;
4
4
  export const MAX_TOOL_NAME_LENGTH = 512;
5
5
  export function normalizeSearchText(value) {
@@ -9,40 +9,56 @@ export function normalizeSearchText(value) {
9
9
  .toLowerCase();
10
10
  }
11
11
  function schemaSearchText(schema) {
12
- if (!schema || typeof schema !== "object")
12
+ if (!schema || typeof schema !== "object") {
13
13
  return "";
14
- if (Array.isArray(schema))
14
+ }
15
+ if (Array.isArray(schema)) {
15
16
  return schema.map(schemaSearchText).join(" ");
16
- return Object.entries(schema).flatMap(([key, value]) => {
17
- if ((key === "description" || key === "title" || key === "const") && typeof value === "string")
17
+ }
18
+ return Object.entries(schema)
19
+ .flatMap(([key, value]) => {
20
+ if ((key === "description" || key === "title" || key === "const") &&
21
+ typeof value === "string") {
18
22
  return value;
19
- if (key === "enum" && Array.isArray(value))
20
- return value.filter(item => typeof item === "string");
21
- if (key === "properties" && value && typeof value === "object")
23
+ }
24
+ if (key === "enum" && Array.isArray(value)) {
25
+ return value.filter((item) => typeof item === "string");
26
+ }
27
+ if (key === "properties" && value && typeof value === "object") {
22
28
  return [Object.keys(value).join(" "), schemaSearchText(value)];
29
+ }
23
30
  return schemaSearchText(value);
24
- }).join(" ");
31
+ })
32
+ .join(" ");
25
33
  }
26
34
  const indexes = new WeakMap();
27
35
  function getIndex(state) {
28
36
  const cached = indexes.get(state);
29
37
  // Catalog publishers replace each server's metadata array on discovery/reconnect.
30
- if (cached && cached.metadata.size === state.toolMetadata.size
31
- && [...state.toolMetadata].every(([server, metadata]) => cached.metadata.get(server) === metadata)) {
38
+ if (cached &&
39
+ cached.metadata.size === state.toolMetadata.size &&
40
+ [...state.toolMetadata].every(([server, metadata]) => cached.metadata.get(server) === metadata)) {
32
41
  return cached.index;
33
42
  }
34
43
  const index = new MiniSearch({
35
44
  fields: ["name", "originalName", "description", "schema"],
36
45
  storeFields: ["server", "tool"],
37
- searchOptions: { fields: ["name", "description", "schema"], boost: { name: 2, description: 1, schema: 1 }, prefix: true },
46
+ searchOptions: {
47
+ fields: ["name", "description", "schema"],
48
+ boost: { name: 2, description: 1, schema: 1 },
49
+ prefix: true,
50
+ },
38
51
  });
39
52
  let id = 0;
40
53
  for (const [server, tools] of state.toolMetadata) {
41
54
  for (const tool of tools) {
42
- if (tool.resourceUri !== undefined)
55
+ if (tool.resourceUri !== undefined) {
43
56
  continue;
57
+ }
44
58
  index.add({
45
- id: id++, server, tool,
59
+ id: id++,
60
+ server,
61
+ tool,
46
62
  name: normalizeSearchText(tool.name),
47
63
  originalName: normalizeSearchText(tool.originalName),
48
64
  description: normalizeSearchText([tool.title, tool.description].filter(Boolean).join(" ")),
@@ -54,18 +70,22 @@ function getIndex(state) {
54
70
  return index;
55
71
  }
56
72
  function searchCatalog(state, query, server, options) {
57
- return getIndex(state).search(normalizeSearchText(query), {
73
+ return getIndex(state)
74
+ .search(normalizeSearchText(query), {
58
75
  ...options,
59
- filter: result => {
60
- if (server && result.server !== server)
76
+ filter: (result) => {
77
+ if (server && result.server !== server) {
61
78
  return false;
79
+ }
62
80
  const definition = state.config.mcpServers[result.server];
63
- if (!definition || isServerDisabled(definition))
81
+ if (!definition || isServerDisabled(definition)) {
64
82
  return false;
83
+ }
65
84
  const prefix = resolveToolPrefix(definition, state.config.settings?.toolPrefix);
66
85
  return isToolAllowed(result.tool.originalName, result.server, prefix, definition.includeTools, definition.excludeTools);
67
86
  },
68
- }).map(({ server, tool, score }) => ({ server, tool, score }))
87
+ })
88
+ .map(({ server, tool, score }) => ({ server, tool, score }))
69
89
  .sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name));
70
90
  }
71
91
  export function rankToolMatches(state, query, server) {
@@ -73,11 +93,13 @@ export function rankToolMatches(state, query, server) {
73
93
  const identity = query.trim();
74
94
  const exact = ({ tool }) => tool.name === identity || (Boolean(server) && tool.originalName === identity);
75
95
  // Stable partition retains MiniSearch order and scores within both groups.
76
- return [...matches.filter(exact), ...matches.filter(match => !exact(match))];
96
+ return [...matches.filter(exact), ...matches.filter((match) => !exact(match))];
77
97
  }
78
98
  export function paginate(items, offset, limit) {
79
99
  const safeOffset = Number.isFinite(offset) ? Math.max(0, Math.trunc(offset)) : 0;
80
- const safeLimit = Number.isFinite(limit) ? Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit))) : 1;
100
+ const safeLimit = Number.isFinite(limit)
101
+ ? Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)))
102
+ : 1;
81
103
  const total = items.length;
82
104
  const page = items.slice(safeOffset, safeOffset + safeLimit);
83
105
  const nextOffset = safeOffset + page.length;
@@ -89,25 +111,36 @@ export function paginate(items, offset, limit) {
89
111
  };
90
112
  }
91
113
  export function rankSuggestions(state, name, limit, server) {
92
- if (name.length > MAX_TOOL_NAME_LENGTH)
114
+ if (name.length > MAX_TOOL_NAME_LENGTH) {
93
115
  return [];
116
+ }
94
117
  const globalPrefix = state.config.settings?.toolPrefix ?? "server";
95
- const inferred = server ? undefined : Object.entries(state.config.mcpServers)
96
- .filter(([, definition]) => !isServerDisabled(definition))
97
- .map(([candidateServer, definition]) => ({
98
- server: candidateServer,
99
- prefix: getServerPrefix(candidateServer, resolveToolPrefix(definition, globalPrefix)),
100
- }))
101
- .filter((candidate) => Boolean(candidate.prefix) && name.startsWith(`${candidate.prefix}_`))
102
- .sort((a, b) => b.prefix.length - a.prefix.length)[0];
118
+ const inferred = server
119
+ ? undefined
120
+ : Object.entries(state.config.mcpServers)
121
+ .filter(([, definition]) => !isServerDisabled(definition))
122
+ .map(([candidateServer, definition]) => ({
123
+ server: candidateServer,
124
+ prefix: getServerPrefix(candidateServer, resolveToolPrefix(definition, globalPrefix)),
125
+ }))
126
+ .filter((candidate) => Boolean(candidate.prefix) && name.startsWith(`${candidate.prefix}_`))
127
+ .sort((a, b) => b.prefix.length - a.prefix.length)[0];
103
128
  const matchedServer = server ?? inferred?.server;
104
129
  const definition = matchedServer ? state.config.mcpServers[matchedServer] : undefined;
105
- if (matchedServer && (!definition || isServerDisabled(definition)))
130
+ if (matchedServer && (!definition || isServerDisabled(definition))) {
106
131
  return [];
107
- const prefix = inferred?.prefix ?? (matchedServer ? getServerPrefix(matchedServer, resolveToolPrefix(definition, globalPrefix)) : "");
132
+ }
133
+ const prefix = inferred?.prefix ??
134
+ (matchedServer
135
+ ? getServerPrefix(matchedServer, resolveToolPrefix(definition, globalPrefix))
136
+ : "");
108
137
  const query = prefix && name.startsWith(`${prefix}_`) ? name.slice(prefix.length + 1) : name;
109
138
  const suggestions = searchCatalog(state, query, matchedServer, {
110
- fields: [matchedServer ? "originalName" : "name"], combineWith: "AND", fuzzy: 2,
111
- }).slice(0, limit).map(match => match.tool.name);
139
+ fields: [matchedServer ? "originalName" : "name"],
140
+ combineWith: "AND",
141
+ fuzzy: 2,
142
+ })
143
+ .slice(0, limit)
144
+ .map((match) => match.tool.name);
112
145
  return suggestions.length > 0 || !server ? suggestions : rankSuggestions(state, name, limit);
113
146
  }
@@ -23,24 +23,24 @@ export interface ServerConnection {
23
23
  type UiStreamListener = (serverName: string, notification: ServerStreamResultPatchNotification["params"]) => void;
24
24
  type MetadataListChangedListener = (serverName: string, reason: string) => void;
25
25
  export declare class McpServerManager {
26
- private ownedClients;
27
- private connections;
28
- private connectPromises;
29
- private reconnectPromises;
30
- private retiredConnections;
31
- private disposePromises;
32
- private uiStreamListeners;
26
+ private readonly ownedClients;
27
+ private readonly connections;
28
+ private readonly connectPromises;
29
+ private readonly reconnectPromises;
30
+ private readonly retiredConnections;
31
+ private readonly disposePromises;
32
+ private readonly uiStreamListeners;
33
33
  private samplingConfig;
34
34
  private metadataListChangedListener;
35
35
  private elicitationConfig;
36
36
  private authStorageOptions;
37
37
  private oauthRuntime;
38
- private acceptedUrlElicitations;
38
+ private readonly acceptedUrlElicitations;
39
39
  private defaultRequestTimeoutMs;
40
40
  private runtimeSignal;
41
- private closePromises;
42
- private closeGenerations;
43
- private connectAttempts;
41
+ private readonly closePromises;
42
+ private readonly closeGenerations;
43
+ private readonly connectAttempts;
44
44
  private traceSettings;
45
45
  private traceWriter;
46
46
  private stopped;