@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
@@ -115,10 +115,12 @@ function handleRequest(req, res) {
115
115
  }
116
116
  try {
117
117
  const validate = pending?.validate ?? reserved?.validate;
118
- if (validate)
118
+ if (validate) {
119
119
  validate({ ...(iss !== null ? { iss } : {}), ...(error ? { error } : {}) });
120
- else if (error)
120
+ }
121
+ else if (error) {
121
122
  throw new Error("Cannot verify the OAuth error callback issuer");
123
+ }
122
124
  }
123
125
  catch (failure) {
124
126
  const message = failure instanceof Error ? failure.message : "Invalid OAuth callback";
@@ -147,7 +149,10 @@ function handleRequest(req, res) {
147
149
  }
148
150
  else if (reserved) {
149
151
  // Keep provider-controlled callback payloads out of tool output.
150
- reserved.result = { error: "OAuth authorization was denied or failed. Finish authorization in the browser, then retry auth-complete.", ...(iss !== null ? { iss } : {}) };
152
+ reserved.result = {
153
+ error: "OAuth authorization was denied or failed. Finish authorization in the browser, then retry auth-complete.",
154
+ ...(iss !== null ? { iss } : {}),
155
+ };
151
156
  }
152
157
  return;
153
158
  }
@@ -158,8 +163,9 @@ function handleRequest(req, res) {
158
163
  return;
159
164
  }
160
165
  if (!pending) {
161
- if (reserved)
166
+ if (reserved) {
162
167
  reserved.result = { code, ...(iss !== null ? { iss } : {}) };
168
+ }
163
169
  res.writeHead(200, { "Content-Type": "text/html" });
164
170
  res.end(HTML_MANUAL_SUCCESS);
165
171
  return;
@@ -206,7 +212,9 @@ async function ensureCallbackServerLocked(options = {}) {
206
212
  const strictPort = options.strictPort === true;
207
213
  const requestedHost = options.callbackHost ?? DEFAULT_OAUTH_CALLBACK_HOST;
208
214
  const rawRequestedPath = options.callbackPath ?? DEFAULT_OAUTH_CALLBACK_PATH;
209
- const requestedPath = rawRequestedPath.startsWith("/") ? rawRequestedPath : `/${rawRequestedPath}`;
215
+ const requestedPath = rawRequestedPath.startsWith("/")
216
+ ? rawRequestedPath
217
+ : `/${rawRequestedPath}`;
210
218
  if (options.reserveState && !options.oauthState) {
211
219
  throw new Error("OAuth callback reservation requires an oauthState");
212
220
  }
@@ -289,8 +297,9 @@ export function releaseCallbackServer(oauthState) {
289
297
  export function takeCallbackResult(oauthState) {
290
298
  const reserved = reservedAuthStates.get(oauthState);
291
299
  const result = reserved?.result;
292
- if (reserved)
300
+ if (reserved) {
293
301
  delete reserved.result;
302
+ }
294
303
  return result;
295
304
  }
296
305
  /**
@@ -333,8 +342,9 @@ export function cancelPendingCallback(oauthState) {
333
342
  * Stop the callback server and reject all pending authorizations.
334
343
  */
335
344
  export function stopCallbackServer() {
336
- if (stoppingPromise)
345
+ if (stoppingPromise) {
337
346
  return stoppingPromise;
347
+ }
338
348
  callbackGeneration += 1;
339
349
  const cleanup = (async () => {
340
350
  while (bindingPromise) {
@@ -363,8 +373,9 @@ export function stopCallbackServer() {
363
373
  }, 0);
364
374
  })();
365
375
  const operation = cleanup.finally(() => {
366
- if (stoppingPromise === operation)
376
+ if (stoppingPromise === operation) {
367
377
  stoppingPromise = undefined;
378
+ }
368
379
  });
369
380
  stoppingPromise = operation;
370
381
  return operation;
package/dist/mcp-code.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { formatWithOptions } from "node:util";
2
2
  import { Worker } from "node:worker_threads";
3
- import { guardedMcpDetails, resolveMcpOutputGuardOptions, readMcpResult, formatMcpResultReference, formatMcpPayloadFile } from "./mcp-output-guard.js";
3
+ import { guardedMcpDetails, resolveMcpOutputGuardOptions, readMcpResult, formatMcpResultReference, formatMcpPayloadFile, } from "./mcp-output-guard.js";
4
4
  import { createMcpOutputSpool } from "./mcp-output-preview.js";
5
5
  import { executeCall, executeConnect } from "./proxy-modes.js";
6
6
  import { executeResourceList, executeResourceRead } from "./resource-tools.js";
@@ -18,14 +18,24 @@ class McpScriptTimeoutError extends Error {
18
18
  }
19
19
  }
20
20
  function needsInspectableFormatting(value, stack = new WeakSet()) {
21
- if (value === undefined || typeof value === "bigint" || typeof value === "function" || typeof value === "symbol")
21
+ if (value === undefined ||
22
+ typeof value === "bigint" ||
23
+ typeof value === "function" ||
24
+ typeof value === "symbol") {
22
25
  return true;
23
- if (typeof value !== "object" || value === null)
26
+ }
27
+ if (typeof value !== "object" || value === null) {
24
28
  return false;
25
- if (stack.has(value))
29
+ }
30
+ if (stack.has(value)) {
26
31
  return true;
27
- if (value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet)
32
+ }
33
+ if (value instanceof Map ||
34
+ value instanceof Set ||
35
+ value instanceof WeakMap ||
36
+ value instanceof WeakSet) {
28
37
  return true;
38
+ }
29
39
  stack.add(value);
30
40
  try {
31
41
  return Object.values(value).some((entry) => needsInspectableFormatting(entry, stack));
@@ -35,13 +45,15 @@ function needsInspectableFormatting(value, stack = new WeakSet()) {
35
45
  }
36
46
  }
37
47
  function formatValue(value) {
38
- if (typeof value === "string")
48
+ if (typeof value === "string") {
39
49
  return value;
50
+ }
40
51
  try {
41
52
  if (!needsInspectableFormatting(value)) {
42
53
  const json = JSON.stringify(value, null, 2);
43
- if (json !== undefined)
54
+ if (json !== undefined) {
44
55
  return json;
56
+ }
45
57
  }
46
58
  return formatWithOptions({ colors: false, depth: 6 }, value);
47
59
  }
@@ -55,7 +67,9 @@ function toContentBlock(value) {
55
67
  if (block.type === "text" && typeof block.text === "string") {
56
68
  return { type: "text", text: block.text };
57
69
  }
58
- if (block.type === "image" && typeof block.data === "string" && typeof block.mimeType === "string") {
70
+ if (block.type === "image" &&
71
+ typeof block.data === "string" &&
72
+ typeof block.mimeType === "string") {
59
73
  return { type: "image", data: block.data, mimeType: block.mimeType };
60
74
  }
61
75
  }
@@ -71,22 +85,43 @@ function abortReasonError(reason) {
71
85
  return reason instanceof Error ? reason : new Error(String(reason ?? "MCP request aborted"));
72
86
  }
73
87
  function parseWorkerMessage(value) {
74
- if (typeof value !== "object" || value === null)
88
+ if (typeof value !== "object" || value === null) {
75
89
  return null;
90
+ }
76
91
  const message = value;
77
92
  // An older worker file, such as one restored by a rollback under a running Pi, neither charges the window nor sends a size.
78
- if (message.type === "emit" && "block" in message)
79
- return { type: "emit", block: message.block, size: typeof message.size === "number" ? message.size : 0 };
80
- if (message.type === "call" && typeof message.id === "number" && typeof message.path === "string") {
81
- return { type: "call", id: message.id, path: message.path, ...(message.args !== undefined ? { args: message.args } : {}), ...(typeof message.server === "string" ? { server: message.server } : {}) };
93
+ if (message.type === "emit" && "block" in message) {
94
+ return {
95
+ type: "emit",
96
+ block: message.block,
97
+ size: typeof message.size === "number" ? message.size : 0,
98
+ };
82
99
  }
83
- if ((message.type === "search" || message.type === "describe" || message.type === "resources" || message.type === "readResource" || message.type === "readResult") && typeof message.id === "number") {
100
+ if (message.type === "call" &&
101
+ typeof message.id === "number" &&
102
+ typeof message.path === "string") {
103
+ return {
104
+ type: "call",
105
+ id: message.id,
106
+ path: message.path,
107
+ ...(message.args !== undefined ? { args: message.args } : {}),
108
+ ...(typeof message.server === "string" ? { server: message.server } : {}),
109
+ };
110
+ }
111
+ if ((message.type === "search" ||
112
+ message.type === "describe" ||
113
+ message.type === "resources" ||
114
+ message.type === "readResource" ||
115
+ message.type === "readResult") &&
116
+ typeof message.id === "number") {
84
117
  return "input" in message
85
118
  ? { type: message.type, id: message.id, input: message.input }
86
119
  : { type: message.type, id: message.id };
87
120
  }
88
121
  if (message.type === "done") {
89
- return "returnBlock" in message ? { type: "done", returnBlock: message.returnBlock } : { type: "done" };
122
+ return "returnBlock" in message
123
+ ? { type: "done", returnBlock: message.returnBlock }
124
+ : { type: "done" };
90
125
  }
91
126
  if (message.type === "error" && typeof message.message === "string") {
92
127
  return { type: "error", message: message.message };
@@ -111,9 +146,11 @@ export const runMcpScript = async (...args) => {
111
146
  }
112
147
  };
113
148
  async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT_TIMEOUT_MS, getPiTools, signal, toolCallId, beforeDispatch) {
114
- const resolvedTimeoutMs = timeoutMs === null ? null : Number.isFinite(timeoutMs) && timeoutMs > 0
115
- ? Math.floor(timeoutMs)
116
- : DEFAULT_MCP_SCRIPT_TIMEOUT_MS;
149
+ const resolvedTimeoutMs = timeoutMs === null
150
+ ? null
151
+ : Number.isFinite(timeoutMs) && timeoutMs > 0
152
+ ? Math.floor(timeoutMs)
153
+ : DEFAULT_MCP_SCRIPT_TIMEOUT_MS;
117
154
  const output = createMcpOutputSpool(resolveMcpOutputGuardOptions(state.config.settings, state.outputDirectory));
118
155
  const externalSignal = combineAbortSignals(state.owner?.signal, signal);
119
156
  const timeoutController = new AbortController();
@@ -131,15 +168,25 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
131
168
  const recordCall = async (path, dispatch) => {
132
169
  // Record before dispatch so calls still in flight at timeout/abort appear in the trace.
133
170
  const startedAt = Date.now();
134
- const index = calls.push({ operation: "call", path, ok: false, error: "incomplete", durationMs: 0, startedAt }) - 1;
171
+ const index = calls.push({
172
+ operation: "call",
173
+ path,
174
+ ok: false,
175
+ error: "incomplete",
176
+ durationMs: 0,
177
+ startedAt,
178
+ }) - 1;
135
179
  const result = await dispatch();
136
180
  const details = result.details;
137
- if (typeof details.resultRef === "string")
181
+ if (typeof details.resultRef === "string") {
138
182
  resultRefs.add(details.resultRef);
139
- if (typeof details.resultWriteError === "string")
183
+ }
184
+ if (typeof details.resultWriteError === "string") {
140
185
  payloadNotices.add(`[MCP result could not be saved: ${details.resultWriteError}]`);
141
- for (const file of (details.payloadFiles ?? []))
186
+ }
187
+ for (const file of (details.payloadFiles ?? [])) {
142
188
  payloadNotices.add(formatMcpPayloadFile(file));
189
+ }
143
190
  if (details.error !== undefined) {
144
191
  const errorCode = String(details.error);
145
192
  const suggestions = Array.isArray(details.suggestions)
@@ -151,38 +198,63 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
151
198
  ? details.message
152
199
  : textFromContent(result.content);
153
200
  calls[index] = {
154
- operation: "call", path, ok: false, error: errorCode, durationMs: Date.now() - startedAt, startedAt,
201
+ operation: "call",
202
+ path,
203
+ ok: false,
204
+ error: errorCode,
205
+ durationMs: Date.now() - startedAt,
206
+ startedAt,
155
207
  ...(details.recovery !== undefined ? { recovery: details.recovery } : {}),
156
208
  };
157
- if (errorCode === "call_capture_failed")
209
+ if (errorCode === "call_capture_failed") {
158
210
  throw new McpScriptCaptureError(details.recovery, message);
159
- if (details.recovery !== undefined)
211
+ }
212
+ if (details.recovery !== undefined) {
160
213
  output.push({ type: "text", text: message });
214
+ }
161
215
  return {
162
216
  ok: false,
163
217
  ...(details.mcpResult !== undefined ? { data: details.mcpResult } : {}),
164
218
  ...(details.resultRef ? { resultRef: details.resultRef } : {}),
165
- error: { code: errorCode, message, ...(details.recovery ? { recovery: details.recovery } : {}) },
219
+ error: {
220
+ code: errorCode,
221
+ message,
222
+ ...(details.recovery ? { recovery: details.recovery } : {}),
223
+ },
166
224
  };
167
225
  }
168
- calls[index] = { operation: "call", path, ok: true, durationMs: Date.now() - startedAt, startedAt };
226
+ calls[index] = {
227
+ operation: "call",
228
+ path,
229
+ ok: true,
230
+ durationMs: Date.now() - startedAt,
231
+ startedAt,
232
+ };
169
233
  return {
170
234
  ok: true,
171
235
  data: details.mcpResult !== undefined ? details.mcpResult : textFromContent(result.content),
172
236
  ...(details.resultRef ? { resultRef: details.resultRef } : {}),
173
237
  };
174
238
  };
175
- const callIdentity = (innerCallId) => ({ ...(toolCallId !== undefined ? { toolCallId } : {}), innerCallId });
239
+ const callIdentity = (innerCallId) => ({
240
+ ...(toolCallId !== undefined ? { toolCallId } : {}),
241
+ innerCallId,
242
+ });
176
243
  const callTool = (innerCallId, path, args, server) => recordCall(path, () => executeCall(state, path, args, server, getPiTools, callSignal, callIdentity(innerCallId), beforeDispatch, { raw: true }));
177
244
  const discoverServer = async (server) => {
178
- if (!server)
245
+ if (!server) {
179
246
  return undefined;
180
- if (isServerDisabled(state.config.mcpServers[server]))
247
+ }
248
+ if (isServerDisabled(state.config.mcpServers[server])) {
181
249
  return { code: "server_disabled", message: `Server "${server}" is disabled.` };
182
- if (state.config.mcpServers[server] && state.toolMetadata.has(server))
250
+ }
251
+ if (state.config.mcpServers[server] && state.toolMetadata.has(server)) {
183
252
  return undefined;
253
+ }
184
254
  const result = await executeConnect(state, server, callSignal);
185
- return result.details.error ? { code: result.details.error, message: textFromContent(result.content) } : undefined;
255
+ return result.details.error
256
+ ? { code: result.details.error, message: textFromContent(result.content) }
257
+ : undefined;
186
258
  };
187
259
  const searchTools = async (input) => {
188
260
  const startedAt = Date.now();
@@ -196,13 +268,15 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
196
268
  error = discoveryError.code;
197
269
  return { error: discoveryError, coverage };
198
270
  }
199
- if (query.trim() === "")
271
+ if (query.trim() === "") {
200
272
  return { items: [], total: 0, hasMore: false, nextOffset: null, coverage };
273
+ }
201
274
  const limit = typeof input?.limit === "number" ? input.limit : 12;
202
275
  const offset = typeof input?.offset === "number" ? input.offset : 0;
203
- const page = paginate(rankToolMatches(state, query, server).filter(match => !match.tool.resourceUri), offset, limit);
276
+ const page = paginate(rankToolMatches(state, query, server).filter((match) => !match.tool.resourceUri), offset, limit);
204
277
  return {
205
- ...page, coverage,
278
+ ...page,
279
+ coverage,
206
280
  items: page.items.map(({ server: matchServer, tool, score }) => ({
207
281
  path: tool.name,
208
282
  name: tool.originalName,
@@ -219,7 +293,14 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
219
293
  finally {
220
294
  calls.push(error === undefined
221
295
  ? { operation: "search", query, ok: true, durationMs: Date.now() - startedAt, startedAt }
222
- : { operation: "search", query, ok: false, error: error instanceof Error ? error.message : String(error), durationMs: Date.now() - startedAt, startedAt });
296
+ : {
297
+ operation: "search",
298
+ query,
299
+ ok: false,
300
+ error: error instanceof Error ? error.message : String(error),
301
+ durationMs: Date.now() - startedAt,
302
+ startedAt,
303
+ });
223
304
  }
224
305
  };
225
306
  const describeTool = async (input) => {
@@ -234,17 +315,24 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
234
315
  return { path, error: discoveryError };
235
316
  }
236
317
  const matches = [...state.toolMetadata].flatMap(([server, metadata]) => {
237
- if ((selectedServer && server !== selectedServer) || !state.config.mcpServers[server] || isServerDisabled(state.config.mcpServers[server]))
318
+ if ((selectedServer && server !== selectedServer) ||
319
+ !state.config.mcpServers[server] ||
320
+ isServerDisabled(state.config.mcpServers[server])) {
238
321
  return [];
239
- const tools = metadata.filter(tool => !tool.resourceUri);
240
- const originals = selectedServer ? tools.filter(tool => tool.originalName === path) : [];
241
- return (originals.length ? originals : tools.filter(tool => tool.name === path)).map(tool => toToolDescriptor(server, tool));
322
+ }
323
+ const tools = metadata.filter((tool) => !tool.resourceUri);
324
+ const originals = selectedServer ? tools.filter((tool) => tool.originalName === path) : [];
325
+ return (originals.length ? originals : tools.filter((tool) => tool.name === path)).map((tool) => toToolDescriptor(server, tool));
242
326
  });
243
- if (matches.length === 1)
327
+ if (matches.length === 1) {
244
328
  return matches[0];
329
+ }
245
330
  if (matches.length > 1) {
246
331
  error = "ambiguous_tool";
247
- return { path, error: { code: "ambiguous_tool", message: "Specify server and exact tool name." } };
332
+ return {
333
+ path,
334
+ error: { code: "ambiguous_tool", message: "Specify server and exact tool name." },
335
+ };
248
336
  }
249
337
  const suggestions = path ? rankSuggestions(state, path, 5, selectedServer) : [];
250
338
  error = "tool_not_found";
@@ -264,7 +352,14 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
264
352
  finally {
265
353
  calls.push(error === undefined
266
354
  ? { operation: "describe", path, ok: true, durationMs: Date.now() - startedAt, startedAt }
267
- : { operation: "describe", path, ok: false, error: error instanceof Error ? error.message : String(error), durationMs: Date.now() - startedAt, startedAt });
355
+ : {
356
+ operation: "describe",
357
+ path,
358
+ ok: false,
359
+ error: error instanceof Error ? error.message : String(error),
360
+ durationMs: Date.now() - startedAt,
361
+ startedAt,
362
+ });
268
363
  }
269
364
  };
270
365
  let worker;
@@ -287,19 +382,22 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
287
382
  let completed = false;
288
383
  activeWorker.on("message", (value) => {
289
384
  const message = parseWorkerMessage(value);
290
- if (!message || completed)
385
+ if (!message || completed) {
291
386
  return;
387
+ }
292
388
  if (message.type === "emit") {
293
389
  output.push(toContentBlock(message.block));
294
390
  const queued = Atomics.sub(outputFlow, 0, message.size) - message.size;
295
- if (queued <= SCRIPT_OUTPUT_WINDOW / 2)
391
+ if (queued <= SCRIPT_OUTPUT_WINDOW / 2) {
296
392
  Atomics.notify(outputFlow, 0);
393
+ }
297
394
  return;
298
395
  }
299
396
  if (message.type === "done") {
300
397
  completed = true;
301
- if ("returnBlock" in message)
398
+ if ("returnBlock" in message) {
302
399
  output.push(toContentBlock(message.returnBlock));
400
+ }
303
401
  resolve();
304
402
  return;
305
403
  }
@@ -324,12 +422,23 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
324
422
  }
325
423
  else {
326
424
  const input = message.input;
327
- if (typeof input?.server !== "string" || !input.server || (message.type === "readResource" && (typeof input.uri !== "string" || !input.uri))) {
328
- envelope = { error: { code: "invalid_arguments", message: "Specify server and, for reads, resource uri." } };
425
+ if (typeof input?.server !== "string" ||
426
+ !input.server ||
427
+ (message.type === "readResource" && (typeof input.uri !== "string" || !input.uri))) {
428
+ envelope = {
429
+ error: {
430
+ code: "invalid_arguments",
431
+ message: "Specify server and, for reads, resource uri.",
432
+ },
433
+ };
329
434
  }
330
435
  else if (message.type === "resources") {
331
436
  const result = await executeResourceList(state, input.server, typeof input.limit === "number" ? input.limit : 12, typeof input.offset === "number" ? input.offset : 0, callSignal);
332
- envelope = result.details.error ? { error: { code: result.details.error, message: textFromContent(result.content) } } : result.details;
437
+ envelope = result.details.error
438
+ ? {
439
+ error: { code: result.details.error, message: textFromContent(result.content) },
440
+ }
441
+ : result.details;
333
442
  }
334
443
  else {
335
444
  const server = input.server;
@@ -345,13 +454,15 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
345
454
  });
346
455
  activeWorker.once("error", reject);
347
456
  activeWorker.once("exit", (code) => {
348
- if (!completed && code !== 0)
457
+ if (!completed && code !== 0) {
349
458
  reject(new Error(`mcp_script worker exited with code ${code}`));
459
+ }
350
460
  });
351
461
  });
352
462
  const timeout = new Promise((_resolve, reject) => {
353
- if (resolvedTimeoutMs === null)
463
+ if (resolvedTimeoutMs === null) {
354
464
  return;
465
+ }
355
466
  const timeoutError = new McpScriptTimeoutError(resolvedTimeoutMs);
356
467
  timer = setTimeout(() => {
357
468
  timeoutController.abort(timeoutError);
@@ -403,12 +514,15 @@ async function runMcpScriptOperation(state, code, timeoutMs = DEFAULT_MCP_SCRIPT
403
514
  await worker?.terminate();
404
515
  }
405
516
  const callsSnapshot = snapshotCalls();
406
- if (output.empty)
517
+ if (output.empty) {
407
518
  output.push({ type: "text", text: "(no output)" });
408
- for (const text of payloadNotices)
519
+ }
520
+ for (const text of payloadNotices) {
409
521
  output.push({ type: "text", text });
410
- for (const ref of resultRefs)
522
+ }
523
+ for (const ref of resultRefs) {
411
524
  output.push({ type: "text", text: formatMcpResultReference(ref) });
525
+ }
412
526
  const guarded = output.finish();
413
527
  return {
414
528
  content: guarded.content,
@@ -1,14 +1,26 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
2
+ "use strict";
3
3
 
4
- const { createRequire } = require('node:module');
5
- const { dirname, join } = require('node:path');
4
+ const { createRequire } = require("node:module");
5
+ const { dirname, join } = require("node:path");
6
6
 
7
7
  const requireFromHere = createRequire(__filename);
8
8
 
9
+ /** @param {unknown} error @returns {unknown} */
10
+ function errorMessage(error) {
11
+ if (
12
+ error !== null &&
13
+ (typeof error === "object" || typeof error === "function") &&
14
+ "message" in error
15
+ ) {
16
+ return error.message;
17
+ }
18
+ return undefined;
19
+ }
20
+
9
21
  function loadKeyringEntryClass() {
10
22
  try {
11
- return requireFromHere('@napi-rs/keyring').Entry;
23
+ return requireFromHere("@napi-rs/keyring").Entry;
12
24
  } catch (loaderError) {
13
25
  const suffixes = getNativeBindingSuffixes(process.platform, process.arch);
14
26
  let lastError;
@@ -20,38 +32,50 @@ function loadKeyringEntryClass() {
20
32
  lastError = error;
21
33
  }
22
34
  }
23
- const error = new Error(`Failed to load @napi-rs/keyring in recovery helper: ${lastError?.message ?? loaderError.message}`);
35
+ const error = new Error(
36
+ `Failed to load @napi-rs/keyring in recovery helper: ${errorMessage(lastError) ?? errorMessage(loaderError)}`,
37
+ );
24
38
  error.cause = loaderError;
25
39
  throw error;
26
40
  }
27
41
  }
28
42
 
43
+ /** @param {string} platform @param {string} arch */
29
44
  function getNativeBindingSuffixes(platform, arch) {
30
- if (platform === 'linux') {
31
- if (arch === 'arm64') return ['linux-arm64-gnu', 'linux-arm64-musl'];
32
- if (arch === 'arm') return ['linux-arm-gnueabihf'];
33
- if (arch === 'riscv64') return ['linux-riscv64-gnu'];
34
- if (arch === 'x64') return ['linux-x64-gnu', 'linux-x64-musl'];
45
+ if (platform === "linux") {
46
+ if (arch === "arm64") {
47
+ return ["linux-arm64-gnu", "linux-arm64-musl"];
48
+ }
49
+ if (arch === "arm") {
50
+ return ["linux-arm-gnueabihf"];
51
+ }
52
+ if (arch === "riscv64") {
53
+ return ["linux-riscv64-gnu"];
54
+ }
55
+ if (arch === "x64") {
56
+ return ["linux-x64-gnu", "linux-x64-musl"];
57
+ }
35
58
  }
36
59
  return [];
37
60
  }
38
61
 
39
62
  function readStdin() {
40
63
  return new Promise((resolve, reject) => {
41
- let input = '';
42
- process.stdin.setEncoding('utf8');
43
- process.stdin.on('data', chunk => {
64
+ let input = "";
65
+ process.stdin.setEncoding("utf8");
66
+ process.stdin.on("data", (chunk) => {
44
67
  input += chunk;
45
68
  if (input.length > 1024 * 1024) {
46
- reject(new Error('request too large'));
69
+ reject(new Error("request too large"));
47
70
  process.stdin.destroy();
48
71
  }
49
72
  });
50
- process.stdin.on('error', reject);
51
- process.stdin.on('end', () => resolve(input));
73
+ process.stdin.on("error", reject);
74
+ process.stdin.on("end", () => resolve(input));
52
75
  });
53
76
  }
54
77
 
78
+ /** @param {unknown} response */
55
79
  function writeResponse(response) {
56
80
  process.stdout.write(`${JSON.stringify(response)}\n`);
57
81
  }
@@ -59,22 +83,32 @@ function writeResponse(response) {
59
83
  (async () => {
60
84
  try {
61
85
  const request = JSON.parse(await readStdin());
62
- if (!request || typeof request !== 'object') throw new Error('invalid request');
86
+ if (!request || typeof request !== "object") {
87
+ throw new Error("invalid request");
88
+ }
63
89
  const { operation, service, account, payload } = request;
64
- if (!['read', 'write', 'remove'].includes(operation)) throw new Error('invalid operation');
65
- if (typeof service !== 'string' || !service) throw new Error('invalid service');
66
- if (typeof account !== 'string' || !account) throw new Error('invalid account');
90
+ if (!["read", "write", "remove"].includes(operation)) {
91
+ throw new Error("invalid operation");
92
+ }
93
+ if (typeof service !== "string" || !service) {
94
+ throw new Error("invalid service");
95
+ }
96
+ if (typeof account !== "string" || !account) {
97
+ throw new Error("invalid account");
98
+ }
67
99
 
68
100
  const Entry = loadKeyringEntryClass();
69
101
  const entry = new Entry(service, account);
70
102
 
71
- if (operation === 'read') {
103
+ if (operation === "read") {
72
104
  const value = entry.getPassword();
73
105
  writeResponse(value === null ? { ok: true, found: false } : { ok: true, found: true, value });
74
106
  return;
75
107
  }
76
- if (operation === 'write') {
77
- if (typeof payload !== 'string') throw new Error('invalid payload');
108
+ if (operation === "write") {
109
+ if (typeof payload !== "string") {
110
+ throw new Error("invalid payload");
111
+ }
78
112
  entry.setPassword(payload);
79
113
  writeResponse({ ok: true });
80
114
  return;
@@ -4,8 +4,7 @@
4
4
  * Implementation of the MCP SDK's OAuthClientProvider interface.
5
5
  * Handles OAuth client registration, token storage, and authorization redirection.
6
6
  */
7
- import { type FetchLike, type AddClientAuthentication, type OAuthClientProvider, type OAuthClientInformationContext, type StoredOAuthClientInformation, type StoredOAuthTokens, type OAuthDiscoveryState } from "@modelcontextprotocol/client";
8
- import type { OAuthClientMetadata } from "@modelcontextprotocol/client";
7
+ import { type FetchLike, type AddClientAuthentication, type OAuthClientProvider, type OAuthClientInformationContext, type StoredOAuthClientInformation, type StoredOAuthTokens, type OAuthDiscoveryState, type OAuthClientMetadata } from "@modelcontextprotocol/client";
9
8
  import { type AuthStorageOptions } from "./mcp-auth.ts";
10
9
  import { type OAuthPrivateKeyJwtConfig, type OAuthCrossAppAccessConfig } from "./types.ts";
11
10
  /** Validate explicit document URLs without echoing possible credentials in errors. */
@@ -54,11 +53,11 @@ export declare class McpOAuthProvider implements OAuthClientProvider {
54
53
  private flowDiscoveryState;
55
54
  private flowIssuerMismatch;
56
55
  private flowState;
57
- private serverName;
58
- private serverUrl;
59
- private config;
60
- private callbacks;
61
- private storageOptions;
56
+ private readonly serverName;
57
+ private readonly serverUrl;
58
+ private readonly config;
59
+ private readonly callbacks;
60
+ private readonly storageOptions;
62
61
  constructor(serverName: string, serverUrl: string, config: McpOAuthConfig, callbacks: McpOAuthCallbacks, storageOptions?: AuthStorageOptions, runtimeSignal?: AbortSignal, initialState?: string);
63
62
  private get isNonInteractive();
64
63
  private get discoveredIssuer();