@j0hanz/fetch-url-mcp 1.12.0 → 1.12.2

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 (61) hide show
  1. package/README.md +34 -17
  2. package/dist/http/auth.d.ts.map +1 -1
  3. package/dist/http/auth.js +61 -20
  4. package/dist/http/helpers.d.ts +1 -1
  5. package/dist/http/helpers.d.ts.map +1 -1
  6. package/dist/http/helpers.js +7 -9
  7. package/dist/http/native.d.ts.map +1 -1
  8. package/dist/http/native.js +271 -54
  9. package/dist/http/rate-limit.d.ts.map +1 -1
  10. package/dist/http/rate-limit.js +2 -1
  11. package/dist/index.js +5 -4
  12. package/dist/lib/config.d.ts +1 -1
  13. package/dist/lib/config.d.ts.map +1 -1
  14. package/dist/lib/config.js +8 -1
  15. package/dist/lib/core.d.ts +8 -4
  16. package/dist/lib/core.d.ts.map +1 -1
  17. package/dist/lib/core.js +240 -73
  18. package/dist/lib/fetch-pipeline.d.ts.map +1 -1
  19. package/dist/lib/fetch-pipeline.js +15 -2
  20. package/dist/lib/http.d.ts.map +1 -1
  21. package/dist/lib/http.js +1 -1
  22. package/dist/lib/mcp-interop.d.ts +15 -3
  23. package/dist/lib/mcp-interop.d.ts.map +1 -1
  24. package/dist/lib/mcp-interop.js +92 -23
  25. package/dist/lib/url.d.ts.map +1 -1
  26. package/dist/lib/url.js +1 -1
  27. package/dist/lib/utils.d.ts.map +1 -1
  28. package/dist/lib/utils.js +2 -2
  29. package/dist/resources/index.d.ts +4 -0
  30. package/dist/resources/index.d.ts.map +1 -1
  31. package/dist/resources/index.js +39 -4
  32. package/dist/schemas.d.ts +5 -5
  33. package/dist/schemas.d.ts.map +1 -1
  34. package/dist/schemas.js +7 -9
  35. package/dist/server.d.ts +3 -1
  36. package/dist/server.d.ts.map +1 -1
  37. package/dist/server.js +20 -11
  38. package/dist/tasks/execution.d.ts +1 -1
  39. package/dist/tasks/execution.d.ts.map +1 -1
  40. package/dist/tasks/execution.js +72 -25
  41. package/dist/tasks/handlers.d.ts.map +1 -1
  42. package/dist/tasks/handlers.js +31 -24
  43. package/dist/tasks/manager.d.ts +5 -2
  44. package/dist/tasks/manager.d.ts.map +1 -1
  45. package/dist/tasks/manager.js +58 -19
  46. package/dist/tasks/owner.d.ts +5 -0
  47. package/dist/tasks/owner.d.ts.map +1 -1
  48. package/dist/tasks/owner.js +15 -7
  49. package/dist/tasks/registry.d.ts +10 -8
  50. package/dist/tasks/registry.d.ts.map +1 -1
  51. package/dist/tasks/registry.js +27 -15
  52. package/dist/tools/fetch-url.d.ts +2 -0
  53. package/dist/tools/fetch-url.d.ts.map +1 -1
  54. package/dist/tools/fetch-url.js +76 -21
  55. package/dist/transform/dom-prep.d.ts.map +1 -1
  56. package/dist/transform/dom-prep.js +6 -6
  57. package/dist/transform/transform.d.ts.map +1 -1
  58. package/dist/transform/transform.js +17 -14
  59. package/dist/transform/worker-pool.d.ts.map +1 -1
  60. package/dist/transform/worker-pool.js +43 -3
  61. package/package.json +2 -2
@@ -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;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,CAGvD;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,SAA4D,GACxE,MAAM,CAOR;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAMD,KAAK,WAAW,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAC5C,KAAK,uBAAuB,GAAG,IAAI,CACjC,SAAS,EACP,QAAQ,GACR,QAAQ,GACR,eAAe,GACf,WAAW,GACX,eAAe,GACf,KAAK,GACL,cAAc,CACjB,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,uBAAuB,GAAG,WAAW,CAUxE;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,GACd,IAAI,CAeN;AAED,wBAAgB,iBAAiB,IAAI,KAAK,CAEzC;AAmID,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,CAAC,CA4CvB"}
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;AAc5C,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;AAkCpB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGvD;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,SAA4D,GACxE,MAAM,CAOR;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAMD,KAAK,WAAW,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAC5C,KAAK,uBAAuB,GAAG,IAAI,CACjC,SAAS,EACP,QAAQ,GACR,QAAQ,GACR,eAAe,GACf,UAAU,GACV,OAAO,GACP,WAAW,GACX,eAAe,GACf,KAAK,GACL,cAAc,CACjB,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,uBAAuB,GAAG,WAAW,CAYxE;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,GACd,IAAI,CAmBN;AAED,wBAAgB,iBAAiB,IAAI,KAAK,CAEzC;AA2JD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,CAAC,CAwFvB"}
@@ -1,13 +1,13 @@
1
1
  import { ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { config } from '../lib/core.js';
3
- import { logWarn, runWithRequestContext } from '../lib/core.js';
3
+ import { logDebug, logError, logInfo, logWarn, runWithRequestContext, } from '../lib/core.js';
4
+ import {} from '../lib/mcp-interop.js';
4
5
  import { getErrorMessage } from '../lib/utils.js';
5
6
  import { isObject } from '../lib/utils.js';
6
7
  import { buildRelatedTaskMeta, } from './call-contract.js';
7
8
  import { taskManager, } from './manager.js';
8
9
  import { buildToolHandlerExtra, compact, tryReadToolStructuredError, } from './owner.js';
9
10
  import { getTaskCapableTool, getTaskCapableToolSupport, } from './registry.js';
10
- const TASK_NOT_FOUND_ERROR_CODE = -32002;
11
11
  /* -------------------------------------------------------------------------------------------------
12
12
  * Abort-controller management for in-flight task executions
13
13
  * ------------------------------------------------------------------------------------------------- */
@@ -20,7 +20,7 @@ function attachAbortController(taskId) {
20
20
  logWarn('Abort controller map reached task capacity — possible leak', {
21
21
  size: taskAbortControllers.size,
22
22
  maxTotal: config.tasks.maxTotal,
23
- });
23
+ }, 'tasks');
24
24
  }
25
25
  const controller = new AbortController();
26
26
  taskAbortControllers.set(taskId, controller);
@@ -48,6 +48,8 @@ export function toTaskSummary(task) {
48
48
  taskId: task.taskId,
49
49
  status: task.status,
50
50
  ...(task.statusMessage ? { statusMessage: task.statusMessage } : {}),
51
+ ...(task.progress !== undefined ? { progress: task.progress } : {}),
52
+ ...(task.total !== undefined ? { total: task.total } : {}),
51
53
  createdAt: task.createdAt,
52
54
  lastUpdatedAt: task.lastUpdatedAt,
53
55
  ttl: task.ttl,
@@ -63,15 +65,15 @@ export function emitTaskStatusNotification(server, task) {
63
65
  params: { ...toTaskSummary(task) },
64
66
  })
65
67
  .catch((error) => {
66
- logWarn('Failed to send task status notification', {
68
+ logError('Failed to send task status notification', {
67
69
  taskId: task.taskId,
68
70
  status: task.status,
69
71
  error: getErrorMessage(error),
70
- });
72
+ }, 'tasks');
71
73
  });
72
74
  }
73
75
  export function throwTaskNotFound() {
74
- throw new McpError(TASK_NOT_FOUND_ERROR_CODE, 'Task not found');
76
+ throw new McpError(ErrorCode.InvalidParams, 'Task not found');
75
77
  }
76
78
  /* -------------------------------------------------------------------------------------------------
77
79
  * Execution pipeline
@@ -87,28 +89,23 @@ function buildTaskFailureState(error) {
87
89
  ? (/^MCP error -?\d+:\s*(.*)$/s.exec(error.message)?.[1] ?? error.message)
88
90
  : undefined;
89
91
  const statusMessage = mcpErrorMessage ?? getErrorMessage(error);
90
- const payload = { error: statusMessage };
91
92
  if (error instanceof McpError) {
92
- payload['code'] = error.code;
93
- if (error.data !== undefined) {
94
- payload['data'] = error.data;
95
- }
96
93
  return {
97
94
  status: 'failed',
98
95
  statusMessage,
99
- result: {
100
- content: [{ type: 'text', text: JSON.stringify(payload) }],
101
- isError: true,
96
+ error: {
97
+ code: error.code,
98
+ ...(error.data !== undefined ? { data: error.data } : {}),
99
+ message: statusMessage,
102
100
  },
103
101
  };
104
102
  }
105
- payload['code'] = ErrorCode.InternalError;
106
103
  return {
107
104
  status: 'failed',
108
105
  statusMessage,
109
- result: {
110
- content: [{ type: 'text', text: JSON.stringify(payload) }],
111
- isError: true,
106
+ error: {
107
+ code: ErrorCode.InternalError,
108
+ message: statusMessage,
112
109
  },
113
110
  };
114
111
  }
@@ -131,30 +128,50 @@ async function runTaskToolExecution(params) {
131
128
  ...(sessionId ? { sessionId } : {}),
132
129
  }, async () => {
133
130
  const controller = attachAbortController(taskId);
131
+ const progressState = { closed: false };
134
132
  try {
133
+ logInfo('Task execution started', { taskId, tool: tool.name }, 'tasks');
135
134
  const relatedMeta = buildRelatedTaskMeta(taskId, meta);
136
135
  const result = await tool.execute(args, {
137
136
  signal: controller.signal,
138
137
  requestId: taskId,
139
138
  _meta: relatedMeta,
139
+ progressState,
140
140
  canReportProgress: () => taskManager.getTask(taskId)?.status === 'working',
141
141
  ...compact({ sendNotification }),
142
- onProgress: (_progress, message) => {
142
+ onProgress: (progress, message, total) => {
143
143
  const current = taskManager.getTask(taskId);
144
144
  if (current?.status === 'working' &&
145
- current.statusMessage !== message) {
145
+ (current.statusMessage !== message ||
146
+ current.progress !== progress ||
147
+ (total !== undefined && current.total !== total))) {
146
148
  updateTaskAndEmitStatus(server, taskId, {
147
149
  statusMessage: message,
150
+ progress,
151
+ ...(total !== undefined ? { total } : {}),
148
152
  });
149
153
  }
150
154
  },
151
155
  });
152
- updateTaskAndEmitStatus(server, taskId, buildTaskCompletionUpdate(result, tool));
156
+ const completionUpdate = buildTaskCompletionUpdate(result, tool);
157
+ updateTaskAndEmitStatus(server, taskId, completionUpdate);
158
+ if (completionUpdate.status === 'completed') {
159
+ logInfo('Task execution completed', { taskId, tool: tool.name }, 'tasks');
160
+ }
161
+ else {
162
+ logWarn('Task execution completed with tool error result', { taskId, tool: tool.name }, 'tasks');
163
+ }
153
164
  }
154
165
  catch (error) {
166
+ logError('Task execution failed', {
167
+ taskId,
168
+ tool: tool.name,
169
+ error: getErrorMessage(error),
170
+ }, 'tasks');
155
171
  updateTaskAndEmitStatus(server, taskId, buildTaskFailureState(error));
156
172
  }
157
173
  finally {
174
+ progressState.closed = true;
158
175
  taskAbortControllers.delete(taskId);
159
176
  }
160
177
  });
@@ -162,16 +179,21 @@ async function runTaskToolExecution(params) {
162
179
  export async function handleToolCallRequest(server, request, context) {
163
180
  const { params } = request;
164
181
  // Validate the tool name first so an unknown tool always produces MethodNotFound
165
- const tool = getTaskCapableTool(params.name);
182
+ const tool = getTaskCapableTool(server, params.name);
166
183
  if (!tool) {
167
184
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: '${params.name}'`);
168
185
  }
169
186
  if (params.task) {
170
- if (getTaskCapableToolSupport(params.name) === 'forbidden') {
187
+ if (getTaskCapableToolSupport(server, params.name) === 'forbidden') {
171
188
  throw new McpError(ErrorCode.MethodNotFound, `Task augmentation is forbidden for tool '${params.name}'`);
172
189
  }
173
190
  const args = tool.parseArguments(params.arguments);
174
191
  const task = taskManager.createTask(params.task.ttl !== undefined ? { ttl: params.task.ttl } : undefined, 'Task started', context.ownerKey);
192
+ logInfo('Task execution queued', {
193
+ taskId: task.taskId,
194
+ tool: params.name,
195
+ ...(params.task.ttl !== undefined ? { ttl: params.task.ttl } : {}),
196
+ }, 'tasks');
175
197
  void runTaskToolExecution({
176
198
  server,
177
199
  taskId: task.taskId,
@@ -183,8 +205,33 @@ export async function handleToolCallRequest(server, request, context) {
183
205
  sendNotification: context.sendNotification,
184
206
  }),
185
207
  });
186
- return { task: toTaskSummary(task) };
208
+ return {
209
+ task: toTaskSummary(task),
210
+ ...(tool.immediateResponse
211
+ ? {
212
+ _meta: {
213
+ 'io.modelcontextprotocol/model-immediate-response': tool.immediateResponse,
214
+ },
215
+ }
216
+ : {}),
217
+ };
218
+ }
219
+ if (getTaskCapableToolSupport(server, params.name) === 'required') {
220
+ throw new McpError(ErrorCode.MethodNotFound, `Task augmentation is required for tool '${params.name}'`);
187
221
  }
188
222
  const args = tool.parseArguments(params.arguments);
189
- return tool.execute(args, buildToolHandlerExtra(context, params._meta));
223
+ const progressState = { closed: false };
224
+ logDebug('Executing task-capable tool inline', {
225
+ tool: params.name,
226
+ hasProgressToken: params._meta?.progressToken !== undefined,
227
+ }, 'tasks');
228
+ try {
229
+ return await tool.execute(args, {
230
+ ...buildToolHandlerExtra(context, params._meta),
231
+ progressState,
232
+ });
233
+ }
234
+ finally {
235
+ progressState.closed = true;
236
+ }
190
237
  }
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/tasks/handlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmCzE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AA4D7E,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,CAyJ/B"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/tasks/handlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmCzE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AA4D7E,UAAU,8BAA8B;IACtC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,6BAA6B;IACrC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,0BAA0B,EAAE,OAAO,CAAC;CACrC;AAkBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE,8BAA8B,GACvC,6BAA6B,CAoK/B"}
@@ -1,8 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import {} from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { CallToolRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
3
+ import { CallToolRequestSchema, ErrorCode, } from '@modelcontextprotocol/sdk/types.js';
4
+ import { McpError } from '@modelcontextprotocol/sdk/types.js';
4
5
  import { z } from 'zod';
5
- import { logWarn, runWithRequestContext } from '../lib/core.js';
6
+ import { logDebug, logWarn, runWithRequestContext } from '../lib/core.js';
6
7
  import { getSdkCallToolHandler } from '../lib/mcp-interop.js';
7
8
  import { parseExtendedCallToolRequest, withRelatedTaskMeta, } from './call-contract.js';
8
9
  import { abortTaskExecution, emitTaskStatusNotification, handleToolCallRequest, throwTaskNotFound, toTaskSummary, } from './execution.js';
@@ -40,15 +41,21 @@ function resolveOwnerScopedExtra(extra) {
40
41
  ownerKey: resolveTaskOwnerKey(parsedExtra),
41
42
  };
42
43
  }
44
+ function throwStoredTaskError(task) {
45
+ if (task.error) {
46
+ throw new McpError(task.error.code, task.error.message, task.error.data);
47
+ }
48
+ throw new McpError(ErrorCode.InternalError, task.statusMessage ?? 'Task execution failed', { taskId: task.taskId });
49
+ }
43
50
  export function registerTaskHandlers(server, options) {
44
51
  const sdkCallToolHandler = getSdkCallToolHandler(server);
45
- const taskCapableToolsRegistered = hasRegisteredTaskCapableTools();
52
+ const taskCapableToolsRegistered = hasRegisteredTaskCapableTools(server);
46
53
  const requireInterception = options?.requireInterception ?? true;
47
54
  if (!sdkCallToolHandler) {
48
55
  if (taskCapableToolsRegistered && requireInterception) {
49
56
  throw new Error('Task-capable tools are registered but SDK tools/call interception is unavailable. Upgrade compatibility or disable strict interception with TASKS_REQUIRE_INTERCEPTION=false.');
50
57
  }
51
- logWarn('Task call interception disabled: SDK tools/call handler unavailable; task-capable tools require MCP SDK compatibility update', { sdkVersion: 'unknown' });
58
+ logWarn('Task call interception disabled: SDK tools/call handler unavailable; task-capable tools require MCP SDK compatibility update', { sdkVersion: 'unknown' }, 'tasks');
52
59
  }
53
60
  if (sdkCallToolHandler) {
54
61
  server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
@@ -66,11 +73,16 @@ export function registerTaskHandlers(server, options) {
66
73
  const toolName = request.params.name;
67
74
  // Only intercept task-capable tools managed by the local task registry.
68
75
  // Delegate all other tools to the SDK handler to avoid shadowing future tools.
69
- if (!hasTaskCapableTool(toolName)) {
76
+ if (!hasTaskCapableTool(server, toolName)) {
70
77
  return sdkCallToolHandler(request, extra);
71
78
  }
72
79
  const parsed = parseExtendedCallToolRequest(request);
73
80
  const context = resolveToolCallContext(parsedExtra, parsed.params._meta);
81
+ logDebug('Intercepted task-capable tool call', {
82
+ tool: toolName,
83
+ taskRequested: parsed.params.task !== undefined,
84
+ hasProgressToken: parsed.params._meta?.progressToken !== undefined,
85
+ }, 'tasks');
74
86
  return handleToolCallRequest(server, parsed, context);
75
87
  });
76
88
  });
@@ -78,6 +90,7 @@ export function registerTaskHandlers(server, options) {
78
90
  server.server.setRequestHandler(TaskGetSchema, (request, extra) => {
79
91
  const { taskId } = request.params;
80
92
  const { ownerKey } = resolveOwnerScopedExtra(extra);
93
+ logDebug('tasks/get requested', { taskId }, 'tasks');
81
94
  const task = taskManager.getTask(taskId, ownerKey);
82
95
  if (!task)
83
96
  throwTaskNotFound();
@@ -86,31 +99,23 @@ export function registerTaskHandlers(server, options) {
86
99
  server.server.setRequestHandler(TaskResultSchema, async (request, extra) => {
87
100
  const { taskId } = request.params;
88
101
  const { parsedExtra, ownerKey } = resolveOwnerScopedExtra(extra);
102
+ logDebug('tasks/result requested', { taskId }, 'tasks');
89
103
  const task = await taskManager.waitForTerminalTask(taskId, ownerKey, parsedExtra?.signal);
90
104
  if (!task)
91
105
  throwTaskNotFound();
92
106
  try {
107
+ if (task.status === 'cancelled') {
108
+ throwStoredTaskError(task);
109
+ }
93
110
  if (task.status === 'failed') {
111
+ if (task.error) {
112
+ throwStoredTaskError(task);
113
+ }
94
114
  const failedResult = (task.result ?? null);
95
- const fallback = failedResult ?? {
96
- content: [
97
- {
98
- type: 'text',
99
- text: JSON.stringify({
100
- error: task.statusMessage ?? 'Task execution failed',
101
- }),
102
- },
103
- ],
104
- isError: true,
105
- };
106
- return withRelatedTaskMeta(fallback, task.taskId);
107
- }
108
- if (task.status === 'cancelled') {
109
- throw new McpError(ErrorCode.InvalidRequest, 'Task was cancelled', {
110
- taskId: task.taskId,
111
- status: 'cancelled',
112
- ...(task.statusMessage ? { statusMessage: task.statusMessage } : {}),
113
- });
115
+ if (failedResult) {
116
+ return withRelatedTaskMeta(failedResult, task.taskId);
117
+ }
118
+ throwStoredTaskError(task);
114
119
  }
115
120
  const result = isServerResult(task.result)
116
121
  ? task.result
@@ -127,6 +132,7 @@ export function registerTaskHandlers(server, options) {
127
132
  server.server.setRequestHandler(TaskListSchema, (request, extra) => {
128
133
  const { ownerKey } = resolveOwnerScopedExtra(extra);
129
134
  const cursor = request.params?.cursor;
135
+ logDebug('tasks/list requested', { hasCursor: cursor !== undefined }, 'tasks');
130
136
  const { tasks, nextCursor } = taskManager.listTasks(cursor === undefined ? { ownerKey } : { ownerKey, cursor });
131
137
  return {
132
138
  tasks: tasks.map((task) => toTaskSummary(task)),
@@ -136,6 +142,7 @@ export function registerTaskHandlers(server, options) {
136
142
  server.server.setRequestHandler(TaskCancelSchema, (request, extra) => {
137
143
  const { taskId } = request.params;
138
144
  const { ownerKey } = resolveOwnerScopedExtra(extra);
145
+ logDebug('tasks/cancel requested', { taskId }, 'tasks');
139
146
  const task = taskManager.cancelTask(taskId, ownerKey);
140
147
  if (!task)
141
148
  throwTaskNotFound();
@@ -1,4 +1,4 @@
1
- export type TaskStatus = 'working' | 'completed' | 'failed' | 'cancelled';
1
+ export type TaskStatus = 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled';
2
2
  export interface TaskError {
3
3
  code: number;
4
4
  message: string;
@@ -9,6 +9,8 @@ export interface TaskState {
9
9
  ownerKey: string;
10
10
  status: TaskStatus;
11
11
  statusMessage?: string;
12
+ progress?: number;
13
+ total?: number;
12
14
  createdAt: string;
13
15
  lastUpdatedAt: string;
14
16
  ttl: number;
@@ -25,6 +27,8 @@ export interface CreateTaskResult {
25
27
  taskId: string;
26
28
  status: TaskStatus;
27
29
  statusMessage?: string;
30
+ progress?: number;
31
+ total?: number;
28
32
  createdAt: string;
29
33
  lastUpdatedAt: string;
30
34
  ttl: number;
@@ -33,7 +37,6 @@ export interface CreateTaskResult {
33
37
  }
34
38
  declare class TaskManager {
35
39
  private tasks;
36
- private activeTaskCount;
37
40
  private ownerCounts;
38
41
  private readonly waiters;
39
42
  private cleanupInterval;
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/tasks/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/tasks/manager.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { createHmac, randomBytes } from 'node:crypto';
3
3
  import { setInterval } from 'node:timers';
4
4
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
5
- import { config, logWarn } from '../lib/core.js';
5
+ import { config, logInfo, logWarn } from '../lib/core.js';
6
6
  import { isObject, timingSafeEqualUtf8 } from '../lib/utils.js';
7
7
  import { TaskWaiterRegistry, waitForTerminalTask as waitForTerminalTaskWithDeadline, } from './waiters.js';
8
8
  const DEFAULT_TTL_MS = 60_000;
@@ -15,12 +15,18 @@ const CLEANUP_INTERVAL_MS = 60_000;
15
15
  const RESULT_DELIVERY_GRACE_MS = 10_000;
16
16
  const TASK_STATUS_VALUES = new Set([
17
17
  'working',
18
+ 'input_required',
19
+ 'completed',
20
+ 'failed',
21
+ 'cancelled',
22
+ ]);
23
+ const TERMINAL_STATUSES = new Set([
18
24
  'completed',
19
25
  'failed',
20
26
  'cancelled',
21
27
  ]);
22
28
  function isTerminalStatus(status) {
23
- return status !== 'working';
29
+ return TERMINAL_STATUSES.has(status);
24
30
  }
25
31
  function resolveNextTaskStatus(task, updates) {
26
32
  const nextStatus = updates.status;
@@ -39,9 +45,24 @@ function normalizeTaskTtl(ttl) {
39
45
  return DEFAULT_TTL_MS;
40
46
  return Math.max(MIN_TTL_MS, Math.min(Math.trunc(ttl), MAX_TTL_MS));
41
47
  }
48
+ function logTaskStatusTransition(task, previousStatus, nextStatus) {
49
+ if (previousStatus === nextStatus)
50
+ return;
51
+ const meta = {
52
+ taskId: task.taskId,
53
+ ownerKey: task.ownerKey,
54
+ previousStatus,
55
+ nextStatus,
56
+ ...(task.statusMessage ? { statusMessage: task.statusMessage } : {}),
57
+ };
58
+ if (nextStatus === 'failed') {
59
+ logWarn('Task status changed to failed', meta, 'tasks');
60
+ return;
61
+ }
62
+ logInfo('Task status changed', meta, 'tasks');
63
+ }
42
64
  class TaskManager {
43
65
  tasks = new Map();
44
- activeTaskCount = 0;
45
66
  ownerCounts = new Map();
46
67
  waiters = new TaskWaiterRegistry(isTerminalStatus);
47
68
  cleanupInterval = null;
@@ -67,6 +88,11 @@ class TaskManager {
67
88
  const now = Date.now();
68
89
  for (const task of this.tasks.values()) {
69
90
  if (this.isTaskExpired(task, now)) {
91
+ logWarn('Task expired', {
92
+ taskId: task.taskId,
93
+ ownerKey: task.ownerKey,
94
+ status: task.status,
95
+ }, 'tasks');
70
96
  this.removeTask(task.taskId);
71
97
  }
72
98
  }
@@ -90,15 +116,18 @@ class TaskManager {
90
116
  task.lastUpdatedAt = new Date().toISOString();
91
117
  }
92
118
  cancelActiveTask(task, statusMessage) {
93
- this.applyTaskUpdate(task, { status: 'cancelled', statusMessage });
119
+ this.applyTaskUpdate(task, {
120
+ status: 'cancelled',
121
+ statusMessage,
122
+ error: {
123
+ code: ErrorCode.ConnectionClosed,
124
+ message: statusMessage,
125
+ data: { code: 'ABORTED' },
126
+ },
127
+ });
94
128
  this.waiters.notify(task);
95
- this.releaseTaskCapacity(task);
96
129
  }
97
130
  releaseTaskCapacity(task) {
98
- const internal = task;
99
- if (!internal._capacityCounted)
100
- return;
101
- internal._capacityCounted = false;
102
131
  const { ownerKey } = task;
103
132
  const nextCount = (this.ownerCounts.get(ownerKey) ?? 0) - 1;
104
133
  if (nextCount > 0) {
@@ -107,19 +136,16 @@ class TaskManager {
107
136
  else {
108
137
  this.ownerCounts.delete(ownerKey);
109
138
  }
110
- if (this.activeTaskCount > 0)
111
- this.activeTaskCount--;
112
139
  }
113
140
  reserveTaskCapacity(ownerKey) {
114
141
  const { maxPerOwner, maxTotal } = config.tasks;
115
- if (this.activeTaskCount >= maxTotal) {
142
+ if (this.tasks.size >= maxTotal) {
116
143
  throw new McpError(ErrorCode.InvalidRequest, `Task capacity reached (${maxTotal} total tasks)`);
117
144
  }
118
145
  if ((this.ownerCounts.get(ownerKey) ?? 0) >= maxPerOwner) {
119
146
  throw new McpError(ErrorCode.InvalidRequest, `Task capacity reached for owner (${maxPerOwner} tasks)`);
120
147
  }
121
148
  this.ownerCounts.set(ownerKey, (this.ownerCounts.get(ownerKey) ?? 0) + 1);
122
- this.activeTaskCount += 1;
123
149
  }
124
150
  createTask(options, statusMessage = 'Task started', ownerKey = DEFAULT_OWNER_KEY) {
125
151
  this.removeExpiredTasks();
@@ -136,10 +162,14 @@ class TaskManager {
136
162
  ttl: normalizeTaskTtl(options?.ttl),
137
163
  pollInterval: DEFAULT_POLL_INTERVAL_MS,
138
164
  _createdAtMs: now.getTime(),
139
- _capacityCounted: true,
140
165
  };
141
166
  this.tasks.set(task.taskId, task);
142
167
  this.ensureCleanupLoop();
168
+ logInfo('Task created', {
169
+ taskId: task.taskId,
170
+ ownerKey,
171
+ ttl: task.ttl,
172
+ }, 'tasks');
143
173
  return task;
144
174
  }
145
175
  lookupActiveTask(taskId, ownerKey) {
@@ -160,25 +190,24 @@ class TaskManager {
160
190
  updateTask(taskId, updates) {
161
191
  const task = this.tasks.get(taskId);
162
192
  if (!task) {
163
- logWarn('updateTask called for unknown task', { taskId });
193
+ logWarn('updateTask called for unknown task', { taskId }, 'tasks');
164
194
  return;
165
195
  }
166
196
  if (isTerminalStatus(task.status)) {
167
197
  logWarn('updateTask called for terminal task', {
168
198
  taskId,
169
199
  currentStatus: task.status,
170
- });
200
+ }, 'tasks');
171
201
  return;
172
202
  }
173
203
  const nextStatus = resolveNextTaskStatus(task, updates);
204
+ const previousStatus = task.status;
174
205
  this.applyTaskUpdate(task, {
175
206
  ...updates,
176
207
  ...(updates.status === undefined ? {} : { status: nextStatus }),
177
208
  });
209
+ logTaskStatusTransition(task, previousStatus, task.status);
178
210
  this.waiters.notify(task);
179
- if (isTerminalStatus(nextStatus)) {
180
- this.releaseTaskCapacity(task);
181
- }
182
211
  }
183
212
  cancelTask(taskId, ownerKey) {
184
213
  const task = this.lookupActiveTask(taskId, ownerKey);
@@ -188,6 +217,10 @@ class TaskManager {
188
217
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task: already in terminal status '${task.status}'`);
189
218
  }
190
219
  this.cancelActiveTask(task, 'The task was cancelled by request.');
220
+ logInfo('Task cancelled by request', {
221
+ taskId: task.taskId,
222
+ ownerKey: task.ownerKey,
223
+ }, 'tasks');
191
224
  return task;
192
225
  }
193
226
  cancelTasksByOwner(ownerKey, statusMessage = 'The task was cancelled because its owner is no longer active.') {
@@ -200,6 +233,12 @@ class TaskManager {
200
233
  cancelled.push(task);
201
234
  }
202
235
  }
236
+ if (cancelled.length > 0) {
237
+ logInfo('Tasks cancelled for owner', {
238
+ ownerKey,
239
+ count: cancelled.length,
240
+ }, 'tasks');
241
+ }
203
242
  return cancelled;
204
243
  }
205
244
  collectPage(ownerKey, anchorTaskId, pageSize) {
@@ -21,6 +21,10 @@ export interface ToolExecutionContext {
21
21
  requestMeta?: ToolCallRequestMeta;
22
22
  }
23
23
  export type ToolCallContext = ToolExecutionContext;
24
+ interface AuthIdentity {
25
+ clientId?: string;
26
+ token?: string;
27
+ }
24
28
  /** Strip keys whose value is `undefined`, returning an object with only the
25
29
  * present keys. Return type correctly omits the `undefined` union so the result
26
30
  * is compatible with `exactOptionalPropertyTypes`. */
@@ -29,6 +33,7 @@ type Compacted<T extends object> = {
29
33
  };
30
34
  export declare function compact<T extends object>(obj: T): Compacted<T>;
31
35
  export declare function parseHandlerExtra(extra: unknown): HandlerExtra | undefined;
36
+ export declare function buildAuthenticatedOwnerKey(authInfo?: AuthIdentity): string | undefined;
32
37
  export declare function resolveTaskOwnerKey(extra?: HandlerExtra): string;
33
38
  export declare function resolveToolCallContext(extra?: HandlerExtra, requestMeta?: ToolCallRequestMeta): ToolCallContext;
34
39
  export declare function buildToolHandlerExtra(context: ToolExecutionContext, requestMeta?: ToolCallRequestMeta): ToolHandlerExtra;
@@ -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;AAIvE,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAM5B,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;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,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;IACzE,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAEnD;;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,CA0B1E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAMhE;AAuDD,wBAAgB,sBAAsB,CACpC,KAAK,CAAC,EAAE,YAAY,EACpB,WAAW,CAAC,EAAE,mBAAmB,GAChC,eAAe,CAEjB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,WAAW,CAAC,EAAE,mBAAmB,GAChC,gBAAgB,CAOlB;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,EAC5E,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAC7D,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAmBvD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAWD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAe7E"}
1
+ {"version":3,"file":"owner.d.ts","sourceRoot":"","sources":["../../src/tasks/owner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAQvE,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAM5B,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;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,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;IACzE,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAEnD,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;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,CA0B1E;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,CAAC,EAAE,YAAY,GACtB,MAAM,GAAG,SAAS,CAUpB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAWhE;AAuDD,wBAAgB,sBAAsB,CACpC,KAAK,CAAC,EAAE,YAAY,EACpB,WAAW,CAAC,EAAE,mBAAmB,GAChC,eAAe,CAEjB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,WAAW,CAAC,EAAE,mBAAmB,GAChC,gBAAgB,CAOlB;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,EAC5E,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAC7D,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAmBvD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAWD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAe7E"}
@@ -1,6 +1,6 @@
1
1
  import { hash, randomUUID } from 'node:crypto';
2
2
  import { z } from 'zod';
3
- import { getRequestId, runWithRequestContext } from '../lib/core.js';
3
+ import { getRequestId, resolveMcpSessionOwnerKey, runWithRequestContext, } from '../lib/core.js';
4
4
  import { isObject } from '../lib/utils.js';
5
5
  import { sanitizeToolCallMeta, } from './call-contract.js';
6
6
  export function compact(obj) {
@@ -54,13 +54,21 @@ export function parseHandlerExtra(extra) {
54
54
  }
55
55
  return parsed;
56
56
  }
57
+ export function buildAuthenticatedOwnerKey(authInfo) {
58
+ const authClientId = typeof authInfo?.clientId === 'string' ? authInfo.clientId : '';
59
+ const authToken = typeof authInfo?.token === 'string' ? authInfo.token : '';
60
+ if (authClientId || authToken) {
61
+ return `auth:${hash('sha256', `${authClientId}:${authToken}`, 'hex')}`;
62
+ }
63
+ return undefined;
64
+ }
57
65
  export function resolveTaskOwnerKey(extra) {
58
- if (extra?.sessionId)
59
- return `session:${extra.sessionId}`;
60
- if (extra?.authInfo?.clientId)
61
- return `client:${extra.authInfo.clientId}`;
62
- if (extra?.authInfo?.token)
63
- return `token:${hash('sha256', extra.authInfo.token, 'hex')}`;
66
+ const authenticatedOwnerKey = buildAuthenticatedOwnerKey(extra?.authInfo);
67
+ if (authenticatedOwnerKey)
68
+ return authenticatedOwnerKey;
69
+ if (extra?.sessionId) {
70
+ return (resolveMcpSessionOwnerKey(extra.sessionId) ?? `session:${extra.sessionId}`);
71
+ }
64
72
  return 'default';
65
73
  }
66
74
  function resolveRequestIdFromExtra(extra) {
@@ -1,18 +1,20 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
2
  import type { ServerResult } from '@modelcontextprotocol/sdk/types.js';
2
3
  import type { ToolHandlerExtra } from '../lib/mcp-interop.js';
3
- export type TaskCapableToolSupport = 'optional' | 'forbidden';
4
+ export type TaskCapableToolSupport = 'required' | 'optional' | 'forbidden';
4
5
  export interface TaskCapableToolDescriptor<TArgs = unknown> {
5
6
  name: string;
6
7
  parseArguments: (args: unknown) => TArgs;
7
8
  execute: (args: TArgs, extra?: ToolHandlerExtra) => Promise<ServerResult>;
8
9
  getCompletionStatusMessage?: (result: ServerResult) => string | undefined;
9
10
  taskSupport?: TaskCapableToolSupport;
11
+ immediateResponse?: string;
10
12
  }
11
- export declare function registerTaskCapableTool<TArgs>(descriptor: TaskCapableToolDescriptor<TArgs>): void;
12
- export declare function unregisterTaskCapableTool(name: string): void;
13
- export declare function getTaskCapableTool(name: string): TaskCapableToolDescriptor | undefined;
14
- export declare function getTaskCapableToolSupport(name: string): TaskCapableToolSupport | undefined;
15
- export declare function hasTaskCapableTool(name: string): boolean;
16
- export declare function hasRegisteredTaskCapableTools(): boolean;
17
- export declare function setTaskCapableToolSupport(name: string, support: TaskCapableToolSupport): void;
13
+ export declare function registerTaskCapableTool<TArgs>(server: McpServer, descriptor: TaskCapableToolDescriptor<TArgs>): void;
14
+ export declare function unregisterTaskCapableTool(server: McpServer, name: string): void;
15
+ export declare function getTaskCapableTool(server: McpServer, name: string): TaskCapableToolDescriptor | undefined;
16
+ export declare function getTaskCapableToolSupport(server: McpServer, name: string): TaskCapableToolSupport | undefined;
17
+ export declare function hasTaskCapableTool(server: McpServer, name: string): boolean;
18
+ export declare function hasRegisteredTaskCapableTools(server: McpServer): boolean;
19
+ export declare function setTaskCapableToolSupport(server: McpServer, name: string, support: TaskCapableToolSupport): void;
18
20
  //# sourceMappingURL=registry.d.ts.map