@j0hanz/fetch-url-mcp 1.10.2 → 1.10.4
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/dist/http/native.d.ts.map +1 -1
- package/dist/http/native.js +20 -26
- package/dist/http/session-teardown.d.ts +17 -0
- package/dist/http/session-teardown.d.ts.map +1 -0
- package/dist/http/session-teardown.js +30 -0
- package/dist/lib/code-lang.d.ts.map +1 -1
- package/dist/lib/code-lang.js +2 -8
- package/dist/lib/core.d.ts.map +1 -1
- package/dist/lib/core.js +2 -0
- package/dist/lib/dom-prep.d.ts +2 -0
- package/dist/lib/dom-prep.d.ts.map +1 -1
- package/dist/lib/dom-prep.js +150 -139
- package/dist/lib/md-cleanup.d.ts.map +1 -1
- package/dist/lib/md-cleanup.js +53 -20
- package/dist/lib/progress.d.ts.map +1 -1
- package/dist/lib/progress.js +59 -32
- package/dist/lib/sdk-interop.d.ts +8 -0
- package/dist/lib/sdk-interop.d.ts.map +1 -0
- package/dist/lib/sdk-interop.js +73 -0
- package/dist/lib/task-handlers.d.ts +0 -2
- package/dist/lib/task-handlers.d.ts.map +1 -1
- package/dist/lib/task-handlers.js +9 -94
- package/dist/resources/index.js +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -3
- package/dist/tasks/call-contract.d.ts +25 -0
- package/dist/tasks/call-contract.d.ts.map +1 -0
- package/dist/tasks/call-contract.js +58 -0
- package/dist/tasks/cursor-codec.d.ts +5 -0
- package/dist/tasks/cursor-codec.d.ts.map +1 -0
- package/dist/tasks/cursor-codec.js +41 -0
- package/dist/tasks/execution.d.ts +1 -20
- package/dist/tasks/execution.d.ts.map +1 -1
- package/dist/tasks/execution.js +7 -32
- package/dist/tasks/manager.d.ts +1 -5
- package/dist/tasks/manager.d.ts.map +1 -1
- package/dist/tasks/manager.js +16 -144
- package/dist/tasks/owner.d.ts +12 -2
- package/dist/tasks/owner.d.ts.map +1 -1
- package/dist/tasks/owner.js +52 -3
- package/dist/tasks/tool-registry.d.ts +1 -0
- package/dist/tasks/tool-registry.d.ts.map +1 -1
- package/dist/tasks/waiters.d.ts +27 -0
- package/dist/tasks/waiters.d.ts.map +1 -0
- package/dist/tasks/waiters.js +113 -0
- package/dist/tools/fetch-url-progress.d.ts +17 -0
- package/dist/tools/fetch-url-progress.d.ts.map +1 -0
- package/dist/tools/fetch-url-progress.js +78 -0
- package/dist/tools/fetch-url.d.ts +5 -9
- package/dist/tools/fetch-url.d.ts.map +1 -1
- package/dist/tools/fetch-url.js +43 -96
- package/dist/transform/next-flight.d.ts +2 -0
- package/dist/transform/next-flight.d.ts.map +1 -0
- package/dist/transform/next-flight.js +285 -0
- package/dist/transform/title-policy.d.ts +12 -0
- package/dist/transform/title-policy.d.ts.map +1 -0
- package/dist/transform/title-policy.js +85 -0
- package/dist/transform/transform.d.ts.map +1 -1
- package/dist/transform/transform.js +62 -475
- package/package.json +4 -4
- package/dist/tools/index.d.ts +0 -3
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -4
package/dist/tasks/execution.js
CHANGED
|
@@ -3,8 +3,9 @@ import { config } from '../lib/core.js';
|
|
|
3
3
|
import { logWarn, runWithRequestContext } from '../lib/core.js';
|
|
4
4
|
import { getErrorMessage } from '../lib/utils.js';
|
|
5
5
|
import { isObject } from '../lib/utils.js';
|
|
6
|
+
import { buildRelatedTaskMeta, } from './call-contract.js';
|
|
6
7
|
import { taskManager, } from './manager.js';
|
|
7
|
-
import { compact, tryReadToolStructuredError, } from './owner.js';
|
|
8
|
+
import { buildToolHandlerExtra, compact, tryReadToolStructuredError, } from './owner.js';
|
|
8
9
|
import { getTaskCapableTool, hasTaskCapableTool, } from './tool-registry.js';
|
|
9
10
|
const TASK_NOT_FOUND_ERROR_CODE = ErrorCode.InvalidParams;
|
|
10
11
|
/* -------------------------------------------------------------------------------------------------
|
|
@@ -61,18 +62,6 @@ export function toTaskSummary(task) {
|
|
|
61
62
|
pollInterval: task.pollInterval,
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
|
-
export function withRelatedTaskMeta(result, taskId) {
|
|
65
|
-
const relatedTaskMeta = {
|
|
66
|
-
'io.modelcontextprotocol/related-task': { taskId },
|
|
67
|
-
};
|
|
68
|
-
return {
|
|
69
|
-
...result,
|
|
70
|
-
_meta: {
|
|
71
|
-
...result._meta,
|
|
72
|
-
...relatedTaskMeta,
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
65
|
export function emitTaskStatusNotification(server, task) {
|
|
77
66
|
if (!config.tasks.emitStatusNotifications)
|
|
78
67
|
return;
|
|
@@ -124,15 +113,6 @@ function assertKnownTool(name) {
|
|
|
124
113
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: '${name}'`);
|
|
125
114
|
}
|
|
126
115
|
}
|
|
127
|
-
/* -------------------------------------------------------------------------------------------------
|
|
128
|
-
* Task result builders
|
|
129
|
-
* ------------------------------------------------------------------------------------------------- */
|
|
130
|
-
function buildRelatedTaskMeta(taskId, meta) {
|
|
131
|
-
return {
|
|
132
|
-
...(meta ?? {}),
|
|
133
|
-
'io.modelcontextprotocol/related-task': { taskId },
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
116
|
function buildCreateTaskResult(task) {
|
|
137
117
|
return { task };
|
|
138
118
|
}
|
|
@@ -178,13 +158,14 @@ function resolveToolAndArgs(params) {
|
|
|
178
158
|
const args = tool.parseArguments(params.arguments);
|
|
179
159
|
return { tool, args };
|
|
180
160
|
}
|
|
181
|
-
function buildTaskCompletionUpdate(result) {
|
|
161
|
+
function buildTaskCompletionUpdate(result, tool) {
|
|
182
162
|
const isToolError = isObject(result) && 'isError' in result && result.isError === true;
|
|
183
163
|
return {
|
|
184
164
|
status: isToolError ? 'failed' : 'completed',
|
|
185
165
|
statusMessage: isToolError
|
|
186
166
|
? (tryReadToolStructuredError(result) ?? 'Tool execution failed')
|
|
187
|
-
:
|
|
167
|
+
: (tool.getCompletionStatusMessage?.(result) ??
|
|
168
|
+
'Task completed successfully.'),
|
|
188
169
|
result,
|
|
189
170
|
};
|
|
190
171
|
}
|
|
@@ -208,7 +189,7 @@ async function runTaskToolExecution(params) {
|
|
|
208
189
|
updateWorkingTaskStatus(server, taskId, message);
|
|
209
190
|
},
|
|
210
191
|
});
|
|
211
|
-
updateTaskAndEmitStatus(server, taskId, buildTaskCompletionUpdate(result));
|
|
192
|
+
updateTaskAndEmitStatus(server, taskId, buildTaskCompletionUpdate(result, tool));
|
|
212
193
|
}
|
|
213
194
|
catch (error) {
|
|
214
195
|
const failure = buildTaskFailureState(error);
|
|
@@ -241,13 +222,7 @@ function handleTaskToolCall(server, params, context) {
|
|
|
241
222
|
}
|
|
242
223
|
async function handleDirectToolCall(params, context) {
|
|
243
224
|
const { tool, args } = resolveToolAndArgs(params);
|
|
244
|
-
|
|
245
|
-
signal: context.signal,
|
|
246
|
-
requestId: context.requestId,
|
|
247
|
-
sendNotification: context.sendNotification,
|
|
248
|
-
_meta: params._meta,
|
|
249
|
-
});
|
|
250
|
-
return tool.execute(args, extra);
|
|
225
|
+
return tool.execute(args, buildToolHandlerExtra(context, params._meta));
|
|
251
226
|
}
|
|
252
227
|
export async function handleToolCallRequest(server, request, context) {
|
|
253
228
|
const { params } = request;
|
package/dist/tasks/manager.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface CreateTaskResult {
|
|
|
34
34
|
declare class TaskManager {
|
|
35
35
|
private tasks;
|
|
36
36
|
private ownerCounts;
|
|
37
|
-
private waiters;
|
|
37
|
+
private readonly waiters;
|
|
38
38
|
private cleanupInterval;
|
|
39
39
|
private ensureCleanupLoop;
|
|
40
40
|
private stopCleanupLoop;
|
|
@@ -63,15 +63,11 @@ declare class TaskManager {
|
|
|
63
63
|
nextCursor?: string;
|
|
64
64
|
};
|
|
65
65
|
private resolveAnchorTaskId;
|
|
66
|
-
private addWaiter;
|
|
67
|
-
private removeWaiter;
|
|
68
66
|
waitForTerminalTask(taskId: string, ownerKey: string, signal?: AbortSignal): Promise<TaskState | undefined>;
|
|
69
67
|
private notifyWaiters;
|
|
70
68
|
private isExpired;
|
|
71
69
|
private maybeUpdateLastUpdatedAt;
|
|
72
70
|
shrinkTtlAfterDelivery(taskId: string): void;
|
|
73
|
-
private encodeCursor;
|
|
74
|
-
private decodeCursor;
|
|
75
71
|
private resolveNextCursor;
|
|
76
72
|
}
|
|
77
73
|
export declare const taskManager: TaskManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/tasks/manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/tasks/manager.ts"],"names":[],"mappings":"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"}
|
package/dist/tasks/manager.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
-
import { Buffer } from 'node:buffer';
|
|
3
1
|
import { randomUUID } from 'node:crypto';
|
|
4
2
|
import { setInterval } from 'node:timers';
|
|
5
3
|
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
6
4
|
import { config } from '../lib/core.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { decodeTaskCursor, encodeTaskCursor } from './cursor-codec.js';
|
|
6
|
+
import { TaskWaiterRegistry, waitForTerminalTask as waitForTerminalTaskWithDeadline, } from './waiters.js';
|
|
9
7
|
const DEFAULT_TTL_MS = 60_000;
|
|
10
8
|
const MIN_TTL_MS = 1_000;
|
|
11
9
|
const MAX_TTL_MS = 86_400_000;
|
|
@@ -13,7 +11,6 @@ const DEFAULT_POLL_INTERVAL_MS = 1_000;
|
|
|
13
11
|
const DEFAULT_OWNER_KEY = 'default';
|
|
14
12
|
const DEFAULT_PAGE_SIZE = 50;
|
|
15
13
|
const CLEANUP_INTERVAL_MS = 60_000;
|
|
16
|
-
const MAX_CURSOR_LENGTH = 256;
|
|
17
14
|
const RESULT_DELIVERY_GRACE_MS = 10_000;
|
|
18
15
|
const TASK_STATUS_VALUES = new Set([
|
|
19
16
|
'working',
|
|
@@ -59,7 +56,7 @@ function normalizeTaskTtl(ttl) {
|
|
|
59
56
|
class TaskManager {
|
|
60
57
|
tasks = new Map();
|
|
61
58
|
ownerCounts = new Map();
|
|
62
|
-
waiters = new
|
|
59
|
+
waiters = new TaskWaiterRegistry(isTerminalStatus);
|
|
63
60
|
cleanupInterval = null;
|
|
64
61
|
ensureCleanupLoop() {
|
|
65
62
|
if (this.cleanupInterval)
|
|
@@ -259,116 +256,27 @@ class TaskManager {
|
|
|
259
256
|
resolveAnchorTaskId(cursor) {
|
|
260
257
|
if (cursor === undefined)
|
|
261
258
|
return null;
|
|
262
|
-
const decoded =
|
|
259
|
+
const decoded = decodeTaskCursor(cursor);
|
|
263
260
|
if (decoded === null) {
|
|
264
261
|
throw new McpError(ErrorCode.InvalidParams, 'Invalid cursor');
|
|
265
262
|
}
|
|
266
263
|
return decoded.anchorTaskId;
|
|
267
264
|
}
|
|
268
|
-
addWaiter(taskId, waiter) {
|
|
269
|
-
let set = this.waiters.get(taskId);
|
|
270
|
-
if (!set) {
|
|
271
|
-
set = new Set();
|
|
272
|
-
this.waiters.set(taskId, set);
|
|
273
|
-
}
|
|
274
|
-
set.add(waiter);
|
|
275
|
-
}
|
|
276
|
-
removeWaiter(taskId, waiter) {
|
|
277
|
-
if (!waiter)
|
|
278
|
-
return;
|
|
279
|
-
const set = this.waiters.get(taskId);
|
|
280
|
-
if (!set)
|
|
281
|
-
return;
|
|
282
|
-
set.delete(waiter);
|
|
283
|
-
if (set.size === 0) {
|
|
284
|
-
this.waiters.delete(taskId);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
265
|
async waitForTerminalTask(taskId, ownerKey, signal) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const resolveInContext = AsyncLocalStorage.bind((value) => {
|
|
299
|
-
resolve(value);
|
|
300
|
-
});
|
|
301
|
-
const rejectInContext = AsyncLocalStorage.bind((error) => {
|
|
302
|
-
reject(toError(error));
|
|
303
|
-
});
|
|
304
|
-
let settled = false;
|
|
305
|
-
let waiter = null;
|
|
306
|
-
let deadlineTimeout;
|
|
307
|
-
const cleanup = () => {
|
|
308
|
-
if (deadlineTimeout) {
|
|
309
|
-
deadlineTimeout.cancel();
|
|
310
|
-
deadlineTimeout = undefined;
|
|
311
|
-
}
|
|
312
|
-
if (signal) {
|
|
313
|
-
signal.removeEventListener('abort', onAbort);
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
const settleOnce = (fn) => {
|
|
317
|
-
if (settled)
|
|
318
|
-
return;
|
|
319
|
-
settled = true;
|
|
320
|
-
fn();
|
|
321
|
-
};
|
|
322
|
-
const onAbort = () => {
|
|
323
|
-
settleOnce(() => {
|
|
324
|
-
cleanup();
|
|
325
|
-
this.removeWaiter(taskId, waiter);
|
|
326
|
-
rejectInContext(new McpError(ErrorCode.ConnectionClosed, 'Request was cancelled'));
|
|
327
|
-
});
|
|
328
|
-
};
|
|
329
|
-
waiter = (updated) => {
|
|
330
|
-
settleOnce(() => {
|
|
331
|
-
cleanup();
|
|
332
|
-
if (updated.ownerKey !== ownerKey) {
|
|
333
|
-
resolveInContext(undefined);
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
resolveInContext(updated);
|
|
337
|
-
});
|
|
338
|
-
};
|
|
339
|
-
if (signal?.aborted) {
|
|
340
|
-
onAbort();
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
this.addWaiter(taskId, waiter);
|
|
344
|
-
if (signal) {
|
|
345
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
346
|
-
}
|
|
347
|
-
const timeoutMs = Math.max(0, deadlineMs - Date.now());
|
|
348
|
-
deadlineTimeout = createUnrefTimeout(timeoutMs, { timeout: true });
|
|
349
|
-
void deadlineTimeout.promise
|
|
350
|
-
.then(() => {
|
|
351
|
-
settleOnce(() => {
|
|
352
|
-
cleanup();
|
|
353
|
-
this.removeWaiter(taskId, waiter);
|
|
354
|
-
this.removeTask(taskId);
|
|
355
|
-
rejectInContext(new McpError(ErrorCode.InvalidParams, 'Task expired', {
|
|
356
|
-
taskId,
|
|
357
|
-
}));
|
|
358
|
-
});
|
|
359
|
-
})
|
|
360
|
-
.catch(rejectInContext);
|
|
266
|
+
return waitForTerminalTaskWithDeadline({
|
|
267
|
+
taskId,
|
|
268
|
+
ownerKey,
|
|
269
|
+
...(signal ? { signal } : {}),
|
|
270
|
+
lookupTask: (currentTaskId, currentOwnerKey) => this.lookupActiveTask(currentTaskId, currentOwnerKey),
|
|
271
|
+
removeTask: (currentTaskId) => {
|
|
272
|
+
this.removeTask(currentTaskId);
|
|
273
|
+
},
|
|
274
|
+
registry: this.waiters,
|
|
275
|
+
isTerminalStatus,
|
|
361
276
|
});
|
|
362
277
|
}
|
|
363
278
|
notifyWaiters(task) {
|
|
364
|
-
|
|
365
|
-
return;
|
|
366
|
-
const waiters = this.waiters.get(task.taskId);
|
|
367
|
-
if (!waiters)
|
|
368
|
-
return;
|
|
369
|
-
this.waiters.delete(task.taskId);
|
|
370
|
-
for (const waiter of waiters)
|
|
371
|
-
waiter(task);
|
|
279
|
+
this.waiters.notify(task);
|
|
372
280
|
}
|
|
373
281
|
isExpired(task, now = Date.now()) {
|
|
374
282
|
return now - task._createdAtMs > task.ttl;
|
|
@@ -389,47 +297,11 @@ class TaskManager {
|
|
|
389
297
|
this.maybeUpdateLastUpdatedAt(task);
|
|
390
298
|
}
|
|
391
299
|
}
|
|
392
|
-
encodeCursor(taskId) {
|
|
393
|
-
// Base64url-encode the taskId to produce a compact opaque cursor string.
|
|
394
|
-
// The taskId is a UUID, which is 36 ASCII chars, so the resulting cursor will be 48 chars (36 * 4/3) plus padding if any.
|
|
395
|
-
return Buffer.from(taskId, 'utf8').toString('base64url');
|
|
396
|
-
}
|
|
397
|
-
decodeCursor(cursor) {
|
|
398
|
-
try {
|
|
399
|
-
if (!isValidBase64UrlCursor(cursor))
|
|
400
|
-
return null;
|
|
401
|
-
const decoded = Buffer.from(cursor, 'base64url').toString('utf8');
|
|
402
|
-
// Basic sanity: non-empty and plausible taskId length (UUIDs are 36 chars)
|
|
403
|
-
if (!decoded || decoded.length > 128)
|
|
404
|
-
return null;
|
|
405
|
-
return { anchorTaskId: decoded };
|
|
406
|
-
}
|
|
407
|
-
catch {
|
|
408
|
-
return null;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
300
|
resolveNextCursor(page, hasMore) {
|
|
412
301
|
if (!hasMore)
|
|
413
302
|
return undefined;
|
|
414
303
|
const lastTask = page.at(-1);
|
|
415
|
-
return lastTask ?
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
function isValidBase64UrlCursor(cursor) {
|
|
419
|
-
if (!cursor)
|
|
420
|
-
return false;
|
|
421
|
-
if (cursor.length > MAX_CURSOR_LENGTH)
|
|
422
|
-
return false;
|
|
423
|
-
if (!/^[A-Za-z0-9_-]+={0,2}$/u.test(cursor))
|
|
424
|
-
return false;
|
|
425
|
-
const firstPaddingIndex = cursor.indexOf('=');
|
|
426
|
-
if (firstPaddingIndex !== -1) {
|
|
427
|
-
for (let i = firstPaddingIndex; i < cursor.length; i += 1) {
|
|
428
|
-
if (cursor[i] !== '=')
|
|
429
|
-
return false;
|
|
430
|
-
}
|
|
431
|
-
return cursor.length % 4 === 0;
|
|
304
|
+
return lastTask ? encodeTaskCursor(lastTask.taskId) : undefined;
|
|
432
305
|
}
|
|
433
|
-
return cursor.length % 4 !== 1;
|
|
434
306
|
}
|
|
435
307
|
export const taskManager = new TaskManager();
|
package/dist/tasks/owner.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ServerResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
import type { ProgressNotification } from '../lib/progress.js';
|
|
3
|
+
import type { ToolHandlerExtra } from '../lib/progress.js';
|
|
4
|
+
import { type ToolCallRequestMeta } from './call-contract.js';
|
|
3
5
|
interface HandlerExtra {
|
|
4
6
|
sessionId?: string;
|
|
5
7
|
authInfo?: {
|
|
@@ -9,14 +11,17 @@ interface HandlerExtra {
|
|
|
9
11
|
signal?: AbortSignal;
|
|
10
12
|
requestId?: string | number;
|
|
11
13
|
sendNotification?: (notification: ProgressNotification) => Promise<void>;
|
|
14
|
+
requestInfo?: unknown;
|
|
12
15
|
}
|
|
13
|
-
export interface
|
|
16
|
+
export interface ToolExecutionContext {
|
|
14
17
|
ownerKey: string;
|
|
15
18
|
sessionId?: string;
|
|
16
19
|
signal?: AbortSignal;
|
|
17
20
|
requestId?: string | number;
|
|
18
21
|
sendNotification?: (notification: ProgressNotification) => Promise<void>;
|
|
22
|
+
requestMeta?: ToolCallRequestMeta;
|
|
19
23
|
}
|
|
24
|
+
export type ToolCallContext = ToolExecutionContext;
|
|
20
25
|
/** Strip keys whose value is `undefined`, returning an object with only the
|
|
21
26
|
* present keys. Return type correctly omits the `undefined` union so the result
|
|
22
27
|
* is compatible with `exactOptionalPropertyTypes`. */
|
|
@@ -26,7 +31,12 @@ type Compacted<T extends object> = {
|
|
|
26
31
|
export declare function compact<T extends object>(obj: T): Compacted<T>;
|
|
27
32
|
export declare function parseHandlerExtra(extra: unknown): HandlerExtra | undefined;
|
|
28
33
|
export declare function resolveTaskOwnerKey(extra?: HandlerExtra): string;
|
|
29
|
-
export declare function
|
|
34
|
+
export declare function resolveRequestIdFromExtra(extra: unknown): string | undefined;
|
|
35
|
+
export declare function resolveSessionIdFromExtra(extra: unknown): string | undefined;
|
|
36
|
+
export declare function resolveToolExecutionContext(extra?: HandlerExtra, requestMeta?: ToolCallRequestMeta): ToolExecutionContext;
|
|
37
|
+
export declare function resolveToolCallContext(extra?: HandlerExtra, requestMeta?: ToolCallRequestMeta): ToolCallContext;
|
|
38
|
+
export declare function buildToolHandlerExtra(context: ToolExecutionContext, requestMeta?: ToolCallRequestMeta): ToolHandlerExtra;
|
|
39
|
+
export declare function withRequestContextIfMissing<TParams, TResult, TExtra = unknown>(handler: (params: TParams, extra?: TExtra) => Promise<TResult>): (params: TParams, extra?: TExtra) => Promise<TResult>;
|
|
30
40
|
export declare function isServerResult(value: unknown): value is ServerResult;
|
|
31
41
|
export declare function tryReadToolStructuredError(value: unknown): string | undefined;
|
|
32
42
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owner.d.ts","sourceRoot":"","sources":["../../src/tasks/owner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"owner.d.ts","sourceRoot":"","sources":["../../src/tasks/owner.ts"],"names":[],"mappings":"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"}
|
package/dist/tasks/owner.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
3
|
+
import { getRequestId, runWithRequestContext } from '../lib/core.js';
|
|
4
|
+
import { isObject, readNestedRecord } from '../lib/utils.js';
|
|
5
|
+
import { sanitizeToolCallMeta, } from './call-contract.js';
|
|
4
6
|
export function compact(obj) {
|
|
5
7
|
const result = {};
|
|
6
8
|
for (const key of Object.keys(obj)) {
|
|
@@ -60,15 +62,62 @@ export function resolveTaskOwnerKey(extra) {
|
|
|
60
62
|
return `token:${createHash('sha256').update(extra.authInfo.token).digest('hex')}`;
|
|
61
63
|
return 'default';
|
|
62
64
|
}
|
|
63
|
-
export function
|
|
65
|
+
export function resolveRequestIdFromExtra(extra) {
|
|
66
|
+
if (!isObject(extra))
|
|
67
|
+
return undefined;
|
|
68
|
+
const { requestId } = extra;
|
|
69
|
+
if (typeof requestId === 'string')
|
|
70
|
+
return requestId;
|
|
71
|
+
if (typeof requestId === 'number')
|
|
72
|
+
return String(requestId);
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
export function resolveSessionIdFromExtra(extra) {
|
|
76
|
+
if (!isObject(extra))
|
|
77
|
+
return undefined;
|
|
78
|
+
const { sessionId } = extra;
|
|
79
|
+
if (typeof sessionId === 'string')
|
|
80
|
+
return sessionId;
|
|
81
|
+
const headers = readNestedRecord(extra, ['requestInfo', 'headers']);
|
|
82
|
+
const headerValue = headers ? headers['mcp-session-id'] : undefined;
|
|
83
|
+
return typeof headerValue === 'string' ? headerValue : undefined;
|
|
84
|
+
}
|
|
85
|
+
export function resolveToolExecutionContext(extra, requestMeta) {
|
|
64
86
|
return compact({
|
|
65
87
|
ownerKey: resolveTaskOwnerKey(extra),
|
|
66
88
|
sessionId: extra?.sessionId,
|
|
67
89
|
signal: extra?.signal,
|
|
68
90
|
requestId: extra?.requestId,
|
|
69
91
|
sendNotification: extra?.sendNotification,
|
|
92
|
+
requestMeta: sanitizeToolCallMeta(requestMeta),
|
|
70
93
|
});
|
|
71
94
|
}
|
|
95
|
+
export function resolveToolCallContext(extra, requestMeta) {
|
|
96
|
+
return resolveToolExecutionContext(extra, requestMeta);
|
|
97
|
+
}
|
|
98
|
+
export function buildToolHandlerExtra(context, requestMeta) {
|
|
99
|
+
return compact({
|
|
100
|
+
signal: context.signal,
|
|
101
|
+
requestId: context.requestId,
|
|
102
|
+
sendNotification: context.sendNotification,
|
|
103
|
+
_meta: sanitizeToolCallMeta(requestMeta ?? context.requestMeta),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export function withRequestContextIfMissing(handler) {
|
|
107
|
+
return async (params, extra) => {
|
|
108
|
+
const existingRequestId = getRequestId();
|
|
109
|
+
if (existingRequestId) {
|
|
110
|
+
return handler(params, extra);
|
|
111
|
+
}
|
|
112
|
+
const derivedRequestId = resolveRequestIdFromExtra(extra) ?? randomUUID();
|
|
113
|
+
const derivedSessionId = resolveSessionIdFromExtra(extra);
|
|
114
|
+
return runWithRequestContext({
|
|
115
|
+
requestId: derivedRequestId,
|
|
116
|
+
operationId: derivedRequestId,
|
|
117
|
+
...(derivedSessionId ? { sessionId: derivedSessionId } : {}),
|
|
118
|
+
}, () => handler(params, extra));
|
|
119
|
+
};
|
|
120
|
+
}
|
|
72
121
|
export function isServerResult(value) {
|
|
73
122
|
return (isObject(value) && Array.isArray(value.content));
|
|
74
123
|
}
|
|
@@ -4,6 +4,7 @@ export interface TaskCapableToolDescriptor<TArgs = unknown> {
|
|
|
4
4
|
name: string;
|
|
5
5
|
parseArguments: (args: unknown) => TArgs;
|
|
6
6
|
execute: (args: TArgs, extra?: ToolHandlerExtra) => Promise<ServerResult>;
|
|
7
|
+
getCompletionStatusMessage?: (result: ServerResult) => string | undefined;
|
|
7
8
|
}
|
|
8
9
|
export declare function registerTaskCapableTool<TArgs>(descriptor: TaskCapableToolDescriptor<TArgs>): void;
|
|
9
10
|
export declare function unregisterTaskCapableTool(name: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/tasks/tool-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,yBAAyB,CAAC,KAAK,GAAG,OAAO;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,KAAK,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC3E;AAID,wBAAgB,uBAAuB,CAAC,KAAK,EAC3C,UAAU,EAAE,yBAAyB,CAAC,KAAK,CAAC,GAC3C,IAAI,CAKN;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GACX,yBAAyB,GAAG,SAAS,CAEvC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,6BAA6B,IAAI,OAAO,CAEvD"}
|
|
1
|
+
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/tasks/tool-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,yBAAyB,CAAC,KAAK,GAAG,OAAO;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,KAAK,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1E,0BAA0B,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,GAAG,SAAS,CAAC;CAC3E;AAID,wBAAgB,uBAAuB,CAAC,KAAK,EAC3C,UAAU,EAAE,yBAAyB,CAAC,KAAK,CAAC,GAC3C,IAAI,CAKN;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GACX,yBAAyB,GAAG,SAAS,CAEvC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,6BAA6B,IAAI,OAAO,CAEvD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface WaitableTask {
|
|
2
|
+
taskId: string;
|
|
3
|
+
ownerKey: string;
|
|
4
|
+
status: string;
|
|
5
|
+
ttl: number;
|
|
6
|
+
_createdAtMs: number;
|
|
7
|
+
}
|
|
8
|
+
type TaskWaiter<TTask extends WaitableTask> = (task: TTask) => void;
|
|
9
|
+
export declare class TaskWaiterRegistry<TTask extends WaitableTask> {
|
|
10
|
+
private readonly isTerminalStatus;
|
|
11
|
+
private waiters;
|
|
12
|
+
constructor(isTerminalStatus: (status: TTask['status']) => boolean);
|
|
13
|
+
add(taskId: string, waiter: TaskWaiter<TTask>): void;
|
|
14
|
+
remove(taskId: string, waiter: TaskWaiter<TTask> | null): void;
|
|
15
|
+
notify(task: TTask): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function waitForTerminalTask<TTask extends WaitableTask>(options: {
|
|
18
|
+
taskId: string;
|
|
19
|
+
ownerKey: string;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
lookupTask: (taskId: string, ownerKey: string) => TTask | undefined;
|
|
22
|
+
removeTask: (taskId: string) => void;
|
|
23
|
+
registry: TaskWaiterRegistry<TTask>;
|
|
24
|
+
isTerminalStatus: (status: TTask['status']) => boolean;
|
|
25
|
+
}): Promise<TTask | undefined>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=waiters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waiters.d.ts","sourceRoot":"","sources":["../../src/tasks/waiters.ts"],"names":[],"mappings":"AAOA,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,UAAU,CAAC,KAAK,SAAS,YAAY,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;AAEpE,qBAAa,kBAAkB,CAAC,KAAK,SAAS,YAAY;IAItD,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAHnC,OAAO,CAAC,OAAO,CAA6C;gBAGzC,gBAAgB,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,OAAO;IAGzE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI;IASpD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI;IAY9D,MAAM,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI;CAS1B;AAED,wBAAsB,mBAAmB,CAAC,KAAK,SAAS,YAAY,EAAE,OAAO,EAAE;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,CAAC;IACpE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACpC,gBAAgB,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC;CACxD,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAwF7B"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { toError } from '../lib/utils.js';
|
|
4
|
+
import { createUnrefTimeout } from '../lib/utils.js';
|
|
5
|
+
export class TaskWaiterRegistry {
|
|
6
|
+
isTerminalStatus;
|
|
7
|
+
waiters = new Map();
|
|
8
|
+
constructor(isTerminalStatus) {
|
|
9
|
+
this.isTerminalStatus = isTerminalStatus;
|
|
10
|
+
}
|
|
11
|
+
add(taskId, waiter) {
|
|
12
|
+
let set = this.waiters.get(taskId);
|
|
13
|
+
if (!set) {
|
|
14
|
+
set = new Set();
|
|
15
|
+
this.waiters.set(taskId, set);
|
|
16
|
+
}
|
|
17
|
+
set.add(waiter);
|
|
18
|
+
}
|
|
19
|
+
remove(taskId, waiter) {
|
|
20
|
+
if (!waiter)
|
|
21
|
+
return;
|
|
22
|
+
const set = this.waiters.get(taskId);
|
|
23
|
+
if (!set)
|
|
24
|
+
return;
|
|
25
|
+
set.delete(waiter);
|
|
26
|
+
if (set.size === 0) {
|
|
27
|
+
this.waiters.delete(taskId);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
notify(task) {
|
|
31
|
+
if (!this.isTerminalStatus(task.status))
|
|
32
|
+
return;
|
|
33
|
+
const waiters = this.waiters.get(task.taskId);
|
|
34
|
+
if (!waiters)
|
|
35
|
+
return;
|
|
36
|
+
this.waiters.delete(task.taskId);
|
|
37
|
+
for (const waiter of waiters)
|
|
38
|
+
waiter(task);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function waitForTerminalTask(options) {
|
|
42
|
+
const task = options.lookupTask(options.taskId, options.ownerKey);
|
|
43
|
+
if (!task)
|
|
44
|
+
return undefined;
|
|
45
|
+
if (options.isTerminalStatus(task.status))
|
|
46
|
+
return task;
|
|
47
|
+
const deadlineMs = task._createdAtMs + task.ttl;
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const resolveInContext = AsyncLocalStorage.bind((value) => {
|
|
50
|
+
resolve(value);
|
|
51
|
+
});
|
|
52
|
+
const rejectInContext = AsyncLocalStorage.bind((error) => {
|
|
53
|
+
reject(toError(error));
|
|
54
|
+
});
|
|
55
|
+
let settled = false;
|
|
56
|
+
let waiter = null;
|
|
57
|
+
let deadlineTimeout;
|
|
58
|
+
const cleanup = () => {
|
|
59
|
+
if (deadlineTimeout) {
|
|
60
|
+
deadlineTimeout.cancel();
|
|
61
|
+
deadlineTimeout = undefined;
|
|
62
|
+
}
|
|
63
|
+
if (options.signal) {
|
|
64
|
+
options.signal.removeEventListener('abort', onAbort);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const settleOnce = (fn) => {
|
|
68
|
+
if (settled)
|
|
69
|
+
return;
|
|
70
|
+
settled = true;
|
|
71
|
+
fn();
|
|
72
|
+
};
|
|
73
|
+
const onAbort = () => {
|
|
74
|
+
settleOnce(() => {
|
|
75
|
+
cleanup();
|
|
76
|
+
options.registry.remove(options.taskId, waiter);
|
|
77
|
+
rejectInContext(new McpError(ErrorCode.ConnectionClosed, 'Request was cancelled'));
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
waiter = (updated) => {
|
|
81
|
+
settleOnce(() => {
|
|
82
|
+
cleanup();
|
|
83
|
+
if (updated.ownerKey !== options.ownerKey) {
|
|
84
|
+
resolveInContext(undefined);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
resolveInContext(updated);
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
if (options.signal?.aborted) {
|
|
91
|
+
onAbort();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
options.registry.add(options.taskId, waiter);
|
|
95
|
+
if (options.signal) {
|
|
96
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
97
|
+
}
|
|
98
|
+
const timeoutMs = Math.max(0, deadlineMs - Date.now());
|
|
99
|
+
deadlineTimeout = createUnrefTimeout(timeoutMs, { timeout: true });
|
|
100
|
+
void deadlineTimeout.promise
|
|
101
|
+
.then(() => {
|
|
102
|
+
settleOnce(() => {
|
|
103
|
+
cleanup();
|
|
104
|
+
options.registry.remove(options.taskId, waiter);
|
|
105
|
+
options.removeTask(options.taskId);
|
|
106
|
+
rejectInContext(new McpError(ErrorCode.InvalidParams, 'Task expired', {
|
|
107
|
+
taskId: options.taskId,
|
|
108
|
+
}));
|
|
109
|
+
});
|
|
110
|
+
})
|
|
111
|
+
.catch(rejectInContext);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ServerResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type { SharedFetchStage } from '../lib/fetch-pipeline.js';
|
|
3
|
+
import type { ProgressReporter } from '../lib/progress.js';
|
|
4
|
+
export declare function buildFetchSuccessSummary(contentSize: number): string;
|
|
5
|
+
export declare function getFetchCompletionStatusMessage(result: ServerResult): string | undefined;
|
|
6
|
+
export declare class FetchUrlProgressPlan {
|
|
7
|
+
private readonly reporter;
|
|
8
|
+
private readonly context;
|
|
9
|
+
private path;
|
|
10
|
+
constructor(reporter: ProgressReporter, context: string);
|
|
11
|
+
reportStart(): void;
|
|
12
|
+
reportStage(stage: SharedFetchStage): void;
|
|
13
|
+
reportSuccess(contentSize: number): void;
|
|
14
|
+
reportFailure(cancelled: boolean): void;
|
|
15
|
+
private mapStage;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=fetch-url-progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-url-progress.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-url-progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAW3D,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,YAAY,GACnB,MAAM,GAAG,SAAS,CAUpB;AAED,qBAAa,oBAAoB;IAI7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,IAAI,CAAwB;gBAGjB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,MAAM;IAGlC,WAAW,IAAI,IAAI;IAInB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAM1C,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIxC,aAAa,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI;IAIvC,OAAO,CAAC,QAAQ;CAiCjB"}
|