@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.
- package/CHANGELOG.md +140 -7
- package/OAUTH.md +13 -16
- package/README.md +8 -8
- package/cli.js +67 -28
- package/dist/abort.js +18 -9
- package/dist/client-operations.js +4 -2
- package/dist/commands.js +77 -43
- package/dist/config.js +194 -105
- package/dist/connection-runtime.d.ts +2 -2
- package/dist/connection-runtime.js +29 -18
- package/dist/consent-manager.d.ts +2 -2
- package/dist/consent-manager.js +5 -2
- package/dist/direct-tools.d.ts +1 -1
- package/dist/direct-tools.js +13 -7
- package/dist/elicitation-handler.d.ts +2 -3
- package/dist/elicitation-handler.js +113 -58
- package/dist/gateway-arguments.d.ts +1 -1
- package/dist/gateway-arguments.js +90 -23
- package/dist/glimpse-ui.js +10 -5
- package/dist/host-html-template.d.ts +3 -1
- package/dist/host-html-template.js +9 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +264 -119
- package/dist/init.d.ts +1 -3
- package/dist/init.js +91 -51
- package/dist/legacy-migration.d.ts +1 -1
- package/dist/legacy-migration.js +17 -12
- package/dist/lifecycle.d.ts +3 -3
- package/dist/lifecycle.js +33 -17
- package/dist/logger.d.ts +2 -2
- package/dist/logger.js +4 -2
- package/dist/mcp-auth-flow.js +150 -58
- package/dist/mcp-auth.d.ts +7 -7
- package/dist/mcp-auth.js +189 -138
- package/dist/mcp-callback-server.js +19 -8
- package/dist/mcp-code.js +168 -54
- package/dist/mcp-keyring-helper.cjs +57 -23
- package/dist/mcp-oauth-provider.d.ts +6 -7
- package/dist/mcp-oauth-provider.js +146 -72
- package/dist/mcp-oauth-runtime.js +29 -16
- package/dist/mcp-output-guard.js +193 -65
- package/dist/mcp-output-preview.js +41 -20
- package/dist/mcp-panel.d.ts +9 -9
- package/dist/mcp-panel.js +141 -70
- package/dist/mcp-script-worker.mjs +135 -43
- package/dist/mcp-setup-panel.d.ts +8 -8
- package/dist/mcp-setup-panel.js +130 -44
- package/dist/mcp-status.js +14 -7
- package/dist/mcp-trace.d.ts +10 -5
- package/dist/mcp-trace.js +127 -61
- package/dist/metadata-cache.d.ts +3 -4
- package/dist/metadata-cache.js +83 -43
- package/dist/npx-resolver.js +101 -60
- package/dist/onboarding-state.js +4 -2
- package/dist/plain-result-reader.js +78 -31
- package/dist/prompts.js +36 -20
- package/dist/proxy-modes.js +670 -193
- package/dist/resource-tools.js +30 -9
- package/dist/runtime-owner.js +36 -18
- package/dist/sampling-handler.d.ts +1 -2
- package/dist/sampling-handler.js +41 -21
- package/dist/search-ranking.d.ts +1 -1
- package/dist/search-ranking.js +67 -34
- package/dist/server-manager.d.ts +11 -11
- package/dist/server-manager.js +283 -166
- package/dist/session-recovery.js +113 -61
- package/dist/tool-approval.js +9 -3
- package/dist/tool-loader.js +154 -55
- package/dist/tool-metadata.d.ts +1 -1
- package/dist/tool-metadata.js +51 -23
- package/dist/tool-registrar.js +32 -10
- package/dist/tool-result-renderer.d.ts +3 -3
- package/dist/tool-result-renderer.js +65 -44
- package/dist/types.d.ts +2 -2
- package/dist/types.js +39 -18
- package/dist/ui-message-history.js +33 -13
- package/dist/ui-resource-handler.d.ts +3 -3
- package/dist/ui-resource-handler.js +37 -21
- package/dist/ui-server.js +101 -43
- package/dist/ui-session.js +66 -29
- package/dist/ui-stream-types.js +14 -4
- package/dist/ui-tool-visibility.js +12 -6
- package/dist/unix-socket-transport.d.ts +1 -2
- package/dist/unix-socket-transport.js +19 -12
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +60 -35
- package/package.json +29 -5
- 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 } =
|
|
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 (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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")
|
|
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)
|
|
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
|
-
|
|
39
|
-
|
|
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 (
|
|
42
|
-
|
|
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
|
-
|
|
52
|
-
if (
|
|
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)
|
|
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
|
-
|
|
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 (
|
|
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))
|
|
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")
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;)
|
|
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: (
|
|
118
|
-
info: (
|
|
119
|
-
warn: (
|
|
120
|
-
error: (
|
|
121
|
-
debug: (
|
|
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(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
225
|
+
port.postMessage(
|
|
226
|
+
returnValue === undefined
|
|
227
|
+
? { type: "done" }
|
|
228
|
+
: { type: "done", returnBlock: toContentBlock(returnValue) },
|
|
229
|
+
);
|
|
138
230
|
} catch (error) {
|
|
139
|
-
|
|
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);
|