@livx.cc/agentx 0.99.46 → 0.99.48
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 +2 -1
- package/dist/{Agent-OcAg2yxA.d.ts → Agent-BeoEFl2T.d.ts} +22 -7
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +904 -417
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +712 -304
- package/dist/index.js.map +1 -1
- package/dist/{mcp-kgG6rtsd.d.ts → mcp-DjYZzGGW.d.ts} +13 -4
- package/dist/mcp.client.d.ts +33 -7
- package/dist/mcp.client.js +147 -40
- package/dist/mcp.client.js.map +1 -1
- package/dist/{tools-BqL8Lk4J.d.ts → tools-MNdqlJIa.d.ts} +180 -40
- package/dist/tools.shell.d.ts +40 -11
- package/dist/tools.shell.js +452 -91
- package/dist/tools.shell.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AgentTool } from './tools-
|
|
1
|
+
import { A as AgentTool } from './tools-MNdqlJIa.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* MCP bridge — adapt a Model Context Protocol tool list into the agent's AgentTool[],
|
|
@@ -14,8 +14,17 @@ interface McpToolSpec {
|
|
|
14
14
|
/** JSON Schema for the tool's arguments (MCP's `inputSchema`). */
|
|
15
15
|
inputSchema?: object;
|
|
16
16
|
}
|
|
17
|
+
/** Liveness + cancellation for one call: `onProgress` ⇒ the client requests MCP progress notifications. */
|
|
18
|
+
interface McpCallOptions {
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
onProgress?: (p: {
|
|
21
|
+
progress?: number;
|
|
22
|
+
total?: number;
|
|
23
|
+
message?: string;
|
|
24
|
+
}) => void;
|
|
25
|
+
}
|
|
17
26
|
/** Perform an MCP tool call; return its textual result. Throw to surface an error to the model. */
|
|
18
|
-
type McpCall = (name: string, args: any) => Promise<unknown>;
|
|
27
|
+
type McpCall = (name: string, args: any, opts?: McpCallOptions) => Promise<unknown>;
|
|
19
28
|
interface McpImage {
|
|
20
29
|
mimeType: string;
|
|
21
30
|
data: string;
|
|
@@ -54,7 +63,7 @@ interface MountedMcpLike {
|
|
|
54
63
|
name: string;
|
|
55
64
|
specs: McpToolSpec[];
|
|
56
65
|
client: {
|
|
57
|
-
callTool(name: string, args: unknown): Promise<unknown>;
|
|
66
|
+
callTool(name: string, args: unknown, opts?: McpCallOptions): Promise<unknown>;
|
|
58
67
|
};
|
|
59
68
|
}
|
|
60
69
|
/** A flattened catalog entry's origin: which server owns it + the un-prefixed raw tool name. */
|
|
@@ -73,7 +82,7 @@ declare function buildMcpCatalog(servers: {
|
|
|
73
82
|
routes: Map<string, McpRoute>;
|
|
74
83
|
};
|
|
75
84
|
/** Resolve a routed MCP call to its result. Throw to surface an error to the model. */
|
|
76
|
-
type McpRouteResolver = (server: string, rawName: string, args: any) => Promise<unknown>;
|
|
85
|
+
type McpRouteResolver = (server: string, rawName: string, args: any, opts?: McpCallOptions) => Promise<unknown>;
|
|
77
86
|
/**
|
|
78
87
|
* Ergonomic deferred-mount over already-mounted MCP servers: flattens their specs into one
|
|
79
88
|
* `mcp__<server>__<tool>` namespace and wires a single `ToolSearch`/`McpCall` pair that routes
|
package/dist/mcp.client.d.ts
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
|
-
import { e as McpToolSearchOptions, f as McpToolSpec } from './mcp-
|
|
2
|
-
import { A as AgentTool } from './tools-
|
|
1
|
+
import { e as McpToolSearchOptions, f as McpToolSpec } from './mcp-DjYZzGGW.js';
|
|
2
|
+
import { A as AgentTool } from './tools-MNdqlJIa.js';
|
|
3
3
|
import '@livx.cc/wcli/core';
|
|
4
4
|
|
|
5
|
+
/** Hard cap for `tools/call`. NOT the 30s protocol timeout: a tool call is real work (an image render, a crawl)
|
|
6
|
+
* that legitimately runs for minutes — the harness hands it to a job past its soft timeout and judges it by
|
|
7
|
+
* progress/idle liveness (src/tools.jobs.ts). This only bounds a call nothing else ever stops. */
|
|
8
|
+
declare const DEFAULT_CALL_TIMEOUT_MS: number;
|
|
9
|
+
/** An MCP `notifications/progress` payload. */
|
|
10
|
+
interface McpProgress {
|
|
11
|
+
progress?: number;
|
|
12
|
+
total?: number;
|
|
13
|
+
message?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Per-request options: cancellation, progress (sends a `_meta.progressToken`), and a deadline override. */
|
|
16
|
+
interface McpRequestOptions {
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
onProgress?: (p: McpProgress) => void;
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
5
21
|
/** A JSON-RPC 2.0 transport: request (awaits a result), notify (fire-and-forget), close. */
|
|
6
22
|
interface McpTransport {
|
|
7
23
|
start(): Promise<void>;
|
|
8
|
-
request(method: string, params?: unknown): Promise<any>;
|
|
24
|
+
request(method: string, params?: unknown, opts?: McpRequestOptions): Promise<any>;
|
|
9
25
|
notify(method: string, params?: unknown): Promise<void>;
|
|
10
26
|
close(): Promise<void>;
|
|
11
27
|
}
|
|
@@ -14,7 +30,10 @@ interface StdioServerSpec {
|
|
|
14
30
|
args?: string[];
|
|
15
31
|
env?: Record<string, string>;
|
|
16
32
|
cwd?: string;
|
|
33
|
+
/** Protocol request deadline (initialize, tools/list, …). Default 30s. */
|
|
17
34
|
timeoutMs?: number;
|
|
35
|
+
/** `tools/call` hard cap. Default max(30 min, timeoutMs). */
|
|
36
|
+
callTimeoutMs?: number;
|
|
18
37
|
}
|
|
19
38
|
declare class StdioTransport implements McpTransport {
|
|
20
39
|
private spec;
|
|
@@ -28,7 +47,7 @@ declare class StdioTransport implements McpTransport {
|
|
|
28
47
|
private dispatch;
|
|
29
48
|
private failAll;
|
|
30
49
|
private write;
|
|
31
|
-
request(method: string, params?: unknown): Promise<any>;
|
|
50
|
+
request(method: string, params?: unknown, opts?: McpRequestOptions): Promise<any>;
|
|
32
51
|
notify(method: string, params?: unknown): Promise<void>;
|
|
33
52
|
close(): Promise<void>;
|
|
34
53
|
}
|
|
@@ -38,7 +57,10 @@ interface HttpServerSpec {
|
|
|
38
57
|
/** Sugar over `headers` → `Authorization: Bearer <token>`. The headless-friendly path (no OAuth loopback);
|
|
39
58
|
* inject a static token (from env / VFS secret) per our CLAUDE.md rule preferring API keys for cron/edge. */
|
|
40
59
|
bearerToken?: string;
|
|
60
|
+
/** Protocol request deadline (initialize, tools/list, …). Default 30s. */
|
|
41
61
|
timeoutMs?: number;
|
|
62
|
+
/** `tools/call` hard cap. Default max(30 min, timeoutMs). */
|
|
63
|
+
callTimeoutMs?: number;
|
|
42
64
|
}
|
|
43
65
|
/** Thrown when an HTTP MCP server rejects auth (401/403). Carries `needsAuth` so callers can surface a hint
|
|
44
66
|
* ("set bearerToken / headers") instead of a generic mount failure — without attempting an interactive flow. */
|
|
@@ -55,7 +77,7 @@ declare class HttpTransport implements McpTransport {
|
|
|
55
77
|
private fetchImpl;
|
|
56
78
|
constructor(spec: HttpServerSpec, fetchImpl?: typeof fetch);
|
|
57
79
|
start(): Promise<void>;
|
|
58
|
-
request(method: string, params?: unknown): Promise<any>;
|
|
80
|
+
request(method: string, params?: unknown, opts?: McpRequestOptions): Promise<any>;
|
|
59
81
|
notify(method: string, params?: unknown): Promise<void>;
|
|
60
82
|
private post;
|
|
61
83
|
close(): Promise<void>;
|
|
@@ -76,7 +98,8 @@ declare class McpClient {
|
|
|
76
98
|
capabilities?: any;
|
|
77
99
|
}>;
|
|
78
100
|
listTools(): Promise<McpToolSpec[]>;
|
|
79
|
-
|
|
101
|
+
/** `tools/call`. Pass `onProgress` to request progress notifications (liveness) and `signal` to cancel. */
|
|
102
|
+
callTool(name: string, args: unknown, opts?: McpRequestOptions): Promise<unknown>;
|
|
80
103
|
listResources(): Promise<Array<{
|
|
81
104
|
uri: string;
|
|
82
105
|
name?: string;
|
|
@@ -98,7 +121,10 @@ interface McpServerConfig {
|
|
|
98
121
|
/** Auth strategy for http servers. `'oauth'` → the CLI resolves a token via the attended OAuth flow
|
|
99
122
|
* (cli/mcpOAuth.ts) and injects it as `bearerToken` at mount; the core never runs the flow. Default none. */
|
|
100
123
|
auth?: 'oauth' | 'none';
|
|
124
|
+
/** Protocol request deadline (initialize, tools/list, …). Default 30s. */
|
|
101
125
|
timeoutMs?: number;
|
|
126
|
+
/** `tools/call` hard cap (the call is handed to a job long before; this bounds what nothing else stops). Default 30 min. */
|
|
127
|
+
callTimeoutMs?: number;
|
|
102
128
|
disabled?: boolean;
|
|
103
129
|
}
|
|
104
130
|
interface MountedMcp {
|
|
@@ -225,4 +251,4 @@ declare function warmMcpCatalog(servers?: Record<string, McpServerConfig>, opts?
|
|
|
225
251
|
toolCount: number;
|
|
226
252
|
}>;
|
|
227
253
|
|
|
228
|
-
export { type HttpServerSpec, HttpTransport, McpAuthError, type McpCatalogOptions, type McpCatalogStore, McpClient, McpPool, type McpServerConfig, type McpTransport, MemMcpCatalog, type MountedMcp, type StdioServerSpec, StdioTransport, mcpConfigKey, mountMcpCatalog, mountMcpDeferred, mountMcpServer, mountMcpServers, warmMcpCatalog };
|
|
254
|
+
export { DEFAULT_CALL_TIMEOUT_MS, type HttpServerSpec, HttpTransport, McpAuthError, type McpCatalogOptions, type McpCatalogStore, McpClient, McpPool, type McpProgress, type McpRequestOptions, type McpServerConfig, type McpTransport, MemMcpCatalog, type MountedMcp, type StdioServerSpec, StdioTransport, mcpConfigKey, mountMcpCatalog, mountMcpDeferred, mountMcpServer, mountMcpServers, warmMcpCatalog };
|
package/dist/mcp.client.js
CHANGED
|
@@ -106,17 +106,42 @@ async function withDeadline(p, ms) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
var mcpTimedOut = (name, ms = MCP_CALL_TIMEOUT_MS) => `[${name}] no response after ${ms / 1e3}s \u2014 the MCP server never answered. The call's outcome is UNKNOWN: do not assume it ran or that it failed. Verify the state (or pick a bounded/narrower call) before retrying.`;
|
|
109
|
+
async function runMcpCall(name, call, ctx, timeoutMs) {
|
|
110
|
+
const jobs = ctx?.toolJobs;
|
|
111
|
+
const shape = (raw) => {
|
|
112
|
+
const r2 = toResult(raw);
|
|
113
|
+
return r2.images?.length ? r2 : r2.text;
|
|
114
|
+
};
|
|
115
|
+
if (!jobs) {
|
|
116
|
+
const raw = await withDeadline(call({}), timeoutMs);
|
|
117
|
+
return raw === TIMED_OUT ? mcpTimedOut(name, timeoutMs) : shape(raw);
|
|
118
|
+
}
|
|
119
|
+
const r = await jobs.runForeground(async ({ signal, touch, onChunk }) => {
|
|
120
|
+
const raw = await call({
|
|
121
|
+
signal,
|
|
122
|
+
onProgress: (p) => {
|
|
123
|
+
touch();
|
|
124
|
+
if (p?.message) onChunk(`[progress${p.progress != null ? ` ${p.progress}${p.total != null ? `/${p.total}` : ""}` : ""}] ${p.message}
|
|
125
|
+
`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
const res = toResult(raw);
|
|
129
|
+
onChunk(res.text + (res.images?.length ? `
|
|
130
|
+
[${res.images.length} image(s) in the result]` : ""));
|
|
131
|
+
return res;
|
|
132
|
+
}, { kind: "mcp", label: name, idleAfterActivity: true });
|
|
133
|
+
if (r.done) return r.value.images?.length ? r.value : r.value.text;
|
|
134
|
+
const soft = jobs.options.foregroundMs.mcp ?? 0;
|
|
135
|
+
return `[still running] MCP call ${name} is still working after ${soft >= 1e3 ? `${Math.round(soft / 1e3)}s` : `${soft}ms`}, so it was handed to background job ${r.id} \u2014 NOT cancelled, the server keeps going. Its result will be delivered to you when it finishes, so continue with other work; to block on it use JobWait({id:"${r.id}"}) instead of sleeping. JobStatus/JobOutput show its progress; JobKill({id:"${r.id}"}) cancels it. Do NOT re-issue the same call \u2014 that would run it twice.`;
|
|
136
|
+
}
|
|
109
137
|
function mcpToolToAgentTool(spec, callTool, prefix = "mcp__", timeoutMs = MCP_CALL_TIMEOUT_MS) {
|
|
110
138
|
return {
|
|
111
139
|
name: `${prefix}${spec.name}`,
|
|
112
140
|
description: spec.description ?? `MCP tool ${spec.name}`,
|
|
113
141
|
parameters: spec.inputSchema ?? { type: "object", properties: {} },
|
|
114
142
|
maxDurationMs: timeoutMs,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (raw === TIMED_OUT) return mcpTimedOut(spec.name, timeoutMs);
|
|
118
|
-
const r = toResult(raw);
|
|
119
|
-
return r.images?.length ? r : r.text;
|
|
143
|
+
run(args, ctx) {
|
|
144
|
+
return runMcpCall(spec.name, (o) => callTool(spec.name, args ?? {}, o), ctx, timeoutMs);
|
|
120
145
|
}
|
|
121
146
|
};
|
|
122
147
|
}
|
|
@@ -157,13 +182,10 @@ function makeMcpToolSearch(specs, callTool, options = {}) {
|
|
|
157
182
|
}
|
|
158
183
|
},
|
|
159
184
|
maxDurationMs: callTimeoutMs,
|
|
160
|
-
async run({ name, args }) {
|
|
185
|
+
async run({ name, args }, ctx) {
|
|
161
186
|
const n = String(name ?? "");
|
|
162
187
|
if (!byName.has(n)) return `Error: unknown MCP tool '${n}'. Use ToolSearch to find valid names.`;
|
|
163
|
-
|
|
164
|
-
if (raw === TIMED_OUT) return mcpTimedOut(n, callTimeoutMs);
|
|
165
|
-
const r = toResult(raw);
|
|
166
|
-
return r.images?.length ? r : r.text;
|
|
188
|
+
return runMcpCall(n, (o) => callTool(n, args ?? {}, o), ctx, callTimeoutMs);
|
|
167
189
|
}
|
|
168
190
|
};
|
|
169
191
|
return [searchTool, callMcpTool];
|
|
@@ -183,17 +205,17 @@ function buildMcpCatalog(servers) {
|
|
|
183
205
|
return { specs, routes };
|
|
184
206
|
}
|
|
185
207
|
function searchOverCatalog(servers, specs, routes, resolve, options) {
|
|
186
|
-
const tools = specs.length ? makeMcpToolSearch(specs, (name, args) => {
|
|
208
|
+
const tools = specs.length ? makeMcpToolSearch(specs, (name, args, o) => {
|
|
187
209
|
const r = routes.get(name);
|
|
188
210
|
if (!r) throw new Error(`unknown MCP tool '${name}' \u2014 use ToolSearch to find valid names`);
|
|
189
|
-
return resolve(r.server, r.rawName, args ?? {});
|
|
211
|
+
return resolve(r.server, r.rawName, args ?? {}, o);
|
|
190
212
|
}, options) : [];
|
|
191
213
|
return { tools, serverNames: servers, toolCount: specs.length };
|
|
192
214
|
}
|
|
193
215
|
function makeMcpToolSearchFromMounted(mounted, options) {
|
|
194
216
|
const { specs, routes } = buildMcpCatalog(mounted);
|
|
195
217
|
const byName = new Map(mounted.map((m) => [m.name, m]));
|
|
196
|
-
return searchOverCatalog(mounted.map((m) => m.name), specs, routes, (server, rawName, args) => byName.get(server).client.callTool(rawName, args), options);
|
|
218
|
+
return searchOverCatalog(mounted.map((m) => m.name), specs, routes, (server, rawName, args, o) => byName.get(server).client.callTool(rawName, args, o), options);
|
|
197
219
|
}
|
|
198
220
|
function makeLazyMcpToolSearch(servers, resolve, options) {
|
|
199
221
|
const { specs, routes } = buildMcpCatalog(servers);
|
|
@@ -204,6 +226,17 @@ function makeLazyMcpToolSearch(servers, resolve, options) {
|
|
|
204
226
|
var log2 = forComponent("mcp");
|
|
205
227
|
var PROTOCOL_VERSION = "2025-06-18";
|
|
206
228
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
229
|
+
var DEFAULT_CALL_TIMEOUT_MS = 30 * 6e4;
|
|
230
|
+
function deadlineFor(method, spec, opts) {
|
|
231
|
+
if (opts?.timeoutMs != null) return opts.timeoutMs;
|
|
232
|
+
if (method === "tools/call") return spec.callTimeoutMs ?? Math.max(DEFAULT_CALL_TIMEOUT_MS, spec.timeoutMs ?? 0);
|
|
233
|
+
return spec.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
234
|
+
}
|
|
235
|
+
function withProgressToken(params, token) {
|
|
236
|
+
const p = params && typeof params === "object" ? params : {};
|
|
237
|
+
return { ...p, _meta: { ...p._meta ?? {}, progressToken: token } };
|
|
238
|
+
}
|
|
239
|
+
var abortError = (signal) => signal.reason instanceof Error ? signal.reason : Object.assign(new Error("MCP request cancelled"), { name: "AbortError" });
|
|
207
240
|
var StdioTransport = class {
|
|
208
241
|
constructor(spec) {
|
|
209
242
|
this.spec = spec;
|
|
@@ -239,16 +272,27 @@ var StdioTransport = class {
|
|
|
239
272
|
}
|
|
240
273
|
}
|
|
241
274
|
dispatch(msg) {
|
|
275
|
+
if (msg?.method === "notifications/progress") {
|
|
276
|
+
const p2 = this.pending.get(msg.params?.progressToken);
|
|
277
|
+
try {
|
|
278
|
+
p2?.onProgress?.(msg.params ?? {});
|
|
279
|
+
} catch (e) {
|
|
280
|
+
log2.debug("onProgress handler threw", e);
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
242
284
|
if (msg?.id == null || !this.pending.has(msg.id)) return;
|
|
243
285
|
const p = this.pending.get(msg.id);
|
|
244
286
|
this.pending.delete(msg.id);
|
|
245
287
|
clearTimeout(p.timer);
|
|
288
|
+
p.done?.();
|
|
246
289
|
if (msg.error) p.reject(new Error(msg.error?.message ?? JSON.stringify(msg.error)));
|
|
247
290
|
else p.resolve(msg.result);
|
|
248
291
|
}
|
|
249
292
|
failAll(e) {
|
|
250
293
|
for (const p of this.pending.values()) {
|
|
251
294
|
clearTimeout(p.timer);
|
|
295
|
+
p.done?.();
|
|
252
296
|
p.reject(e);
|
|
253
297
|
}
|
|
254
298
|
this.pending.clear();
|
|
@@ -257,19 +301,31 @@ var StdioTransport = class {
|
|
|
257
301
|
if (!this.proc?.stdin) throw new Error("MCP stdio transport not started");
|
|
258
302
|
this.proc.stdin.write(JSON.stringify(msg) + "\n");
|
|
259
303
|
}
|
|
260
|
-
request(method, params) {
|
|
304
|
+
request(method, params, opts = {}) {
|
|
261
305
|
const id = this.nextId++;
|
|
262
|
-
const timeoutMs = this.spec
|
|
306
|
+
const timeoutMs = deadlineFor(method, this.spec, opts);
|
|
307
|
+
const { signal, onProgress } = opts;
|
|
263
308
|
return new Promise((resolve, reject) => {
|
|
264
|
-
|
|
309
|
+
if (signal?.aborted) return reject(abortError(signal));
|
|
310
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
265
311
|
this.pending.delete(id);
|
|
312
|
+
done();
|
|
266
313
|
reject(new Error(`MCP request "${method}" timed out after ${timeoutMs}ms`));
|
|
267
|
-
}, timeoutMs);
|
|
268
|
-
|
|
314
|
+
}, timeoutMs) : void 0;
|
|
315
|
+
const onAbort = () => {
|
|
316
|
+
if (!this.pending.delete(id)) return;
|
|
317
|
+
clearTimeout(timer);
|
|
318
|
+
this.notify("notifications/cancelled", { requestId: id, reason: String(signal?.reason?.message ?? "cancelled") }).catch((e) => log2.debug("cancel notify failed", e));
|
|
319
|
+
reject(abortError(signal));
|
|
320
|
+
};
|
|
321
|
+
const done = () => signal?.removeEventListener("abort", onAbort);
|
|
322
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
323
|
+
this.pending.set(id, { resolve, reject, timer, onProgress, done });
|
|
269
324
|
try {
|
|
270
|
-
this.write({ jsonrpc: "2.0", id, method, params });
|
|
325
|
+
this.write({ jsonrpc: "2.0", id, method, params: onProgress ? withProgressToken(params, id) : params });
|
|
271
326
|
} catch (e) {
|
|
272
327
|
clearTimeout(timer);
|
|
328
|
+
done();
|
|
273
329
|
this.pending.delete(id);
|
|
274
330
|
reject(e);
|
|
275
331
|
}
|
|
@@ -310,13 +366,24 @@ var HttpTransport = class {
|
|
|
310
366
|
fetchImpl;
|
|
311
367
|
async start() {
|
|
312
368
|
}
|
|
313
|
-
request(method, params) {
|
|
314
|
-
|
|
369
|
+
async request(method, params, opts = {}) {
|
|
370
|
+
const id = this.nextId++;
|
|
371
|
+
const deadline = AbortSignal.timeout(deadlineFor(method, this.spec, opts));
|
|
372
|
+
const signal = opts.signal ? AbortSignal.any([opts.signal, deadline]) : deadline;
|
|
373
|
+
try {
|
|
374
|
+
return await this.post({ jsonrpc: "2.0", id, method, params: opts.onProgress ? withProgressToken(params, id) : params }, false, signal, opts.onProgress);
|
|
375
|
+
} catch (e) {
|
|
376
|
+
if (opts.signal?.aborted) {
|
|
377
|
+
void this.notify("notifications/cancelled", { requestId: id, reason: String(opts.signal.reason?.message ?? "cancelled") }).catch((err) => log2.debug("cancel notify failed", err));
|
|
378
|
+
throw abortError(opts.signal);
|
|
379
|
+
}
|
|
380
|
+
throw e;
|
|
381
|
+
}
|
|
315
382
|
}
|
|
316
383
|
async notify(method, params) {
|
|
317
|
-
await this.post({ jsonrpc: "2.0", method, params }, true);
|
|
384
|
+
await this.post({ jsonrpc: "2.0", method, params }, true, AbortSignal.timeout(this.spec.timeoutMs ?? DEFAULT_TIMEOUT_MS));
|
|
318
385
|
}
|
|
319
|
-
async post(msg, isNotify) {
|
|
386
|
+
async post(msg, isNotify, signal, onProgress) {
|
|
320
387
|
const headers = {
|
|
321
388
|
"content-type": "application/json",
|
|
322
389
|
accept: "application/json, text/event-stream",
|
|
@@ -329,7 +396,7 @@ var HttpTransport = class {
|
|
|
329
396
|
method: "POST",
|
|
330
397
|
headers,
|
|
331
398
|
body: JSON.stringify(msg),
|
|
332
|
-
signal
|
|
399
|
+
signal
|
|
333
400
|
});
|
|
334
401
|
const sid = resp.headers.get("mcp-session-id");
|
|
335
402
|
if (sid) this.sessionId = sid;
|
|
@@ -337,25 +404,63 @@ var HttpTransport = class {
|
|
|
337
404
|
if (!resp.ok) throw new Error(`MCP HTTP ${resp.status} ${resp.statusText}`);
|
|
338
405
|
if (isNotify) return;
|
|
339
406
|
const ct = resp.headers.get("content-type") ?? "";
|
|
340
|
-
const data = ct.includes("text/event-stream") ?
|
|
407
|
+
const data = ct.includes("text/event-stream") ? await readSseResponse(resp, msg.id, onProgress) : await resp.json();
|
|
341
408
|
if (data?.error) throw new Error(data.error?.message ?? JSON.stringify(data.error));
|
|
342
409
|
return data?.result;
|
|
343
410
|
}
|
|
344
411
|
async close() {
|
|
345
412
|
}
|
|
346
413
|
};
|
|
347
|
-
function
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
414
|
+
function sseMessage(line) {
|
|
415
|
+
const trimmed = line.trimStart();
|
|
416
|
+
if (!trimmed.startsWith("data:")) return void 0;
|
|
417
|
+
try {
|
|
418
|
+
return JSON.parse(trimmed.slice(5).trim());
|
|
419
|
+
} catch (e) {
|
|
420
|
+
log2.debug("skipping unparseable SSE data line", e);
|
|
421
|
+
return void 0;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function readSseResponse(resp, id, onProgress) {
|
|
425
|
+
const handle = (obj) => {
|
|
426
|
+
if (obj?.method === "notifications/progress") {
|
|
427
|
+
try {
|
|
428
|
+
onProgress?.(obj.params ?? {});
|
|
429
|
+
} catch (e) {
|
|
430
|
+
log2.debug("onProgress handler threw", e);
|
|
431
|
+
}
|
|
432
|
+
return void 0;
|
|
356
433
|
}
|
|
434
|
+
if (obj && (obj.result !== void 0 || obj.error !== void 0) && (obj.id == null || id == null || obj.id === id)) return obj;
|
|
435
|
+
return void 0;
|
|
436
|
+
};
|
|
437
|
+
const reader = resp.body?.getReader?.();
|
|
438
|
+
if (!reader) {
|
|
439
|
+
for (const l of (await resp.text()).split("\n")) {
|
|
440
|
+
const r = handle(sseMessage(l));
|
|
441
|
+
if (r) return r;
|
|
442
|
+
}
|
|
443
|
+
return {};
|
|
444
|
+
}
|
|
445
|
+
const dec = new TextDecoder();
|
|
446
|
+
let buf = "";
|
|
447
|
+
try {
|
|
448
|
+
for (; ; ) {
|
|
449
|
+
const { value, done } = await reader.read();
|
|
450
|
+
buf += done ? dec.decode() : dec.decode(value, { stream: true });
|
|
451
|
+
let nl;
|
|
452
|
+
while ((nl = buf.indexOf("\n")) >= 0 || done && buf) {
|
|
453
|
+
const line = nl >= 0 ? buf.slice(0, nl) : buf;
|
|
454
|
+
buf = nl >= 0 ? buf.slice(nl + 1) : "";
|
|
455
|
+
const r = handle(sseMessage(line));
|
|
456
|
+
if (r) return r;
|
|
457
|
+
}
|
|
458
|
+
if (done) return {};
|
|
459
|
+
}
|
|
460
|
+
} finally {
|
|
461
|
+
reader.cancel().catch(() => {
|
|
462
|
+
});
|
|
357
463
|
}
|
|
358
|
-
return {};
|
|
359
464
|
}
|
|
360
465
|
var McpClient = class {
|
|
361
466
|
constructor(transport, clientInfo = { name: "agentx", version: "0" }) {
|
|
@@ -379,8 +484,9 @@ var McpClient = class {
|
|
|
379
484
|
const r = await this.transport.request("tools/list");
|
|
380
485
|
return Array.isArray(r?.tools) ? r.tools : [];
|
|
381
486
|
}
|
|
382
|
-
|
|
383
|
-
|
|
487
|
+
/** `tools/call`. Pass `onProgress` to request progress notifications (liveness) and `signal` to cancel. */
|
|
488
|
+
async callTool(name, args, opts) {
|
|
489
|
+
return this.transport.request("tools/call", { name, arguments: args ?? {} }, opts);
|
|
384
490
|
}
|
|
385
491
|
async listResources() {
|
|
386
492
|
const r = await this.transport.request("resources/list");
|
|
@@ -394,7 +500,7 @@ var McpClient = class {
|
|
|
394
500
|
}
|
|
395
501
|
};
|
|
396
502
|
function buildTransport(cfg) {
|
|
397
|
-
return cfg.url ? new HttpTransport({ url: cfg.url, headers: cfg.headers, bearerToken: cfg.bearerToken, timeoutMs: cfg.timeoutMs }) : new StdioTransport({ command: cfg.command, args: cfg.args, env: cfg.env, cwd: cfg.cwd, timeoutMs: cfg.timeoutMs });
|
|
503
|
+
return cfg.url ? new HttpTransport({ url: cfg.url, headers: cfg.headers, bearerToken: cfg.bearerToken, timeoutMs: cfg.timeoutMs, callTimeoutMs: cfg.callTimeoutMs }) : new StdioTransport({ command: cfg.command, args: cfg.args, env: cfg.env, cwd: cfg.cwd, timeoutMs: cfg.timeoutMs, callTimeoutMs: cfg.callTimeoutMs });
|
|
398
504
|
}
|
|
399
505
|
function withTimeout(p, ms, label) {
|
|
400
506
|
if (!ms || ms <= 0) return p;
|
|
@@ -416,7 +522,7 @@ async function mountWithDeadline(name, cfg, mountTimeoutMs) {
|
|
|
416
522
|
return await withTimeout((async () => {
|
|
417
523
|
const init = await client.connect();
|
|
418
524
|
const specs = await client.listTools();
|
|
419
|
-
const tools = mcpToolsToAgentTools(specs, (tool, a) => client.callTool(tool, a), `mcp__${name}__`);
|
|
525
|
+
const tools = mcpToolsToAgentTools(specs, (tool, a, o) => client.callTool(tool, a, o), `mcp__${name}__`);
|
|
420
526
|
return { name, client, tools, specs, serverInfo: init?.serverInfo, config: cfg };
|
|
421
527
|
})(), mountTimeoutMs, name);
|
|
422
528
|
} catch (e) {
|
|
@@ -599,7 +705,7 @@ async function mountMcpCatalog(servers = {}, opts = {}) {
|
|
|
599
705
|
};
|
|
600
706
|
const search = makeLazyMcpToolSearch(
|
|
601
707
|
discovered.map((d) => ({ name: d.name, specs: d.specs })),
|
|
602
|
-
async (server, rawName, args) => (await connect(server)).callTool(rawName, args),
|
|
708
|
+
async (server, rawName, args, o) => (await connect(server)).callTool(rawName, args, o),
|
|
603
709
|
opts
|
|
604
710
|
);
|
|
605
711
|
const close = async () => {
|
|
@@ -621,6 +727,7 @@ async function warmMcpCatalog(servers = {}, opts = {}) {
|
|
|
621
727
|
};
|
|
622
728
|
}
|
|
623
729
|
export {
|
|
730
|
+
DEFAULT_CALL_TIMEOUT_MS,
|
|
624
731
|
HttpTransport,
|
|
625
732
|
McpAuthError,
|
|
626
733
|
McpClient,
|