@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.
Files changed (63) hide show
  1. package/dist/http/native.d.ts.map +1 -1
  2. package/dist/http/native.js +20 -26
  3. package/dist/http/session-teardown.d.ts +17 -0
  4. package/dist/http/session-teardown.d.ts.map +1 -0
  5. package/dist/http/session-teardown.js +30 -0
  6. package/dist/lib/code-lang.d.ts.map +1 -1
  7. package/dist/lib/code-lang.js +2 -8
  8. package/dist/lib/core.d.ts.map +1 -1
  9. package/dist/lib/core.js +2 -0
  10. package/dist/lib/dom-prep.d.ts +2 -0
  11. package/dist/lib/dom-prep.d.ts.map +1 -1
  12. package/dist/lib/dom-prep.js +150 -139
  13. package/dist/lib/md-cleanup.d.ts.map +1 -1
  14. package/dist/lib/md-cleanup.js +53 -20
  15. package/dist/lib/progress.d.ts.map +1 -1
  16. package/dist/lib/progress.js +59 -32
  17. package/dist/lib/sdk-interop.d.ts +8 -0
  18. package/dist/lib/sdk-interop.d.ts.map +1 -0
  19. package/dist/lib/sdk-interop.js +73 -0
  20. package/dist/lib/task-handlers.d.ts +0 -2
  21. package/dist/lib/task-handlers.d.ts.map +1 -1
  22. package/dist/lib/task-handlers.js +9 -94
  23. package/dist/resources/index.js +1 -1
  24. package/dist/server.d.ts.map +1 -1
  25. package/dist/server.js +10 -3
  26. package/dist/tasks/call-contract.d.ts +25 -0
  27. package/dist/tasks/call-contract.d.ts.map +1 -0
  28. package/dist/tasks/call-contract.js +58 -0
  29. package/dist/tasks/cursor-codec.d.ts +5 -0
  30. package/dist/tasks/cursor-codec.d.ts.map +1 -0
  31. package/dist/tasks/cursor-codec.js +41 -0
  32. package/dist/tasks/execution.d.ts +1 -20
  33. package/dist/tasks/execution.d.ts.map +1 -1
  34. package/dist/tasks/execution.js +7 -32
  35. package/dist/tasks/manager.d.ts +1 -5
  36. package/dist/tasks/manager.d.ts.map +1 -1
  37. package/dist/tasks/manager.js +16 -144
  38. package/dist/tasks/owner.d.ts +12 -2
  39. package/dist/tasks/owner.d.ts.map +1 -1
  40. package/dist/tasks/owner.js +52 -3
  41. package/dist/tasks/tool-registry.d.ts +1 -0
  42. package/dist/tasks/tool-registry.d.ts.map +1 -1
  43. package/dist/tasks/waiters.d.ts +27 -0
  44. package/dist/tasks/waiters.d.ts.map +1 -0
  45. package/dist/tasks/waiters.js +113 -0
  46. package/dist/tools/fetch-url-progress.d.ts +17 -0
  47. package/dist/tools/fetch-url-progress.d.ts.map +1 -0
  48. package/dist/tools/fetch-url-progress.js +78 -0
  49. package/dist/tools/fetch-url.d.ts +5 -9
  50. package/dist/tools/fetch-url.d.ts.map +1 -1
  51. package/dist/tools/fetch-url.js +43 -96
  52. package/dist/transform/next-flight.d.ts +2 -0
  53. package/dist/transform/next-flight.d.ts.map +1 -0
  54. package/dist/transform/next-flight.js +285 -0
  55. package/dist/transform/title-policy.d.ts +12 -0
  56. package/dist/transform/title-policy.d.ts.map +1 -0
  57. package/dist/transform/title-policy.js +85 -0
  58. package/dist/transform/transform.d.ts.map +1 -1
  59. package/dist/transform/transform.js +62 -475
  60. package/package.json +4 -4
  61. package/dist/tools/index.d.ts +0 -3
  62. package/dist/tools/index.d.ts.map +0 -1
  63. package/dist/tools/index.js +0 -4
@@ -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
- : 'Task completed successfully.',
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
- const extra = compact({
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;
@@ -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":"AAWA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1E,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAOD,UAAU,iBAAiB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,UAAU,CAAC;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,GAAG,EAAE,MAAM,CAAC;QACZ,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAwED,cAAM,WAAW;IACf,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,OAAO,CAAqD;IACpE,OAAO,CAAC,eAAe,CAA+C;IAEtE,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,kBAAkB;IAmB1B,UAAU,CACR,OAAO,CAAC,EAAE,iBAAiB,EAC3B,aAAa,SAAiB,EAC9B,QAAQ,GAAE,MAA0B,GACnC,SAAS;IA0BZ,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIjE,UAAU,CACR,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC,GACxD,IAAI;IAgBP,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAgBpE,kBAAkB,CAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,SAAkE,GAC9E,SAAS,EAAE;IAgBd,OAAO,CAAC,WAAW;IAwCnB,SAAS,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QACzE,KAAK,EAAE,SAAS,EAAE,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB;IAiBD,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,YAAY;IAed,mBAAmB,CACvB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IA6FjC,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,wBAAwB;IAIhC,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAa5C,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,iBAAiB;CAQ1B;AAgBD,eAAO,MAAM,WAAW,aAAoB,CAAC"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/tasks/manager.ts"],"names":[],"mappings":"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"}
@@ -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 { toError } from '../lib/utils.js';
8
- import { createUnrefTimeout } from '../lib/utils.js';
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 Map();
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 = this.decodeCursor(cursor);
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
- const task = this.lookupActiveTask(taskId, ownerKey);
289
- if (!task)
290
- return undefined;
291
- if (isTerminalStatus(task.status))
292
- return task;
293
- // isExpired() above guarantees task.ttl has not elapsed; compute deadlineMs
294
- // for the promise-based timeout below.
295
- const deadlineMs = task._createdAtMs + task.ttl;
296
- return new Promise((resolve, reject) => {
297
- // Bind resolve/reject to the AsyncLocalStorage context of the caller, so that any context values (e.g. requestId) are preserved when we later call them from a different tick.
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
- if (!isTerminalStatus(task.status))
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 ? this.encodeCursor(lastTask.taskId) : undefined;
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();
@@ -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 ToolCallContext {
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 resolveToolCallContext(extra?: HandlerExtra): ToolCallContext;
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;AAGvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAO/D,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E;AAED;;sDAEsD;AACtD,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI;KAChC,CAAC,IAAI,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,SAAS,KAAK,GACnD,KAAK,GACL,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;CAClC,CAAC;AAEF,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAQ9D;AA2BD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAyB1E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAMhE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,eAAe,CAQ5E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAIpE;AAWD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAgB7E"}
1
+ {"version":3,"file":"owner.d.ts","sourceRoot":"","sources":["../../src/tasks/owner.ts"],"names":[],"mappings":"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"}
@@ -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 { isObject } from '../lib/utils.js';
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 resolveToolCallContext(extra) {
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"}