@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
@@ -19,6 +19,9 @@ class ToolProgressReporter {
19
19
  canReportProgress;
20
20
  isTerminal = false;
21
21
  lastProgress = -1;
22
+ lastMessage;
23
+ pendingNotification;
24
+ isDispatching = false;
22
25
  constructor(token, sendNotification, relatedTaskMeta, onProgress, canReportProgress) {
23
26
  this.token = token;
24
27
  this.sendNotification = sendNotification;
@@ -50,13 +53,13 @@ class ToolProgressReporter {
50
53
  return;
51
54
  const effectiveProgress = Math.max(progress, this.lastProgress);
52
55
  const isIncreasing = effectiveProgress > this.lastProgress;
56
+ const isMessageChanged = message !== this.lastMessage;
53
57
  this.lastProgress = effectiveProgress;
58
+ this.lastMessage = message;
54
59
  if (effectiveProgress >= FETCH_PROGRESS_TOTAL) {
55
60
  this.isTerminal = true;
56
61
  }
57
- // Only fire onProgress when progress actually increases to avoid duplicate
58
- // task status updates (onProgress drives updateWorkingTaskStatus in task mode).
59
- if (isIncreasing && this.onProgress) {
62
+ if ((isIncreasing || isMessageChanged) && this.onProgress) {
60
63
  try {
61
64
  this.onProgress(effectiveProgress, message);
62
65
  }
@@ -70,44 +73,68 @@ class ToolProgressReporter {
70
73
  }
71
74
  if (!isIncreasing || this.token === null || !this.sendNotification)
72
75
  return;
73
- const { sendNotification } = this;
74
- const notification = this.createProgressNotification(this.token, effectiveProgress, message);
75
- // Fire without chaining to avoid queue buildup and delayed reports
76
- // that could fire after the task reaches a terminal state.
76
+ this.pendingNotification = this.createProgressNotification(this.token, effectiveProgress, message);
77
+ this.flushNotifications();
78
+ }
79
+ flushNotifications() {
80
+ if (this.isDispatching || !this.sendNotification)
81
+ return;
82
+ this.isDispatching = true;
77
83
  void (async () => {
78
- let timeoutId;
79
- const timeoutPromise = new Promise((resolve) => {
80
- timeoutId = setTimeout(() => {
81
- resolve({ timeout: true });
82
- }, PROGRESS_NOTIFICATION_TIMEOUT_MS);
83
- timeoutId.unref();
84
- });
85
84
  try {
86
- const outcome = await Promise.race([
87
- sendNotification(notification).then(() => ({ ok: true })),
88
- timeoutPromise,
89
- ]);
90
- if ('timeout' in outcome) {
91
- logWarn('Progress notification timed out', {
92
- progress: effectiveProgress,
93
- message,
94
- });
85
+ while (this.pendingNotification) {
86
+ if (this.canReportProgress && !this.canReportProgress()) {
87
+ this.pendingNotification = undefined;
88
+ return;
89
+ }
90
+ const notification = this.pendingNotification;
91
+ this.pendingNotification = undefined;
92
+ await this.sendWithTimeout(notification);
95
93
  }
96
94
  }
97
- catch (error) {
98
- logWarn('Failed to send progress notification', {
99
- error: getErrorMessage(error),
100
- progress: effectiveProgress,
101
- message,
102
- });
103
- }
104
95
  finally {
105
- if (timeoutId) {
106
- clearTimeout(timeoutId);
96
+ this.isDispatching = false;
97
+ if (this.pendingNotification) {
98
+ this.flushNotifications();
107
99
  }
108
100
  }
109
101
  })();
110
102
  }
103
+ async sendWithTimeout(notification) {
104
+ if (!this.sendNotification)
105
+ return;
106
+ let timeoutId;
107
+ const timeoutPromise = new Promise((resolve) => {
108
+ timeoutId = setTimeout(() => {
109
+ resolve({ timeout: true });
110
+ }, PROGRESS_NOTIFICATION_TIMEOUT_MS);
111
+ timeoutId.unref();
112
+ });
113
+ try {
114
+ const outcome = await Promise.race([
115
+ this.sendNotification(notification).then(() => ({ ok: true })),
116
+ timeoutPromise,
117
+ ]);
118
+ if ('timeout' in outcome) {
119
+ logWarn('Progress notification timed out', {
120
+ progress: notification.params.progress,
121
+ message: notification.params.message,
122
+ });
123
+ }
124
+ }
125
+ catch (error) {
126
+ logWarn('Failed to send progress notification', {
127
+ error: getErrorMessage(error),
128
+ progress: notification.params.progress,
129
+ message: notification.params.message,
130
+ });
131
+ }
132
+ finally {
133
+ if (timeoutId) {
134
+ clearTimeout(timeoutId);
135
+ }
136
+ }
137
+ }
111
138
  createProgressNotification(token, progress, message) {
112
139
  return {
113
140
  method: 'notifications/progress',
@@ -0,0 +1,8 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ type CleanupCallback = () => void;
3
+ type RequestHandlerFn = (request: unknown, extra?: unknown) => Promise<unknown>;
4
+ export declare function registerServerLifecycleCleanup(server: McpServer, callback: CleanupCallback): void;
5
+ export declare function getSdkCallToolHandler(server: McpServer): RequestHandlerFn | null;
6
+ export declare function setTaskToolCallCapability(server: McpServer, enabled: boolean): void;
7
+ export {};
8
+ //# sourceMappingURL=sdk-interop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-interop.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-interop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,KAAK,eAAe,GAAG,MAAM,IAAI,CAAC;AAClC,KAAK,gBAAgB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AA8ChF,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,eAAe,GACxB,IAAI,CAGN;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,GAChB,gBAAgB,GAAG,IAAI,CAMzB;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,OAAO,GACf,IAAI,CAoBN"}
@@ -0,0 +1,73 @@
1
+ import { logWarn } from './core.js';
2
+ import { isObject } from './utils.js';
3
+ const patchedCleanupServers = new WeakSet();
4
+ const serverCleanupCallbacks = new WeakMap();
5
+ function getServerCleanupCallbackSet(server) {
6
+ let callbacks = serverCleanupCallbacks.get(server);
7
+ if (!callbacks) {
8
+ callbacks = new Set();
9
+ serverCleanupCallbacks.set(server, callbacks);
10
+ }
11
+ return callbacks;
12
+ }
13
+ function drainServerCleanupCallbacks(server) {
14
+ const callbacks = serverCleanupCallbacks.get(server);
15
+ if (!callbacks || callbacks.size === 0)
16
+ return;
17
+ const pending = [...callbacks];
18
+ callbacks.clear();
19
+ for (const callback of pending) {
20
+ try {
21
+ callback();
22
+ }
23
+ catch (error) {
24
+ logWarn('Server cleanup callback failed', { error });
25
+ }
26
+ }
27
+ }
28
+ function ensureServerCleanupHooks(server) {
29
+ if (patchedCleanupServers.has(server))
30
+ return;
31
+ patchedCleanupServers.add(server);
32
+ const originalOnClose = server.server.onclose;
33
+ server.server.onclose = () => {
34
+ drainServerCleanupCallbacks(server);
35
+ originalOnClose?.();
36
+ };
37
+ const originalClose = server.close.bind(server);
38
+ server.close = async () => {
39
+ drainServerCleanupCallbacks(server);
40
+ await originalClose();
41
+ };
42
+ }
43
+ export function registerServerLifecycleCleanup(server, callback) {
44
+ ensureServerCleanupHooks(server);
45
+ getServerCleanupCallbackSet(server).add(callback);
46
+ }
47
+ export function getSdkCallToolHandler(server) {
48
+ const maybeHandlers = Reflect.get(server.server, '_requestHandlers');
49
+ if (!(maybeHandlers instanceof Map))
50
+ return null;
51
+ const handler = maybeHandlers.get('tools/call');
52
+ return typeof handler === 'function' ? handler : null;
53
+ }
54
+ export function setTaskToolCallCapability(server, enabled) {
55
+ const capabilities = Reflect.get(server.server, '_capabilities');
56
+ if (!isObject(capabilities))
57
+ return;
58
+ const tasks = isObject(capabilities.tasks)
59
+ ? capabilities.tasks
60
+ : undefined;
61
+ if (!tasks)
62
+ return;
63
+ const requests = isObject(tasks.requests)
64
+ ? tasks.requests
65
+ : undefined;
66
+ if (!requests)
67
+ return;
68
+ if (enabled) {
69
+ requests.tools = { call: {} };
70
+ return;
71
+ }
72
+ delete requests.tools;
73
+ }
@@ -1,6 +1,4 @@
1
1
  import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- type CleanupCallback = () => void;
3
- export declare function registerServerLifecycleCleanup(server: McpServer, callback: CleanupCallback): void;
4
2
  export { cancelTasksForOwner, abortAllTaskExecutions, } from '../tasks/execution.js';
5
3
  interface TaskHandlerRegistrationOptions {
6
4
  requireInterception?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"task-handlers.d.ts","sourceRoot":"","sources":["../../src/lib/task-handlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0CzE,KAAK,eAAe,GAAG,MAAM,IAAI,CAAC;AAyClC,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,eAAe,GACxB,IAAI,CAGN;AAED,OAAO,EACL,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AA+E/B,UAAU,8BAA8B;IACtC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,6BAA6B;IACrC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,0BAA0B,EAAE,OAAO,CAAC;CACrC;AAUD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE,8BAA8B,GACvC,6BAA6B,CAuJ/B"}
1
+ {"version":3,"file":"task-handlers.d.ts","sourceRoot":"","sources":["../../src/lib/task-handlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAkCzE,OAAO,EACL,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAoC/B,UAAU,8BAA8B;IACtC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,6BAA6B;IACrC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,0BAA0B,EAAE,OAAO,CAAC;CACrC;AACD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE,8BAA8B,GACvC,6BAA6B,CA0J/B"}
@@ -2,56 +2,13 @@ import { randomUUID } from 'node:crypto';
2
2
  import {} from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { CallToolRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
4
4
  import { z } from 'zod';
5
- import { abortTaskExecution, emitTaskStatusNotification, handleToolCallRequest, throwTaskNotFound, toTaskSummary, withRelatedTaskMeta, } from '../tasks/execution.js';
5
+ import { parseExtendedCallToolRequest, withRelatedTaskMeta, } from '../tasks/call-contract.js';
6
+ import { abortTaskExecution, emitTaskStatusNotification, handleToolCallRequest, throwTaskNotFound, toTaskSummary, } from '../tasks/execution.js';
6
7
  import { taskManager } from '../tasks/manager.js';
7
8
  import { isServerResult, parseHandlerExtra, resolveTaskOwnerKey, resolveToolCallContext, } from '../tasks/owner.js';
8
9
  import { hasRegisteredTaskCapableTools, hasTaskCapableTool, } from '../tasks/tool-registry.js';
9
10
  import { logWarn, runWithRequestContext } from './core.js';
10
- import { formatZodError } from './zod.js';
11
- const patchedCleanupServers = new WeakSet();
12
- const serverCleanupCallbacks = new WeakMap();
13
- function getServerCleanupCallbackSet(server) {
14
- let callbacks = serverCleanupCallbacks.get(server);
15
- if (!callbacks) {
16
- callbacks = new Set();
17
- serverCleanupCallbacks.set(server, callbacks);
18
- }
19
- return callbacks;
20
- }
21
- function drainServerCleanupCallbacks(server) {
22
- const callbacks = serverCleanupCallbacks.get(server);
23
- if (!callbacks || callbacks.size === 0)
24
- return;
25
- const pending = [...callbacks];
26
- callbacks.clear();
27
- for (const callback of pending) {
28
- try {
29
- callback();
30
- }
31
- catch (error) {
32
- logWarn('Server cleanup callback failed', { error });
33
- }
34
- }
35
- }
36
- function ensureServerCleanupHooks(server) {
37
- if (patchedCleanupServers.has(server))
38
- return;
39
- patchedCleanupServers.add(server);
40
- const originalOnClose = server.server.onclose;
41
- server.server.onclose = () => {
42
- drainServerCleanupCallbacks(server);
43
- originalOnClose?.();
44
- };
45
- const originalClose = server.close.bind(server);
46
- server.close = async () => {
47
- drainServerCleanupCallbacks(server);
48
- await originalClose();
49
- };
50
- }
51
- export function registerServerLifecycleCleanup(server, callback) {
52
- ensureServerCleanupHooks(server);
53
- getServerCleanupCallbackSet(server).add(callback);
54
- }
11
+ import { getSdkCallToolHandler } from './sdk-interop.js';
55
12
  export { cancelTasksForOwner, abortAllTaskExecutions, } from '../tasks/execution.js';
56
13
  /* -------------------------------------------------------------------------------------------------
57
14
  * Task handler schemas and registration
@@ -76,41 +33,6 @@ const TaskResultSchema = z.looseObject({
76
33
  method: z.literal('tasks/result'),
77
34
  params: z.looseObject({ taskId: z.string() }),
78
35
  });
79
- const MIN_TASK_TTL_MS = 1_000;
80
- const MAX_TASK_TTL_MS = 86_400_000;
81
- const ExtendedCallToolRequestSchema = z.looseObject({
82
- method: z.literal('tools/call'),
83
- params: z.strictObject({
84
- name: z.string().min(1),
85
- arguments: z.record(z.string(), z.unknown()).optional(),
86
- task: z
87
- .strictObject({
88
- ttl: z
89
- .number()
90
- .int()
91
- .min(MIN_TASK_TTL_MS)
92
- .max(MAX_TASK_TTL_MS)
93
- .optional(),
94
- })
95
- .optional(),
96
- _meta: z
97
- .looseObject({
98
- progressToken: z.union([z.string(), z.number()]).optional(),
99
- 'io.modelcontextprotocol/related-task': z
100
- .strictObject({
101
- taskId: z.string(),
102
- })
103
- .optional(),
104
- })
105
- .optional(),
106
- }),
107
- });
108
- function parseExtendedCallToolRequest(request) {
109
- const parsed = ExtendedCallToolRequestSchema.safeParse(request);
110
- if (parsed.success)
111
- return parsed.data;
112
- throw new McpError(ErrorCode.InvalidParams, `Invalid tool request params: ${formatZodError(parsed.error)}`);
113
- }
114
36
  function resolveOwnerScopedExtra(extra) {
115
37
  const parsedExtra = parseHandlerExtra(extra);
116
38
  return {
@@ -118,14 +40,6 @@ function resolveOwnerScopedExtra(extra) {
118
40
  ownerKey: resolveTaskOwnerKey(parsedExtra),
119
41
  };
120
42
  }
121
- function getSdkCallToolHandler(server) {
122
- // S-2: see tests/sdk-compat-guard.test.ts
123
- const maybeHandlers = Reflect.get(server.server, '_requestHandlers');
124
- if (!(maybeHandlers instanceof Map))
125
- return null;
126
- const handler = maybeHandlers.get('tools/call');
127
- return typeof handler === 'function' ? handler : null;
128
- }
129
43
  export function registerTaskHandlers(server, options) {
130
44
  const sdkCallToolHandler = getSdkCallToolHandler(server);
131
45
  const taskCapableToolsRegistered = hasRegisteredTaskCapableTools();
@@ -139,15 +53,15 @@ export function registerTaskHandlers(server, options) {
139
53
  if (sdkCallToolHandler) {
140
54
  server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
141
55
  const parsedExtra = parseHandlerExtra(extra);
142
- const context = resolveToolCallContext(parsedExtra);
143
- const requestId = context.requestId !== undefined
144
- ? String(context.requestId)
56
+ const requestId = parsedExtra?.requestId !== undefined
57
+ ? String(parsedExtra.requestId)
145
58
  : randomUUID();
146
- const sessionId = parsedExtra?.sessionId;
147
59
  return runWithRequestContext({
148
60
  requestId,
149
61
  operationId: requestId,
150
- ...(sessionId ? { sessionId } : {}),
62
+ ...(parsedExtra?.sessionId
63
+ ? { sessionId: parsedExtra.sessionId }
64
+ : {}),
151
65
  }, () => {
152
66
  const toolName = request.params.name;
153
67
  // Only intercept task-capable tools managed by the local task registry.
@@ -156,6 +70,7 @@ export function registerTaskHandlers(server, options) {
156
70
  return sdkCallToolHandler(request, extra);
157
71
  }
158
72
  const parsed = parseExtendedCallToolRequest(request);
73
+ const context = resolveToolCallContext(parsedExtra, parsed.params._meta);
159
74
  return handleToolCallRequest(server, parsed, context);
160
75
  });
161
76
  });
@@ -2,7 +2,7 @@ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { ErrorCode, McpError, SubscribeRequestSchema, UnsubscribeRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
3
3
  import { get as getCacheEntry, getEntryMeta, keys as listCacheKeys, onCacheUpdate, parseCacheKey, } from '../lib/core.js';
4
4
  import { logWarn } from '../lib/core.js';
5
- import { registerServerLifecycleCleanup } from '../lib/task-handlers.js';
5
+ import { registerServerLifecycleCleanup } from '../lib/sdk-interop.js';
6
6
  import { buildOptionalIcons } from '../lib/types.js';
7
7
  import { isObject } from '../lib/utils.js';
8
8
  import { parseCachedPayload, resolveCachedPayloadContent } from '../schemas.js';
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAkGpE,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAE1D;AA8CD,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,SAAS,CAAC,CAExE;AAkFD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMtD"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0GpE,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAE1D;AAiDD,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,SAAS,CAAC,CAExE;AAkFD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMtD"}
package/dist/server.js CHANGED
@@ -5,12 +5,13 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
5
5
  import { SetLevelRequestSchema } from '@modelcontextprotocol/sdk/types.js';
6
6
  import { config } from './lib/core.js';
7
7
  import { getSessionId, logError, logInfo, setLogLevel, setMcpServer, } from './lib/core.js';
8
+ import { setTaskToolCallCapability } from './lib/sdk-interop.js';
8
9
  import { abortAllTaskExecutions, registerTaskHandlers, } from './lib/task-handlers.js';
9
10
  import { toError } from './lib/utils.js';
10
11
  import { registerGetHelpPrompt } from './prompts/index.js';
11
12
  import { registerCacheResourceTemplate, registerInstructionResource, } from './resources/index.js';
12
13
  import { buildServerInstructions } from './resources/instructions.js';
13
- import { registerAllTools } from './tools/index.js';
14
+ import { registerTools as registerFetchUrlTool } from './tools/fetch-url.js';
14
15
  import { shutdownTransformWorkerPool } from './transform/transform.js';
15
16
  /* -------------------------------------------------------------------------------------------------
16
17
  * Icons + server info
@@ -50,6 +51,9 @@ function createServerCapabilities() {
50
51
  },
51
52
  };
52
53
  }
54
+ function syncTaskCapabilityAdvertisement(server, taskToolCallEnabled) {
55
+ setTaskToolCallCapability(server, taskToolCallEnabled);
56
+ }
53
57
  function createServerInfo(icons) {
54
58
  return {
55
59
  name: config.server.name,
@@ -83,13 +87,16 @@ async function createMcpServerWithOptions(options) {
83
87
  if (shouldRegisterObservabilityServer(options)) {
84
88
  setMcpServer(server);
85
89
  }
86
- registerAllTools(server);
90
+ const toolControls = registerFetchUrlTool(server);
87
91
  registerGetHelpPrompt(server, serverInstructions, localIcon);
88
92
  registerInstructionResource(server, serverInstructions, localIcon);
89
93
  registerCacheResourceTemplate(server, localIcon);
90
- registerTaskHandlers(server, {
94
+ const taskRegistration = registerTaskHandlers(server, {
91
95
  requireInterception: config.tasks.requireInterception,
92
96
  });
97
+ const taskToolCallEnabled = taskRegistration.interceptedToolsCall;
98
+ toolControls.setTaskSupport(taskToolCallEnabled ? 'optional' : 'forbidden');
99
+ syncTaskCapabilityAdvertisement(server, taskToolCallEnabled);
93
100
  registerLoggingSetLevelHandler(server);
94
101
  attachServerErrorHandler(server);
95
102
  return server;
@@ -0,0 +1,25 @@
1
+ import type { ServerResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { z } from 'zod';
3
+ export declare const extendedCallToolRequestSchema: z.ZodObject<{
4
+ method: z.ZodLiteral<"tools/call">;
5
+ params: z.ZodObject<{
6
+ name: z.ZodString;
7
+ arguments: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
8
+ task: z.ZodOptional<z.ZodObject<{
9
+ ttl: z.ZodOptional<z.ZodNumber>;
10
+ }, z.core.$strict>>;
11
+ _meta: z.ZodOptional<z.ZodObject<{
12
+ progressToken: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
13
+ 'io.modelcontextprotocol/related-task': z.ZodOptional<z.ZodObject<{
14
+ taskId: z.ZodString;
15
+ }, z.core.$strict>>;
16
+ }, z.core.$loose>>;
17
+ }, z.core.$strict>;
18
+ }, z.core.$loose>;
19
+ export type ExtendedCallToolRequest = z.infer<typeof extendedCallToolRequestSchema>;
20
+ export type ToolCallRequestMeta = ExtendedCallToolRequest['params']['_meta'];
21
+ export declare function parseExtendedCallToolRequest(request: unknown): ExtendedCallToolRequest;
22
+ export declare function sanitizeToolCallMeta(meta?: ToolCallRequestMeta): ToolCallRequestMeta | undefined;
23
+ export declare function buildRelatedTaskMeta(taskId: string, meta?: ToolCallRequestMeta): Record<string, unknown>;
24
+ export declare function withRelatedTaskMeta(result: ServerResult, taskId: string): ServerResult;
25
+ //# sourceMappingURL=call-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-contract.d.ts","sourceRoot":"","sources":["../../src/tasks/call-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;iBAiBxC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAC3C,OAAO,6BAA6B,CACrC,CAAC;AACF,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC;AAE7E,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,OAAO,GACf,uBAAuB,CAQzB;AAED,wBAAgB,oBAAoB,CAClC,IAAI,CAAC,EAAE,mBAAmB,GACzB,mBAAmB,GAAG,SAAS,CAMjC;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,mBAAmB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,GACb,YAAY,CAQd"}
@@ -0,0 +1,58 @@
1
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
+ import { z } from 'zod';
3
+ import { formatZodError } from '../lib/zod.js';
4
+ const MIN_TASK_TTL_MS = 1_000;
5
+ const MAX_TASK_TTL_MS = 86_400_000;
6
+ const relatedTaskMetaSchema = z.strictObject({
7
+ taskId: z.string(),
8
+ });
9
+ const toolCallMetaSchema = z.looseObject({
10
+ progressToken: z.union([z.string(), z.number()]).optional(),
11
+ 'io.modelcontextprotocol/related-task': relatedTaskMetaSchema.optional(),
12
+ });
13
+ export const extendedCallToolRequestSchema = z.looseObject({
14
+ method: z.literal('tools/call'),
15
+ params: z.strictObject({
16
+ name: z.string().min(1),
17
+ arguments: z.record(z.string(), z.unknown()).optional(),
18
+ task: z
19
+ .strictObject({
20
+ ttl: z
21
+ .number()
22
+ .int()
23
+ .min(MIN_TASK_TTL_MS)
24
+ .max(MAX_TASK_TTL_MS)
25
+ .optional(),
26
+ })
27
+ .optional(),
28
+ _meta: toolCallMetaSchema.optional(),
29
+ }),
30
+ });
31
+ export function parseExtendedCallToolRequest(request) {
32
+ const parsed = extendedCallToolRequestSchema.safeParse(request);
33
+ if (parsed.success)
34
+ return parsed.data;
35
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tool request params: ${formatZodError(parsed.error)}`);
36
+ }
37
+ export function sanitizeToolCallMeta(meta) {
38
+ if (!meta)
39
+ return undefined;
40
+ const sanitized = { ...meta };
41
+ delete sanitized['io.modelcontextprotocol/related-task'];
42
+ return Object.keys(sanitized).length > 0 ? sanitized : undefined;
43
+ }
44
+ export function buildRelatedTaskMeta(taskId, meta) {
45
+ return {
46
+ ...(sanitizeToolCallMeta(meta) ?? {}),
47
+ 'io.modelcontextprotocol/related-task': { taskId },
48
+ };
49
+ }
50
+ export function withRelatedTaskMeta(result, taskId) {
51
+ return {
52
+ ...result,
53
+ _meta: {
54
+ ...result._meta,
55
+ 'io.modelcontextprotocol/related-task': { taskId },
56
+ },
57
+ };
58
+ }
@@ -0,0 +1,5 @@
1
+ export declare function encodeTaskCursor(anchorTaskId: string): string;
2
+ export declare function decodeTaskCursor(cursor: string): {
3
+ anchorTaskId: string;
4
+ } | null;
5
+ //# sourceMappingURL=cursor-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-codec.d.ts","sourceRoot":"","sources":["../../src/tasks/cursor-codec.ts"],"names":[],"mappings":"AAkBA,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAO7D;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GACb;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAuBjC"}
@@ -0,0 +1,41 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ const MAX_CURSOR_LENGTH = 256;
3
+ const CURSOR_SECRET = randomBytes(32);
4
+ function signPayload(payload) {
5
+ return createHmac('sha256', CURSOR_SECRET)
6
+ .update(payload)
7
+ .digest('base64url');
8
+ }
9
+ function safeCompare(left, right) {
10
+ const leftBuffer = Buffer.from(left);
11
+ const rightBuffer = Buffer.from(right);
12
+ if (leftBuffer.length !== rightBuffer.length)
13
+ return false;
14
+ return timingSafeEqual(leftBuffer, rightBuffer);
15
+ }
16
+ export function encodeTaskCursor(anchorTaskId) {
17
+ const payload = Buffer.from(JSON.stringify({ anchorTaskId }), 'utf8').toString('base64url');
18
+ const signature = signPayload(payload);
19
+ return `${payload}.${signature}`;
20
+ }
21
+ export function decodeTaskCursor(cursor) {
22
+ if (!cursor || cursor.length > MAX_CURSOR_LENGTH)
23
+ return null;
24
+ const [payload, signature, ...rest] = cursor.split('.');
25
+ if (!payload || !signature || rest.length > 0)
26
+ return null;
27
+ if (!safeCompare(signPayload(payload), signature))
28
+ return null;
29
+ try {
30
+ const decoded = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
31
+ if (typeof decoded.anchorTaskId !== 'string' ||
32
+ decoded.anchorTaskId.length === 0 ||
33
+ decoded.anchorTaskId.length > 128) {
34
+ return null;
35
+ }
36
+ return { anchorTaskId: decoded.anchorTaskId };
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
@@ -1,26 +1,8 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { type ServerResult } from '@modelcontextprotocol/sdk/types.js';
3
+ import { type ExtendedCallToolRequest } from './call-contract.js';
3
4
  import { type CreateTaskResult, type TaskState } from './manager.js';
4
5
  import { type ToolCallContext } from './owner.js';
5
- export interface ExtendedCallToolRequest {
6
- method: 'tools/call';
7
- params: {
8
- name: string;
9
- arguments?: Record<string, unknown> | undefined;
10
- task?: {
11
- ttl?: number | undefined;
12
- } | undefined;
13
- _meta?: {
14
- progressToken?: string | number | undefined;
15
- 'io.modelcontextprotocol/related-task'?: {
16
- taskId: string;
17
- } | undefined;
18
- [key: string]: unknown;
19
- } | undefined;
20
- [key: string]: unknown;
21
- };
22
- [key: string]: unknown;
23
- }
24
6
  export declare function abortTaskExecution(taskId: string): void;
25
7
  export declare function cancelTasksForOwner(ownerKey: string, statusMessage?: string): number;
26
8
  export declare function abortAllTaskExecutions(): void;
@@ -34,7 +16,6 @@ export declare function toTaskSummary(task: {
34
16
  ttl: number;
35
17
  pollInterval: number;
36
18
  }): TaskSummary;
37
- export declare function withRelatedTaskMeta(result: ServerResult, taskId: string): ServerResult;
38
19
  export declare function emitTaskStatusNotification(server: McpServer, task: TaskState): void;
39
20
  export declare function throwTaskNotFound(): never;
40
21
  export declare function handleToolCallRequest(server: McpServer, request: ExtendedCallToolRequest, context: ToolCallContext): Promise<ServerResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../src/tasks/execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,oCAAoC,CAAC;AAW5C,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,YAAY,CAAC;AAapB,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;QAChD,IAAI,CAAC,EACD;YACE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;SAC1B,GACD,SAAS,CAAC;QACd,KAAK,CAAC,EACF;YACE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;YAC5C,sCAAsC,CAAC,EACnC;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,GAClB,SAAS,CAAC;YACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,GACD,SAAS,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAyBD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAKvD;AAMD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,SAA4D,GACxE,MAAM,CASR;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAgBD,KAAK,WAAW,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAE5C,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,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;CACtB,GAAG,WAAW,CAUd;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,GACb,YAAY,CAYd;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,GACd,IAAI,CAmBN;AAoBD,wBAAgB,iBAAiB,IAAI,KAAK,CAEzC;AAmND,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,CAAC,CAYvB"}
1
+ {"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../src/tasks/execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,oCAAoC,CAAC;AAQ5C,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,YAAY,CAAC;AAgCpB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAKvD;AAMD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,SAA4D,GACxE,MAAM,CASR;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAgBD,KAAK,WAAW,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAE5C,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,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;CACtB,GAAG,WAAW,CAUd;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,GACd,IAAI,CAmBN;AAoBD,wBAAgB,iBAAiB,IAAI,KAAK,CAEzC;AA+LD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,CAAC,CAYvB"}