@yanlinglabs/winter-agent-runtime 0.0.27 → 0.0.29
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/README.md +7 -1
- package/dist/context/attachments.d.ts +37 -0
- package/dist/context/seam.d.ts +19 -1
- package/dist/embedded-host.d.ts +14 -0
- package/dist/embedded-host.js +11 -1
- package/dist/embedded-protocol.d.ts +17 -1
- package/dist/embedded-worker.js +20 -3
- package/dist/embedded.js +4 -3
- package/dist/engine.d.ts +23 -0
- package/dist/hooks/additional-context.d.ts +20 -0
- package/dist/hooks/async-hooks.d.ts +64 -0
- package/dist/hooks/command-invoker.d.ts +8 -1
- package/dist/hooks/registry.d.ts +6 -0
- package/dist/hooks/runner.d.ts +22 -0
- package/dist/index-8jgwp1px.js +533 -0
- package/dist/{index-rkhh0457.js → index-eyddzcgs.js} +2 -2
- package/dist/{index-584yahed.js → index-gmxxtqpe.js} +176 -107
- package/dist/{index-9qgkpv56.js → index-jz5d90c6.js} +604 -587
- package/dist/index.js +2 -1
- package/dist/mcp/client.d.ts +16 -1
- package/dist/mcp/test-fixtures.d.ts +2 -0
- package/dist/mcp/transports/stdio.d.ts +19 -0
- package/dist/mcp-client.d.ts +28 -0
- package/dist/mcp-client.js +23 -0
- package/dist/permissions/paths.d.ts +5 -0
- package/dist/process-groups.d.ts +22 -0
- package/dist/provider/classifier/model-classifier.d.ts +12 -0
- package/dist/store/dialect.d.ts +1 -1
- package/dist/store/provider-state.d.ts +14 -1
- package/dist/subagents/child-handle.d.ts +8 -0
- package/dist/testing.js +3 -2
- package/dist/tools/registry.d.ts +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -4
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
// src/process-groups.ts
|
|
2
|
+
var live = new Map;
|
|
3
|
+
var listeners = new Set;
|
|
4
|
+
function notify(change) {
|
|
5
|
+
for (const listener of listeners) {
|
|
6
|
+
try {
|
|
7
|
+
listener(change);
|
|
8
|
+
} catch {}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function trackProcessGroup(pgid, kind) {
|
|
12
|
+
if (!Number.isInteger(pgid) || pgid <= 1)
|
|
13
|
+
return () => {};
|
|
14
|
+
const registration = { kind };
|
|
15
|
+
live.set(pgid, registration);
|
|
16
|
+
notify({ op: "add", pgid, kind });
|
|
17
|
+
let released = false;
|
|
18
|
+
return () => {
|
|
19
|
+
if (released)
|
|
20
|
+
return;
|
|
21
|
+
released = true;
|
|
22
|
+
if (live.get(pgid) !== registration)
|
|
23
|
+
return;
|
|
24
|
+
live.delete(pgid);
|
|
25
|
+
notify({ op: "remove", pgid, kind });
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function liveProcessGroups() {
|
|
29
|
+
return [...live.entries()].map(([pgid, { kind }]) => ({ pgid, kind }));
|
|
30
|
+
}
|
|
31
|
+
function onProcessGroupChange(listener) {
|
|
32
|
+
listeners.add(listener);
|
|
33
|
+
return () => {
|
|
34
|
+
listeners.delete(listener);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/mcp/elicitation.ts
|
|
39
|
+
var MCP_ELICITATION_SUBTYPE = "mcp_elicitation";
|
|
40
|
+
var DETERMINISTIC_DECLINE = { action: "decline" };
|
|
41
|
+
function isElicitationAction(value) {
|
|
42
|
+
return value === "accept" || value === "decline" || value === "cancel";
|
|
43
|
+
}
|
|
44
|
+
function toResultPayload(raw) {
|
|
45
|
+
if (typeof raw !== "object" || raw === null)
|
|
46
|
+
return DETERMINISTIC_DECLINE;
|
|
47
|
+
const candidate = raw;
|
|
48
|
+
if (!isElicitationAction(candidate.action))
|
|
49
|
+
return DETERMINISTIC_DECLINE;
|
|
50
|
+
if (candidate.action !== "accept")
|
|
51
|
+
return { action: candidate.action };
|
|
52
|
+
const content = typeof candidate.content === "object" && candidate.content !== null ? candidate.content : undefined;
|
|
53
|
+
return content !== undefined ? { action: "accept", content } : { action: "accept" };
|
|
54
|
+
}
|
|
55
|
+
function createElicitationAsker2(sender) {
|
|
56
|
+
if (!sender) {
|
|
57
|
+
return async () => DETERMINISTIC_DECLINE;
|
|
58
|
+
}
|
|
59
|
+
return async (payload) => {
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = await sender.request(MCP_ELICITATION_SUBTYPE, payload);
|
|
63
|
+
} catch {
|
|
64
|
+
return DETERMINISTIC_DECLINE;
|
|
65
|
+
}
|
|
66
|
+
return toResultPayload(raw);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function buildElicitationPayload(serverName, params) {
|
|
70
|
+
return {
|
|
71
|
+
serverName,
|
|
72
|
+
message: params.message,
|
|
73
|
+
...params.mode !== undefined ? { mode: params.mode } : {},
|
|
74
|
+
...params.url !== undefined ? { url: params.url } : {},
|
|
75
|
+
...params.elicitationId !== undefined ? { elicitationId: params.elicitationId } : {},
|
|
76
|
+
...params.requestedSchema !== undefined ? { requestedSchema: params.requestedSchema } : {}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function rawElicitParamsOf(params) {
|
|
80
|
+
const p = params;
|
|
81
|
+
return {
|
|
82
|
+
message: p.message,
|
|
83
|
+
...p.mode !== undefined ? { mode: p.mode } : {},
|
|
84
|
+
...typeof p.url === "string" ? { url: p.url } : {},
|
|
85
|
+
...typeof p.elicitationId === "string" ? { elicitationId: p.elicitationId } : {},
|
|
86
|
+
...p.requestedSchema !== undefined ? { requestedSchema: p.requestedSchema } : {}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function installElicitationHandler(client, serverName, ask) {
|
|
90
|
+
client.setRequestHandler("elicitation/create", async (request) => {
|
|
91
|
+
const payload = buildElicitationPayload(serverName, rawElicitParamsOf(request.params));
|
|
92
|
+
const result = await ask(payload);
|
|
93
|
+
if (result.content === undefined)
|
|
94
|
+
return { action: result.action };
|
|
95
|
+
const content = toWireElicitContent(result.content);
|
|
96
|
+
return content !== undefined ? { action: result.action, content } : { action: DETERMINISTIC_DECLINE.action };
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function toWireElicitContent(content) {
|
|
100
|
+
const out = {};
|
|
101
|
+
for (const [key, value] of Object.entries(content)) {
|
|
102
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
103
|
+
out[key] = value;
|
|
104
|
+
else if (Array.isArray(value) && value.every((v) => typeof v === "string"))
|
|
105
|
+
out[key] = value;
|
|
106
|
+
else
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/mcp/client.ts
|
|
113
|
+
import { Client, ProtocolError, ProtocolErrorCode, SdkError, SdkErrorCode, SdkHttpError, SseError, UnauthorizedError, UnsupportedProtocolVersionError } from "@modelcontextprotocol/client";
|
|
114
|
+
|
|
115
|
+
// src/mcp/transports/stdio.ts
|
|
116
|
+
import { spawn } from "node:child_process";
|
|
117
|
+
import { ReadBuffer, serializeMessage } from "@modelcontextprotocol/client";
|
|
118
|
+
var STDIO_BASE_ENV_NAMES = ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"];
|
|
119
|
+
function buildStdioEnv(cfgEnv) {
|
|
120
|
+
const env = {};
|
|
121
|
+
for (const name of STDIO_BASE_ENV_NAMES) {
|
|
122
|
+
const value = process.env[name];
|
|
123
|
+
if (value !== undefined)
|
|
124
|
+
env[name] = value;
|
|
125
|
+
}
|
|
126
|
+
return { ...env, ...cfgEnv ?? {} };
|
|
127
|
+
}
|
|
128
|
+
var STDERR_TAIL_MAX_CHARS = 4096;
|
|
129
|
+
|
|
130
|
+
class WinterStdioTransport {
|
|
131
|
+
opts;
|
|
132
|
+
child;
|
|
133
|
+
readBuffer = new ReadBuffer;
|
|
134
|
+
stderrTailBuffer = "";
|
|
135
|
+
exitedOnItsOwnFlag = false;
|
|
136
|
+
onclose;
|
|
137
|
+
onerror;
|
|
138
|
+
onmessage;
|
|
139
|
+
constructor(opts) {
|
|
140
|
+
this.opts = opts;
|
|
141
|
+
}
|
|
142
|
+
get pid() {
|
|
143
|
+
return this.child?.pid ?? null;
|
|
144
|
+
}
|
|
145
|
+
get stderr() {
|
|
146
|
+
return this.child?.stderr ?? null;
|
|
147
|
+
}
|
|
148
|
+
get exitedOnItsOwn() {
|
|
149
|
+
return this.exitedOnItsOwnFlag;
|
|
150
|
+
}
|
|
151
|
+
async start() {
|
|
152
|
+
if (this.child) {
|
|
153
|
+
throw new Error("WinterStdioTransport already started");
|
|
154
|
+
}
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
const child = spawn(this.opts.command, this.opts.args ?? [], {
|
|
157
|
+
cwd: this.opts.cwd,
|
|
158
|
+
env: buildStdioEnv(this.opts.env),
|
|
159
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
160
|
+
shell: false,
|
|
161
|
+
detached: true
|
|
162
|
+
});
|
|
163
|
+
this.child = child;
|
|
164
|
+
const releaseGroup = child.pid !== undefined ? trackProcessGroup(child.pid, "mcp_stdio") : () => {};
|
|
165
|
+
child.on("error", (error) => {
|
|
166
|
+
releaseGroup();
|
|
167
|
+
reject(this.withStderrTail(error));
|
|
168
|
+
this.onerror?.(error);
|
|
169
|
+
});
|
|
170
|
+
child.on("spawn", () => resolve());
|
|
171
|
+
child.on("close", () => {
|
|
172
|
+
releaseGroup();
|
|
173
|
+
if (this.child === child)
|
|
174
|
+
this.exitedOnItsOwnFlag = true;
|
|
175
|
+
this.child = undefined;
|
|
176
|
+
this.onclose?.();
|
|
177
|
+
});
|
|
178
|
+
child.stdin?.on("error", (error) => this.onerror?.(error));
|
|
179
|
+
child.stdout?.on("data", (chunk) => {
|
|
180
|
+
try {
|
|
181
|
+
this.readBuffer.append(chunk);
|
|
182
|
+
this.processReadBuffer();
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.onerror?.(error);
|
|
185
|
+
this.close().catch(() => {});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
child.stdout?.on("error", (error) => this.onerror?.(error));
|
|
189
|
+
child.stderr?.on("data", (chunk) => {
|
|
190
|
+
this.stderrTailBuffer = (this.stderrTailBuffer + chunk.toString("utf8")).slice(-STDERR_TAIL_MAX_CHARS);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
get stderrTail() {
|
|
195
|
+
return this.stderrTailBuffer;
|
|
196
|
+
}
|
|
197
|
+
withStderrTail(error) {
|
|
198
|
+
const tail = this.stderrTailBuffer.trim();
|
|
199
|
+
if (tail === "")
|
|
200
|
+
return error;
|
|
201
|
+
return new Error(`${error.message}
|
|
202
|
+
--- server stderr (last ${tail.length} chars) ---
|
|
203
|
+
${tail}`, { cause: error });
|
|
204
|
+
}
|
|
205
|
+
processReadBuffer() {
|
|
206
|
+
for (;; ) {
|
|
207
|
+
let message;
|
|
208
|
+
try {
|
|
209
|
+
message = this.readBuffer.readMessage();
|
|
210
|
+
} catch (error) {
|
|
211
|
+
this.onerror?.(error);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (message === null)
|
|
215
|
+
return;
|
|
216
|
+
this.onmessage?.(message);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async send(message) {
|
|
220
|
+
return new Promise((resolve, reject) => {
|
|
221
|
+
if (!this.child?.stdin) {
|
|
222
|
+
reject(new Error("WinterStdioTransport: not connected"));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const json = serializeMessage(message);
|
|
226
|
+
if (this.child.stdin.write(json))
|
|
227
|
+
resolve();
|
|
228
|
+
else
|
|
229
|
+
this.child.stdin.once("drain", resolve);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
async close() {
|
|
233
|
+
const child = this.child;
|
|
234
|
+
this.child = undefined;
|
|
235
|
+
if (!child || child.pid === undefined) {
|
|
236
|
+
this.readBuffer.clear();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const pid = child.pid;
|
|
240
|
+
const exited = new Promise((resolve) => {
|
|
241
|
+
child.once("close", () => resolve());
|
|
242
|
+
});
|
|
243
|
+
try {
|
|
244
|
+
child.stdin?.end();
|
|
245
|
+
} catch {}
|
|
246
|
+
try {
|
|
247
|
+
process.kill(-pid, "SIGKILL");
|
|
248
|
+
} catch {}
|
|
249
|
+
await Promise.race([exited, new Promise((resolve) => setTimeout(resolve, 2000).unref?.())]);
|
|
250
|
+
this.readBuffer.clear();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function buildStdioTransport(cfg, cwd) {
|
|
254
|
+
return new WinterStdioTransport({
|
|
255
|
+
command: cfg.command,
|
|
256
|
+
cwd,
|
|
257
|
+
...cfg.args !== undefined ? { args: cfg.args } : {},
|
|
258
|
+
...cfg.env !== undefined ? { env: cfg.env } : {}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/mcp/transports/http.ts
|
|
263
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
264
|
+
function buildHttpTransport(cfg, opts = {}) {
|
|
265
|
+
const requestInit = { ...cfg.headers !== undefined ? { headers: cfg.headers } : {}, ...opts.refuseRedirects === true ? { redirect: "error" } : {} };
|
|
266
|
+
const transport = new StreamableHTTPClientTransport(new URL(cfg.url), {
|
|
267
|
+
...Object.keys(requestInit).length > 0 ? { requestInit } : {}
|
|
268
|
+
});
|
|
269
|
+
return transport;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/mcp/transports/sse.ts
|
|
273
|
+
import { SSEClientTransport } from "@modelcontextprotocol/client";
|
|
274
|
+
function buildSseTransport(cfg) {
|
|
275
|
+
const transport = new SSEClientTransport(new URL(cfg.url), {
|
|
276
|
+
...cfg.headers !== undefined ? { requestInit: { headers: cfg.headers } } : {}
|
|
277
|
+
});
|
|
278
|
+
return transport;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/mcp/transports/sdk.ts
|
|
282
|
+
import { InMemoryTransport } from "@modelcontextprotocol/client";
|
|
283
|
+
async function buildSdkTransport(server) {
|
|
284
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
285
|
+
await server.connect(serverTransport);
|
|
286
|
+
return clientTransport;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/mcp/client.ts
|
|
290
|
+
class McpConnectError2 extends Error {
|
|
291
|
+
code;
|
|
292
|
+
constructor(code, message, httpStatus) {
|
|
293
|
+
super(message);
|
|
294
|
+
this.name = "McpConnectError";
|
|
295
|
+
this.code = code;
|
|
296
|
+
if (httpStatus !== undefined)
|
|
297
|
+
Object.assign(this, { httpStatus });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function resolveVersionNegotiation2(config) {
|
|
301
|
+
if (config.type === "sdk")
|
|
302
|
+
return "legacy";
|
|
303
|
+
if (config.versionNegotiation !== undefined)
|
|
304
|
+
return config.versionNegotiation;
|
|
305
|
+
return config.type === "http" ? "auto" : "legacy";
|
|
306
|
+
}
|
|
307
|
+
function toSdkNegotiationMode(mode) {
|
|
308
|
+
return typeof mode === "string" ? mode : { pin: mode.pin };
|
|
309
|
+
}
|
|
310
|
+
function classifyConnectError(err, facts = { transportClosed: false, pinned: false }) {
|
|
311
|
+
if (err instanceof McpConnectError2)
|
|
312
|
+
return err;
|
|
313
|
+
if (err instanceof UnauthorizedError)
|
|
314
|
+
return new McpConnectError2("needs_auth", err.message);
|
|
315
|
+
if (err instanceof SdkError && err.code === SdkErrorCode.RequestTimeout)
|
|
316
|
+
return new McpConnectError2("timeout", err.message);
|
|
317
|
+
const httpStatus = err instanceof SdkHttpError ? err.status : err instanceof SseError && typeof err.code === "number" ? err.code : undefined;
|
|
318
|
+
if (err instanceof SdkHttpError || err instanceof SseError) {
|
|
319
|
+
return new McpConnectError2(httpStatus === 401 ? "needs_auth" : "handshake_failed", err.message, httpStatus);
|
|
320
|
+
}
|
|
321
|
+
if (err instanceof SdkError && err.code === SdkErrorCode.EraNegotiationFailed) {
|
|
322
|
+
if (facts.transportClosed)
|
|
323
|
+
return new McpConnectError2("transport_closed", err.message);
|
|
324
|
+
const cause = err.cause;
|
|
325
|
+
if (cause !== undefined && cause !== err)
|
|
326
|
+
return classifyConnectError(cause, facts);
|
|
327
|
+
return new McpConnectError2(facts.pinned ? "version_mismatch" : "handshake_failed", err.message);
|
|
328
|
+
}
|
|
329
|
+
if (err instanceof UnsupportedProtocolVersionError)
|
|
330
|
+
return new McpConnectError2("version_mismatch", err.message);
|
|
331
|
+
if (err instanceof ProtocolError)
|
|
332
|
+
return new McpConnectError2("handshake_failed", err.message);
|
|
333
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
334
|
+
const code = err?.code;
|
|
335
|
+
if (code === "ENOENT" || code === "EACCES")
|
|
336
|
+
return new McpConnectError2("spawn_failed", message);
|
|
337
|
+
if (facts.transportClosed)
|
|
338
|
+
return new McpConnectError2("transport_closed", message);
|
|
339
|
+
return new McpConnectError2("unknown", message);
|
|
340
|
+
}
|
|
341
|
+
function raceConnect(client, transport, timeoutMs) {
|
|
342
|
+
let timer;
|
|
343
|
+
const outer = new Promise((_, reject) => {
|
|
344
|
+
timer = setTimeout(() => reject(new McpConnectError2("timeout", `mcp client: connection attempt exceeded ${timeoutMs}ms`)), timeoutMs);
|
|
345
|
+
timer.unref?.();
|
|
346
|
+
});
|
|
347
|
+
return Promise.race([client.connect(transport, { timeout: timeoutMs }), outer]).finally(() => {
|
|
348
|
+
if (timer !== undefined)
|
|
349
|
+
clearTimeout(timer);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
function dedupeTools(tools) {
|
|
353
|
+
const seen = new Set;
|
|
354
|
+
const out = [];
|
|
355
|
+
for (const tool of tools) {
|
|
356
|
+
if (seen.has(tool.name)) {
|
|
357
|
+
console.error(`winter: mcp client: server reported duplicate tool name "${tool.name}" in one tools/list response -- keeping the first occurrence, dropping the rest`);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
seen.add(tool.name);
|
|
361
|
+
out.push(tool);
|
|
362
|
+
}
|
|
363
|
+
return out;
|
|
364
|
+
}
|
|
365
|
+
var AUTO_PROBE_TIMEOUT_CAP_MS = 5000;
|
|
366
|
+
function autoProbeTimeoutMs(connectTimeoutMs) {
|
|
367
|
+
return Math.max(1, Math.min(AUTO_PROBE_TIMEOUT_CAP_MS, Math.floor(connectTimeoutMs / 3)));
|
|
368
|
+
}
|
|
369
|
+
function retriesAsLegacy(err) {
|
|
370
|
+
if (err.code === "needs_auth" || err.code === "spawn_failed" || err.code === "version_mismatch")
|
|
371
|
+
return false;
|
|
372
|
+
return err.httpStatus !== 401 && err.httpStatus !== 403;
|
|
373
|
+
}
|
|
374
|
+
var announcedLegacyFallbacks = new Set;
|
|
375
|
+
function announceLegacyFallback(name, transport, cause) {
|
|
376
|
+
const key = `${name}\x00${transport}\x00${cause.code}`;
|
|
377
|
+
if (announcedLegacyFallbacks.has(key))
|
|
378
|
+
return;
|
|
379
|
+
announcedLegacyFallbacks.add(key);
|
|
380
|
+
console.error(`winter: mcp: server "${name}" (${transport}) failed the 'auto' version probe (${cause.code}); connecting once more with 'legacy' -- set versionNegotiation: "legacy" on it to skip the probe`);
|
|
381
|
+
}
|
|
382
|
+
var MANUAL_LIST_MAX_PAGES = 64;
|
|
383
|
+
async function listAllPagesManually(fetchPage, what) {
|
|
384
|
+
const out = [];
|
|
385
|
+
const seen = new Set;
|
|
386
|
+
let cursor;
|
|
387
|
+
for (let page = 0;page < MANUAL_LIST_MAX_PAGES; page++) {
|
|
388
|
+
let result;
|
|
389
|
+
try {
|
|
390
|
+
result = await fetchPage(cursor);
|
|
391
|
+
} catch (err) {
|
|
392
|
+
if (page === 0 && err instanceof ProtocolError && err.code === ProtocolErrorCode.MethodNotFound)
|
|
393
|
+
return [];
|
|
394
|
+
throw err;
|
|
395
|
+
}
|
|
396
|
+
out.push(...result.items);
|
|
397
|
+
const next = result.nextCursor;
|
|
398
|
+
if (next === undefined || seen.has(next))
|
|
399
|
+
return out;
|
|
400
|
+
seen.add(next);
|
|
401
|
+
cursor = next;
|
|
402
|
+
}
|
|
403
|
+
throw new Error(`mcp client: ${what} did not finish within ${MANUAL_LIST_MAX_PAGES} pages (the server's pagination never converged)`);
|
|
404
|
+
}
|
|
405
|
+
async function connectMcpServer2(opts) {
|
|
406
|
+
const mode = resolveVersionNegotiation2(opts.config);
|
|
407
|
+
if (mode !== "auto")
|
|
408
|
+
return connectOnce(opts, mode, opts.connectTimeoutMs);
|
|
409
|
+
const started = Date.now();
|
|
410
|
+
try {
|
|
411
|
+
return await connectOnce(opts, "auto", opts.connectTimeoutMs, autoProbeTimeoutMs(opts.connectTimeoutMs));
|
|
412
|
+
} catch (err) {
|
|
413
|
+
if (!(err instanceof McpConnectError2) || !retriesAsLegacy(err))
|
|
414
|
+
throw err;
|
|
415
|
+
const remaining = opts.connectTimeoutMs - (Date.now() - started);
|
|
416
|
+
if (remaining <= 0)
|
|
417
|
+
throw err;
|
|
418
|
+
announceLegacyFallback(opts.name, opts.config.type ?? "stdio", err);
|
|
419
|
+
return connectOnce(opts, "legacy", remaining);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function connectOnce(opts, mode, connectTimeoutMs, probeTimeoutMs) {
|
|
423
|
+
const { name, config, elicitationAsk } = opts;
|
|
424
|
+
let transport;
|
|
425
|
+
try {
|
|
426
|
+
if (config.type === "sdk") {
|
|
427
|
+
if (!opts.inProcessServer) {
|
|
428
|
+
throw new McpConnectError2("spawn_failed", `mcp client: server "${name}" is configured as type "sdk" but no in-process server instance was supplied to connectMcpServer`);
|
|
429
|
+
}
|
|
430
|
+
transport = await buildSdkTransport(opts.inProcessServer);
|
|
431
|
+
} else if (config.type === "http") {
|
|
432
|
+
transport = buildHttpTransport(config, opts.refuseHttpRedirects === true ? { refuseRedirects: true } : {});
|
|
433
|
+
} else if (config.type === "sse") {
|
|
434
|
+
transport = buildSseTransport(config);
|
|
435
|
+
} else {
|
|
436
|
+
if (opts.cwd === undefined) {
|
|
437
|
+
throw new McpConnectError2("spawn_failed", `mcp client: stdio server "${name}" has no session cwd to start in; it is never started in the host process's cwd`);
|
|
438
|
+
}
|
|
439
|
+
transport = buildStdioTransport(config, opts.cwd);
|
|
440
|
+
}
|
|
441
|
+
const onToolListChanged = opts.onToolListChanged;
|
|
442
|
+
const client = new Client({ name: "winter", version: "0.0.1" }, {
|
|
443
|
+
capabilities: { elicitation: { form: {}, url: {} } },
|
|
444
|
+
versionNegotiation: { mode: toSdkNegotiationMode(mode), ...probeTimeoutMs !== undefined ? { probe: { timeoutMs: probeTimeoutMs } } : {} },
|
|
445
|
+
inputRequired: { autoFulfill: true },
|
|
446
|
+
...onToolListChanged !== undefined ? {
|
|
447
|
+
listChanged: {
|
|
448
|
+
tools: {
|
|
449
|
+
autoRefresh: false,
|
|
450
|
+
onChanged: () => {
|
|
451
|
+
try {
|
|
452
|
+
onToolListChanged();
|
|
453
|
+
} catch (err) {
|
|
454
|
+
console.error(`winter: mcp client: server "${name}" tool-list-changed handler threw`, err);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
} : {}
|
|
460
|
+
});
|
|
461
|
+
installElicitationHandler(client, name, elicitationAsk);
|
|
462
|
+
await raceConnect(client, transport, connectTimeoutMs);
|
|
463
|
+
const protocolVersion = client.getNegotiatedProtocolVersion();
|
|
464
|
+
let closed = false;
|
|
465
|
+
return {
|
|
466
|
+
serverName: name,
|
|
467
|
+
...protocolVersion !== undefined ? { protocolVersion } : {},
|
|
468
|
+
async listTools() {
|
|
469
|
+
const rawTools = client.getServerCapabilities()?.tools !== undefined ? (await client.listTools(undefined, { cacheMode: "refresh" })).tools : await listAllPagesManually(async (cursor) => {
|
|
470
|
+
const page = await client.request({ method: "tools/list", ...cursor !== undefined ? { params: { cursor } } : {} });
|
|
471
|
+
return { items: page.tools, nextCursor: page.nextCursor };
|
|
472
|
+
}, "tools/list");
|
|
473
|
+
return dedupeTools(rawTools.map((t) => ({
|
|
474
|
+
name: t.name,
|
|
475
|
+
...t.description !== undefined ? { description: t.description } : {},
|
|
476
|
+
inputSchema: t.inputSchema,
|
|
477
|
+
...t.outputSchema !== undefined ? { outputSchema: t.outputSchema } : {},
|
|
478
|
+
...t.annotations !== undefined ? { annotations: t.annotations } : {},
|
|
479
|
+
...t._meta !== undefined ? { _meta: t._meta } : {}
|
|
480
|
+
})));
|
|
481
|
+
},
|
|
482
|
+
async listResources() {
|
|
483
|
+
const resources = client.getServerCapabilities()?.resources !== undefined ? (await client.listResources(undefined, { cacheMode: "refresh" })).resources : await listAllPagesManually(async (cursor) => {
|
|
484
|
+
const page = await client.request({ method: "resources/list", ...cursor !== undefined ? { params: { cursor } } : {} });
|
|
485
|
+
return { items: page.resources, nextCursor: page.nextCursor };
|
|
486
|
+
}, "resources/list");
|
|
487
|
+
return resources.map((r) => ({
|
|
488
|
+
uri: r.uri,
|
|
489
|
+
...r.name !== undefined ? { name: r.name } : {},
|
|
490
|
+
...r.description !== undefined ? { description: r.description } : {},
|
|
491
|
+
...r.mimeType !== undefined ? { mimeType: r.mimeType } : {}
|
|
492
|
+
}));
|
|
493
|
+
},
|
|
494
|
+
async readResource(uri, readOpts) {
|
|
495
|
+
const result = await client.readResource({ uri }, { cacheMode: "refresh", ...readOpts?.timeoutMs !== undefined ? { timeout: readOpts.timeoutMs } : {} });
|
|
496
|
+
return result.contents.map((c) => {
|
|
497
|
+
const mimeType = "mimeType" in c && c.mimeType !== undefined ? c.mimeType : undefined;
|
|
498
|
+
if ("blob" in c) {
|
|
499
|
+
return { uri: c.uri, ...mimeType !== undefined ? { mimeType } : {}, blobBase64: c.blob };
|
|
500
|
+
}
|
|
501
|
+
return { uri: c.uri, ...mimeType !== undefined ? { mimeType } : {}, text: "text" in c ? c.text : "" };
|
|
502
|
+
});
|
|
503
|
+
},
|
|
504
|
+
async callTool(toolName, args, callOpts) {
|
|
505
|
+
const result = await client.callTool({ name: toolName, arguments: args }, callOpts?.timeoutMs !== undefined ? { timeout: callOpts.timeoutMs } : undefined);
|
|
506
|
+
return {
|
|
507
|
+
content: result.content ?? [],
|
|
508
|
+
...result.isError === true ? { isError: true } : {}
|
|
509
|
+
};
|
|
510
|
+
},
|
|
511
|
+
async close() {
|
|
512
|
+
if (closed)
|
|
513
|
+
return;
|
|
514
|
+
closed = true;
|
|
515
|
+
await client.close();
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
} catch (err) {
|
|
519
|
+
const stderrTail = transport instanceof WinterStdioTransport ? transport.stderrTail.trim() : "";
|
|
520
|
+
const facts = { transportClosed: transport instanceof WinterStdioTransport && transport.exitedOnItsOwn, pinned: typeof mode === "object" };
|
|
521
|
+
try {
|
|
522
|
+
await transport.close();
|
|
523
|
+
} catch {}
|
|
524
|
+
const classified = classifyConnectError(err, facts);
|
|
525
|
+
if (stderrTail === "" || classified.message.includes(stderrTail))
|
|
526
|
+
throw classified;
|
|
527
|
+
throw new McpConnectError2(classified.code, `${classified.message}
|
|
528
|
+
--- server stderr (last ${stderrTail.length} chars) ---
|
|
529
|
+
${stderrTail}`, classified.httpStatus);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export { trackProcessGroup, liveProcessGroups, onProcessGroupChange, createElicitationAsker2, McpConnectError2, resolveVersionNegotiation2, connectMcpServer2 };
|
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
resolveEngineSession2,
|
|
10
10
|
resolveProductionWinterHome2,
|
|
11
11
|
resolveProductionStoreHome
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-jz5d90c6.js";
|
|
13
13
|
import {
|
|
14
14
|
registerDefaultChildEngineFactory,
|
|
15
15
|
loadResumedChain,
|
|
16
16
|
buildProductionWiring,
|
|
17
17
|
withAutoSkillPermissions,
|
|
18
18
|
restoreChildRoster
|
|
19
|
-
} from "./index-
|
|
19
|
+
} from "./index-gmxxtqpe.js";
|
|
20
20
|
|
|
21
21
|
// src/embedded.ts
|
|
22
22
|
import { splitFrames, encodeFrame } from "@yanlinglabs/winter-agent-sdk";
|