@fitchmultz/pi-mcp-adapter 8.0.5 → 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 (88) hide show
  1. package/CHANGELOG.md +140 -7
  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.d.ts +3 -3
  73. package/dist/tool-result-renderer.js +65 -44
  74. package/dist/types.d.ts +2 -2
  75. package/dist/types.js +39 -18
  76. package/dist/ui-message-history.js +33 -13
  77. package/dist/ui-resource-handler.d.ts +3 -3
  78. package/dist/ui-resource-handler.js +37 -21
  79. package/dist/ui-server.js +101 -43
  80. package/dist/ui-session.js +66 -29
  81. package/dist/ui-stream-types.js +14 -4
  82. package/dist/ui-tool-visibility.js +12 -6
  83. package/dist/unix-socket-transport.d.ts +1 -2
  84. package/dist/unix-socket-transport.js +19 -12
  85. package/dist/utils.d.ts +2 -0
  86. package/dist/utils.js +60 -35
  87. package/package.json +29 -5
  88. package/skills/mcp-scripting/SKILL.md +6 -6
@@ -4,14 +4,50 @@ import vm from "node:vm";
4
4
 
5
5
  const TOOLS_ENUMERATION_ERROR = "tools is not enumerable — use tools.search({ query })";
6
6
  const RESERVED_TOOL_PROPS = new Set(["then", "catch", "finally", "toJSON", "toString", "valueOf"]);
7
- const { code, outputFlow, outputWindow } = workerData;
7
+ const { code, outputFlow, outputWindow } =
8
+ /** @type {{ code: string; outputFlow: Int32Array | undefined; outputWindow: number }} */ (
9
+ workerData
10
+ );
11
+
12
+ /** @param {import("node:worker_threads").MessagePort | null} port */
13
+ function requireParentPort(port) {
14
+ if (port === null) {
15
+ throw new Error("mcp-script-worker must be started from a worker thread with a parent port");
16
+ }
17
+ return port;
18
+ }
19
+
20
+ const port = requireParentPort(parentPort);
8
21
 
9
22
  // Keep this formatting logic in sync with mcp-code.ts; the standalone worker cannot import the TypeScript host module.
23
+ /**
24
+ * @param {unknown} value
25
+ * @param {WeakSet<object>} [stack]
26
+ * @returns {boolean}
27
+ */
10
28
  function needsInspectableFormatting(value, stack = new WeakSet()) {
11
- if (value === undefined || typeof value === "bigint" || typeof value === "function" || typeof value === "symbol") return true;
12
- if (typeof value !== "object" || value === null) return false;
13
- if (stack.has(value)) return true;
14
- if (value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) return true;
29
+ if (
30
+ value === undefined ||
31
+ typeof value === "bigint" ||
32
+ typeof value === "function" ||
33
+ typeof value === "symbol"
34
+ ) {
35
+ return true;
36
+ }
37
+ if (typeof value !== "object" || value === null) {
38
+ return false;
39
+ }
40
+ if (stack.has(value)) {
41
+ return true;
42
+ }
43
+ if (
44
+ value instanceof Map ||
45
+ value instanceof Set ||
46
+ value instanceof WeakMap ||
47
+ value instanceof WeakSet
48
+ ) {
49
+ return true;
50
+ }
15
51
  stack.add(value);
16
52
  try {
17
53
  return Object.values(value).some((entry) => needsInspectableFormatting(entry, stack));
@@ -20,12 +56,17 @@ function needsInspectableFormatting(value, stack = new WeakSet()) {
20
56
  }
21
57
  }
22
58
 
59
+ /** @param {unknown} value @returns {string} */
23
60
  function formatValue(value) {
24
- if (typeof value === "string") return value;
61
+ if (typeof value === "string") {
62
+ return value;
63
+ }
25
64
  try {
26
65
  if (!needsInspectableFormatting(value)) {
27
66
  const json = JSON.stringify(value, null, 2);
28
- if (json !== undefined) return json;
67
+ if (json !== undefined) {
68
+ return json;
69
+ }
29
70
  }
30
71
  return formatWithOptions({ colors: false, depth: 6 }, value);
31
72
  } catch {
@@ -33,13 +74,22 @@ function formatValue(value) {
33
74
  }
34
75
  }
35
76
 
77
+ /**
78
+ * @param {unknown} value
79
+ * @returns {{ type: "text"; text: string } | { type: "image"; data: string; mimeType: string }}
80
+ */
36
81
  function toContentBlock(value) {
37
82
  if (typeof value === "object" && value !== null) {
38
- if (value.type === "text" && typeof value.text === "string") {
39
- return { type: "text", text: value.text };
83
+ const record = /** @type {Record<string, unknown>} */ (value);
84
+ if (record.type === "text" && typeof record.text === "string") {
85
+ return { type: "text", text: record.text };
40
86
  }
41
- if (value.type === "image" && typeof value.data === "string" && typeof value.mimeType === "string") {
42
- return { type: "image", data: value.data, mimeType: value.mimeType };
87
+ if (
88
+ record.type === "image" &&
89
+ typeof record.data === "string" &&
90
+ typeof record.mimeType === "string"
91
+ ) {
92
+ return { type: "image", data: record.data, mimeType: record.mimeType };
43
93
  }
44
94
  }
45
95
  return { type: "text", text: formatValue(value) };
@@ -48,29 +98,43 @@ function toContentBlock(value) {
48
98
  let nextRequestId = 0;
49
99
  const pending = new Map();
50
100
 
51
- parentPort.on("message", (message) => {
52
- if (message?.type !== "result" || typeof message.id !== "number") return;
101
+ port.on("message", (message) => {
102
+ if (
103
+ typeof message !== "object" ||
104
+ message === null ||
105
+ message.type !== "result" ||
106
+ typeof message.id !== "number"
107
+ ) {
108
+ return;
109
+ }
53
110
  const resolve = pending.get(message.id);
54
- if (!resolve) return;
111
+ if (!resolve) {
112
+ return;
113
+ }
55
114
  pending.delete(message.id);
56
115
  resolve(message.envelope);
57
116
  });
58
117
 
118
+ /** @param {string} type @param {{ input?: unknown; path?: string; args?: unknown; server?: string | undefined }} payload */
59
119
  function request(type, payload) {
60
120
  return new Promise((resolve) => {
61
121
  const id = ++nextRequestId;
62
122
  pending.set(id, resolve);
63
- parentPort.postMessage({ type, id, ...payload });
123
+ port.postMessage({ type, id, ...payload });
64
124
  });
65
125
  }
66
126
 
67
127
  const tools = new Proxy(Object.create(null), {
68
128
  get(_target, property) {
69
129
  if (property === "search") {
70
- return async (input) => request("search", { input });
130
+ return async (/** @type {unknown} */ input) => request("search", { input });
71
131
  }
72
132
  if (property === "call") {
73
- return async (path, args, server) => {
133
+ return async (
134
+ /** @type {string} */ path,
135
+ /** @type {unknown} */ args,
136
+ /** @type {string | undefined} */ server,
137
+ ) => {
74
138
  // Invalid paths never reach dispatch and therefore never appear in the call trace.
75
139
  if (typeof path !== "string" || path.trim() === "") {
76
140
  return {
@@ -81,62 +145,90 @@ const tools = new Proxy(Object.create(null), {
81
145
  },
82
146
  };
83
147
  }
84
- if (server !== undefined && (typeof server !== "string" || !server)) return { ok: false, error: { code: "invalid_arguments", message: "server must be a non-empty string" } };
148
+ if (server !== undefined && (typeof server !== "string" || !server)) {
149
+ return {
150
+ ok: false,
151
+ error: { code: "invalid_arguments", message: "server must be a non-empty string" },
152
+ };
153
+ }
85
154
  return request("call", { path, args, server });
86
155
  };
87
156
  }
88
- if (property === "resources" || property === "listResources") return async (input) => request("resources", { input });
89
- if (property === "readResource" || property === "readResult") return async (input) => request(property, { input });
157
+ if (property === "resources" || property === "listResources") {
158
+ return async (/** @type {unknown} */ input) => request("resources", { input });
159
+ }
160
+ if (property === "readResource" || property === "readResult") {
161
+ return async (/** @type {unknown} */ input) => request(property, { input });
162
+ }
90
163
  if (property === "describe") {
91
- return async (input) => request("describe", { input });
164
+ return async (/** @type {unknown} */ input) => request("describe", { input });
165
+ }
166
+ if (typeof property !== "string" || RESERVED_TOOL_PROPS.has(property)) {
167
+ return undefined;
92
168
  }
93
- if (typeof property !== "string" || RESERVED_TOOL_PROPS.has(property)) return undefined;
94
- return (args) => request("call", { path: property, args });
169
+ return (/** @type {unknown} */ args) => request("call", { path: property, args });
95
170
  },
96
171
  ownKeys() {
97
172
  throw new Error(TOOLS_ENUMERATION_ERROR);
98
173
  },
99
174
  });
100
175
 
176
+ /** @param {unknown} value */
101
177
  const emit = (value) => {
102
178
  const block = toContentBlock(value);
103
179
  // A running Pi keeps the adapter it loaded but starts this file from disk, so an older host passes no window.
104
180
  if (!outputFlow) {
105
- parentPort.postMessage({ type: "emit", block });
181
+ port.postMessage({ type: "emit", block });
106
182
  return;
107
183
  }
108
184
  // Charge each queued message too, so many tiny emits cannot outrun the host.
109
185
  const size = (block.type === "text" ? block.text.length : block.data.length) + 256;
110
186
  Atomics.add(outputFlow, 0, size);
111
- parentPort.postMessage({ type: "emit", block, size });
187
+ port.postMessage({ type: "emit", block, size });
112
188
  // Block this worker, not the host, until the host consumes queued output.
113
- for (let queued; (queued = Atomics.load(outputFlow, 0)) > outputWindow;) Atomics.wait(outputFlow, 0, queued);
189
+ for (let queued; (queued = Atomics.load(outputFlow, 0)) > outputWindow;) {
190
+ Atomics.wait(outputFlow, 0, queued);
191
+ }
114
192
  };
115
193
 
194
+ /**
195
+ * @param {string} level
196
+ * @returns {(args: unknown[]) => void}
197
+ */
198
+ function consoleEmitter(level) {
199
+ return (...args) =>
200
+ emit(`[console.${level}] ${formatWithOptions({ colors: false, depth: 4 }, ...args)}`);
201
+ }
202
+
116
203
  const capturedConsole = Object.freeze({
117
- log: (...args) => emit(`[console.log] ${formatWithOptions({ colors: false, depth: 4 }, ...args)}`),
118
- info: (...args) => emit(`[console.info] ${formatWithOptions({ colors: false, depth: 4 }, ...args)}`),
119
- warn: (...args) => emit(`[console.warn] ${formatWithOptions({ colors: false, depth: 4 }, ...args)}`),
120
- error: (...args) => emit(`[console.error] ${formatWithOptions({ colors: false, depth: 4 }, ...args)}`),
121
- debug: (...args) => emit(`[console.debug] ${formatWithOptions({ colors: false, depth: 4 }, ...args)}`),
204
+ log: consoleEmitter("log"),
205
+ info: consoleEmitter("info"),
206
+ warn: consoleEmitter("warn"),
207
+ error: consoleEmitter("error"),
208
+ debug: consoleEmitter("debug"),
122
209
  });
123
210
 
124
211
  try {
125
- const context = vm.createContext(Object.assign(Object.create(null), {
126
- tools,
127
- emit,
128
- console: capturedConsole,
129
- }), {
130
- codeGeneration: { strings: false, wasm: false },
131
- name: "mcp_script",
132
- });
212
+ const context = vm.createContext(
213
+ Object.assign(Object.create(null), {
214
+ tools,
215
+ emit,
216
+ console: capturedConsole,
217
+ }),
218
+ {
219
+ codeGeneration: { strings: false, wasm: false },
220
+ name: "mcp_script",
221
+ },
222
+ );
133
223
  const script = new vm.Script(`(async () => {\n${code}\n})()`, { filename: "mcp_script.js" });
134
224
  const returnValue = await Promise.resolve(script.runInContext(context));
135
- parentPort.postMessage(returnValue === undefined
136
- ? { type: "done" }
137
- : { type: "done", returnBlock: toContentBlock(returnValue) });
225
+ port.postMessage(
226
+ returnValue === undefined
227
+ ? { type: "done" }
228
+ : { type: "done", returnBlock: toContentBlock(returnValue) },
229
+ );
138
230
  } catch (error) {
139
- parentPort.postMessage({
231
+ port.postMessage({
140
232
  type: "error",
141
233
  message: error instanceof Error ? error.message : String(error),
142
234
  });
@@ -35,18 +35,18 @@ export declare class McpSetupPanel {
35
35
  private actionCursor;
36
36
  private importCursor;
37
37
  private pathCursor;
38
- private selectedImports;
38
+ private readonly selectedImports;
39
39
  private busy;
40
40
  private notice;
41
- private tui;
42
- private t;
43
- private keys;
41
+ private readonly tui;
42
+ private readonly t;
43
+ private readonly keys;
44
44
  private inactivityTimeout;
45
45
  private static readonly INACTIVITY_MS;
46
- private discovery;
47
- private callbacks;
48
- private options;
49
- private done;
46
+ private readonly discovery;
47
+ private readonly callbacks;
48
+ private readonly options;
49
+ private readonly done;
50
50
  constructor(discovery: McpDiscoverySummary, callbacks: SetupPanelCallbacks, options: SetupPanelOptions, tui: {
51
51
  requestRender(): void;
52
52
  }, done: () => void);