@j0hanz/fetch-url-mcp 1.12.6 → 1.12.8

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 (146) hide show
  1. package/dist/http/auth.d.ts +2 -2
  2. package/dist/http/auth.d.ts.map +1 -1
  3. package/dist/http/auth.js +15 -16
  4. package/dist/http/index.d.ts +6 -0
  5. package/dist/http/index.d.ts.map +1 -0
  6. package/dist/http/index.js +5 -0
  7. package/dist/http/native.d.ts +73 -0
  8. package/dist/http/native.d.ts.map +1 -1
  9. package/dist/http/native.js +585 -62
  10. package/dist/http/rate-limit.d.ts +1 -1
  11. package/dist/http/rate-limit.d.ts.map +1 -1
  12. package/dist/http/rate-limit.js +5 -6
  13. package/dist/index.d.ts +17 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +69 -8
  16. package/dist/lib/config.js +2 -2
  17. package/dist/lib/core.d.ts +56 -4
  18. package/dist/lib/core.d.ts.map +1 -1
  19. package/dist/lib/core.js +162 -11
  20. package/dist/lib/error/classes.d.ts +19 -0
  21. package/dist/lib/error/classes.d.ts.map +1 -0
  22. package/dist/lib/error/classes.js +107 -0
  23. package/dist/lib/error/classify.d.ts +4 -0
  24. package/dist/lib/error/classify.d.ts.map +1 -0
  25. package/dist/lib/error/classify.js +154 -0
  26. package/dist/lib/error/codes.d.ts +23 -0
  27. package/dist/lib/error/codes.d.ts.map +1 -0
  28. package/dist/lib/error/codes.js +22 -0
  29. package/dist/lib/error/index.d.ts +6 -0
  30. package/dist/lib/error/index.d.ts.map +1 -0
  31. package/dist/lib/error/index.js +5 -0
  32. package/dist/lib/{error-messages.d.ts → error/messages.d.ts} +2 -2
  33. package/dist/lib/error/messages.d.ts.map +1 -0
  34. package/dist/lib/{error-messages.js → error/messages.js} +13 -13
  35. package/dist/lib/{tool-errors.d.ts → error/payload.d.ts} +7 -13
  36. package/dist/lib/error/payload.d.ts.map +1 -0
  37. package/dist/lib/error/payload.js +108 -0
  38. package/dist/lib/mcp-interop.d.ts +1 -0
  39. package/dist/lib/mcp-interop.d.ts.map +1 -1
  40. package/dist/lib/mcp-interop.js +17 -9
  41. package/dist/lib/net/http.d.ts.map +1 -0
  42. package/dist/lib/{http.js → net/http.js} +11 -14
  43. package/dist/lib/net/index.d.ts +4 -0
  44. package/dist/lib/net/index.d.ts.map +1 -0
  45. package/dist/lib/net/index.js +3 -0
  46. package/dist/lib/{fetch-pipeline.d.ts → net/pipeline.d.ts} +3 -3
  47. package/dist/lib/net/pipeline.d.ts.map +1 -0
  48. package/dist/lib/{fetch-pipeline.js → net/pipeline.js} +7 -9
  49. package/dist/lib/{url.d.ts → net/url.d.ts} +2 -2
  50. package/dist/lib/net/url.d.ts.map +1 -0
  51. package/dist/lib/{url.js → net/url.js} +6 -8
  52. package/dist/lib/utils.d.ts +3 -18
  53. package/dist/lib/utils.d.ts.map +1 -1
  54. package/dist/lib/utils.js +33 -105
  55. package/dist/resources/index.d.ts.map +1 -1
  56. package/dist/resources/index.js +6 -3
  57. package/dist/schemas.d.ts +1 -1
  58. package/dist/server.d.ts.map +1 -1
  59. package/dist/server.js +12 -14
  60. package/dist/tasks/index.d.ts +2 -0
  61. package/dist/tasks/index.d.ts.map +1 -0
  62. package/dist/tasks/index.js +1 -0
  63. package/dist/tasks/manager.d.ts +123 -1
  64. package/dist/tasks/manager.d.ts.map +1 -1
  65. package/dist/tasks/manager.js +753 -18
  66. package/dist/tools/{fetch-url.d.ts → index.d.ts} +4 -5
  67. package/dist/tools/index.d.ts.map +1 -0
  68. package/dist/tools/{fetch-url.js → index.js} +14 -31
  69. package/dist/transform/index.d.ts +279 -0
  70. package/dist/transform/index.d.ts.map +1 -0
  71. package/dist/transform/index.js +5234 -0
  72. package/package.json +2 -2
  73. package/dist/cli.d.ts +0 -19
  74. package/dist/cli.d.ts.map +0 -1
  75. package/dist/cli.js +0 -65
  76. package/dist/http/health.d.ts +0 -8
  77. package/dist/http/health.d.ts.map +0 -1
  78. package/dist/http/health.js +0 -152
  79. package/dist/http/helpers.d.ts +0 -68
  80. package/dist/http/helpers.d.ts.map +0 -1
  81. package/dist/http/helpers.js +0 -404
  82. package/dist/lib/error-codes.d.ts +0 -11
  83. package/dist/lib/error-codes.d.ts.map +0 -1
  84. package/dist/lib/error-codes.js +0 -15
  85. package/dist/lib/error-messages.d.ts.map +0 -1
  86. package/dist/lib/fetch-pipeline.d.ts.map +0 -1
  87. package/dist/lib/http.d.ts.map +0 -1
  88. package/dist/lib/logger-names.d.ts +0 -14
  89. package/dist/lib/logger-names.d.ts.map +0 -1
  90. package/dist/lib/logger-names.js +0 -13
  91. package/dist/lib/session.d.ts +0 -44
  92. package/dist/lib/session.d.ts.map +0 -1
  93. package/dist/lib/session.js +0 -137
  94. package/dist/lib/tool-errors.d.ts.map +0 -1
  95. package/dist/lib/tool-errors.js +0 -252
  96. package/dist/lib/url.d.ts.map +0 -1
  97. package/dist/lib/zod.d.ts +0 -3
  98. package/dist/lib/zod.d.ts.map +0 -1
  99. package/dist/lib/zod.js +0 -27
  100. package/dist/tasks/call-contract.d.ts +0 -25
  101. package/dist/tasks/call-contract.d.ts.map +0 -1
  102. package/dist/tasks/call-contract.js +0 -59
  103. package/dist/tasks/execution.d.ts +0 -16
  104. package/dist/tasks/execution.d.ts.map +0 -1
  105. package/dist/tasks/execution.js +0 -241
  106. package/dist/tasks/handlers.d.ts +0 -11
  107. package/dist/tasks/handlers.d.ts.map +0 -1
  108. package/dist/tasks/handlers.js +0 -157
  109. package/dist/tasks/owner.d.ts +0 -43
  110. package/dist/tasks/owner.d.ts.map +0 -1
  111. package/dist/tasks/owner.js +0 -144
  112. package/dist/tasks/registry.d.ts +0 -20
  113. package/dist/tasks/registry.d.ts.map +0 -1
  114. package/dist/tasks/registry.js +0 -40
  115. package/dist/tasks/waiters.d.ts +0 -27
  116. package/dist/tasks/waiters.d.ts.map +0 -1
  117. package/dist/tasks/waiters.js +0 -114
  118. package/dist/tools/fetch-url.d.ts.map +0 -1
  119. package/dist/transform/dom-prep.d.ts +0 -16
  120. package/dist/transform/dom-prep.d.ts.map +0 -1
  121. package/dist/transform/dom-prep.js +0 -1287
  122. package/dist/transform/html-translators.d.ts +0 -5
  123. package/dist/transform/html-translators.d.ts.map +0 -1
  124. package/dist/transform/html-translators.js +0 -697
  125. package/dist/transform/markdown-cleanup.d.ts +0 -10
  126. package/dist/transform/markdown-cleanup.d.ts.map +0 -1
  127. package/dist/transform/markdown-cleanup.js +0 -542
  128. package/dist/transform/metadata.d.ts +0 -18
  129. package/dist/transform/metadata.d.ts.map +0 -1
  130. package/dist/transform/metadata.js +0 -462
  131. package/dist/transform/next-flight.d.ts +0 -2
  132. package/dist/transform/next-flight.d.ts.map +0 -1
  133. package/dist/transform/next-flight.js +0 -374
  134. package/dist/transform/shared.d.ts +0 -8
  135. package/dist/transform/shared.d.ts.map +0 -1
  136. package/dist/transform/shared.js +0 -137
  137. package/dist/transform/transform.d.ts +0 -38
  138. package/dist/transform/transform.d.ts.map +0 -1
  139. package/dist/transform/transform.js +0 -1041
  140. package/dist/transform/types.d.ts +0 -124
  141. package/dist/transform/types.d.ts.map +0 -1
  142. package/dist/transform/types.js +0 -5
  143. package/dist/transform/worker-pool.d.ts +0 -76
  144. package/dist/transform/worker-pool.d.ts.map +0 -1
  145. package/dist/transform/worker-pool.js +0 -725
  146. /package/dist/lib/{http.d.ts → net/http.d.ts} +0 -0
@@ -1,252 +0,0 @@
1
- import { McpError, } from '@modelcontextprotocol/sdk/types.js';
2
- import { z } from 'zod';
3
- import { logError, logWarn } from './core.js';
4
- import { ABORTED, FETCH_ERROR, QUEUE_FULL, VALIDATION_ERROR, } from './error-codes.js';
5
- import { FetchError, isAbortError, isObject, isSystemError } from './utils.js';
6
- const toolErrorPayloadSchema = z.strictObject({
7
- error: z.string(),
8
- url: z.string(),
9
- category: z.string().optional(),
10
- code: z.union([z.string(), z.number()]).optional(),
11
- statusCode: z.number().int().optional(),
12
- upstreamMessage: z.string().optional(),
13
- details: z.record(z.string(), z.unknown()).optional(),
14
- data: z.unknown().optional(),
15
- });
16
- const PUBLIC_ERROR_REASONS = new Set(['aborted', QUEUE_FULL, 'timeout']);
17
- function sanitizeToolErrorDetails(details) {
18
- const sanitized = {};
19
- const { retryAfter, timeout, reason } = details;
20
- if (typeof retryAfter === 'number' ||
21
- typeof retryAfter === 'string' ||
22
- retryAfter === null) {
23
- sanitized['retryAfter'] = retryAfter;
24
- }
25
- if (typeof timeout === 'number' && Number.isFinite(timeout) && timeout >= 0) {
26
- sanitized['timeout'] = timeout;
27
- }
28
- if (typeof reason === 'string' && PUBLIC_ERROR_REASONS.has(reason)) {
29
- sanitized['reason'] = reason;
30
- }
31
- return Object.keys(sanitized).length > 0 ? sanitized : undefined;
32
- }
33
- export function createToolErrorResponse(message, url, extra) {
34
- const errorContent = createToolErrorPayload(message, url, extra);
35
- return {
36
- content: [{ type: 'text', text: JSON.stringify(errorContent) }],
37
- isError: true,
38
- };
39
- }
40
- export function createToolErrorPayload(message, url, extra) {
41
- const payload = {
42
- error: message,
43
- url,
44
- };
45
- if (extra?.category !== undefined)
46
- payload.category = extra.category;
47
- if (extra?.code !== undefined)
48
- payload.code = extra.code;
49
- if (extra?.statusCode !== undefined)
50
- payload.statusCode = extra.statusCode;
51
- if (extra?.upstreamMessage !== undefined) {
52
- payload.upstreamMessage = extra.upstreamMessage;
53
- }
54
- if (extra?.details)
55
- payload.details = extra.details;
56
- if (extra?.data !== undefined)
57
- payload.data = extra.data;
58
- return payload;
59
- }
60
- function normalizeToolErrorPayload(value) {
61
- return createToolErrorPayload(value.error, value.url, {
62
- ...(value.category !== undefined ? { category: value.category } : {}),
63
- ...(value.code !== undefined ? { code: value.code } : {}),
64
- ...(value.statusCode !== undefined ? { statusCode: value.statusCode } : {}),
65
- ...(value.upstreamMessage !== undefined
66
- ? { upstreamMessage: value.upstreamMessage }
67
- : {}),
68
- ...(value.details ? { details: value.details } : {}),
69
- ...(value.data !== undefined ? { data: value.data } : {}),
70
- });
71
- }
72
- export function tryReadToolErrorPayload(value) {
73
- if (!isObject(value))
74
- return undefined;
75
- const structuredContent = toolErrorPayloadSchema.safeParse(value['structuredContent']);
76
- if (structuredContent.success) {
77
- return normalizeToolErrorPayload(structuredContent.data);
78
- }
79
- const { content } = value;
80
- if (!Array.isArray(content) || content.length === 0)
81
- return undefined;
82
- const firstBlock = content[0];
83
- if (!isObject(firstBlock))
84
- return undefined;
85
- if (firstBlock['type'] !== 'text')
86
- return undefined;
87
- if (typeof firstBlock['text'] !== 'string')
88
- return undefined;
89
- try {
90
- const payload = toolErrorPayloadSchema.safeParse(JSON.parse(firstBlock['text']));
91
- return payload.success
92
- ? normalizeToolErrorPayload(payload.data)
93
- : undefined;
94
- }
95
- catch {
96
- return undefined;
97
- }
98
- }
99
- export function tryReadToolErrorMessage(value) {
100
- return tryReadToolErrorPayload(value)?.error;
101
- }
102
- function renderToolErrorResponse(presentation) {
103
- return createToolErrorResponse(presentation.message, presentation.url, {
104
- ...(presentation.category !== undefined
105
- ? { category: presentation.category }
106
- : {}),
107
- ...(presentation.code !== undefined ? { code: presentation.code } : {}),
108
- ...(presentation.statusCode !== undefined
109
- ? { statusCode: presentation.statusCode }
110
- : {}),
111
- ...(presentation.upstreamMessage !== undefined
112
- ? { upstreamMessage: presentation.upstreamMessage }
113
- : {}),
114
- ...(presentation.details ? { details: presentation.details } : {}),
115
- ...(presentation.data !== undefined ? { data: presentation.data } : {}),
116
- });
117
- }
118
- function isValidationError(error) {
119
- return (error instanceof Error &&
120
- isSystemError(error) &&
121
- error.code === VALIDATION_ERROR);
122
- }
123
- function extractHttpStatusText(message, statusCode) {
124
- const prefix = `HTTP ${statusCode}:`;
125
- if (!message.startsWith(prefix))
126
- return '';
127
- return message.slice(prefix.length).trim();
128
- }
129
- function buildUpstreamHttpMessage(error) {
130
- const { statusCode } = error;
131
- const statusText = extractHttpStatusText(error.message, statusCode);
132
- const suffix = statusText ? ` ${statusText}` : '';
133
- if (statusCode === 404) {
134
- return `The target page returned 404${suffix}.`;
135
- }
136
- return `The target server returned ${statusCode}${suffix}.`;
137
- }
138
- function mapFetchToolError(error, fallbackUrl) {
139
- const { code: detailsCode, reason } = error.details;
140
- let { code } = error;
141
- if (typeof detailsCode === 'string') {
142
- code = detailsCode;
143
- }
144
- else if (reason === QUEUE_FULL) {
145
- code = QUEUE_FULL;
146
- }
147
- const url = error.url || fallbackUrl;
148
- const details = sanitizeToolErrorDetails(error.details);
149
- if (reason === 'timeout') {
150
- return {
151
- message: 'The request to the target timed out.',
152
- url,
153
- category: 'upstream_timeout',
154
- code,
155
- statusCode: error.statusCode,
156
- upstreamMessage: error.message,
157
- ...(details ? { details } : {}),
158
- };
159
- }
160
- if (reason === 'aborted') {
161
- return {
162
- message: 'The request to the target was cancelled.',
163
- url,
164
- category: 'upstream_aborted',
165
- code,
166
- statusCode: error.statusCode,
167
- upstreamMessage: error.message,
168
- ...(details ? { details } : {}),
169
- };
170
- }
171
- if (reason === QUEUE_FULL) {
172
- return {
173
- message: error.message,
174
- url,
175
- category: 'queue_full',
176
- code,
177
- statusCode: error.statusCode,
178
- ...(details ? { details } : {}),
179
- };
180
- }
181
- const isRealHttpError = typeof error.details['httpStatus'] === 'number';
182
- if (isRealHttpError && error.statusCode >= 400) {
183
- return {
184
- message: buildUpstreamHttpMessage(error),
185
- url,
186
- category: error.statusCode === 429
187
- ? 'upstream_rate_limited'
188
- : 'upstream_http_error',
189
- code,
190
- statusCode: error.statusCode,
191
- upstreamMessage: error.message,
192
- ...(details ? { details } : {}),
193
- };
194
- }
195
- return {
196
- message: error.message,
197
- url,
198
- category: 'fetch_error',
199
- code,
200
- statusCode: error.statusCode,
201
- ...(details ? { details } : {}),
202
- };
203
- }
204
- function mapGenericToolError(error, url, fallbackMessage) {
205
- if (isValidationError(error)) {
206
- return {
207
- message: error.message,
208
- url,
209
- category: 'validation_error',
210
- code: VALIDATION_ERROR,
211
- };
212
- }
213
- const isAborted = isAbortError(error);
214
- return {
215
- message: error instanceof Error
216
- ? error.message
217
- : `${fallbackMessage}: unknown error`,
218
- url,
219
- category: isAborted ? 'upstream_aborted' : 'fetch_error',
220
- code: isAborted ? ABORTED : FETCH_ERROR,
221
- };
222
- }
223
- function resolveToolErrorPresentation(error, url, fallbackMessage) {
224
- if (error instanceof FetchError) {
225
- return mapFetchToolError(error, url);
226
- }
227
- return mapGenericToolError(error, url, fallbackMessage);
228
- }
229
- export function handleToolError(error, url, fallbackMessage = 'Operation failed') {
230
- return renderToolErrorResponse(resolveToolErrorPresentation(error, url, fallbackMessage));
231
- }
232
- export function classifyAndLogToolError(error, meta, loggerName, toolName, fallbackMessage) {
233
- if (error instanceof McpError) {
234
- logError(`${toolName} tool failed`, { url: meta.url, durationMs: meta.durationMs, error }, loggerName);
235
- throw error;
236
- }
237
- if (error instanceof FetchError || isAbortError(error)) {
238
- logWarn(`${toolName} request failed`, {
239
- url: meta.url,
240
- error: error instanceof Error ? error.message : String(error),
241
- durationMs: meta.durationMs,
242
- }, loggerName);
243
- }
244
- else {
245
- logError(`${toolName} request failed unexpectedly`, {
246
- url: meta.url,
247
- error: error instanceof Error ? error.message : String(error),
248
- durationMs: meta.durationMs,
249
- }, loggerName);
250
- }
251
- return handleToolError(error, meta.url, fallbackMessage);
252
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/lib/url.ts"],"names":[],"mappings":"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"}
package/dist/lib/zod.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import { z } from 'zod';
2
- export declare function formatZodError(error: z.ZodError): string;
3
- //# sourceMappingURL=zod.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"zod.d.ts","sourceRoot":"","sources":["../../src/lib/zod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAYxD"}
package/dist/lib/zod.js DELETED
@@ -1,27 +0,0 @@
1
- import { z } from 'zod';
2
- function formatPathSegment(segment) {
3
- if (typeof segment === 'number')
4
- return `[${segment}]`;
5
- if (typeof segment === 'string')
6
- return segment;
7
- return segment.description ?? '<symbol>';
8
- }
9
- function formatIssuePath(path) {
10
- return path.reduce((acc, segment) => {
11
- const isNum = typeof segment === 'number';
12
- const formatted = formatPathSegment(segment);
13
- return acc ? `${acc}${isNum ? '' : '.'}${formatted}` : formatted;
14
- }, '');
15
- }
16
- export function formatZodError(error) {
17
- const parts = error.issues.map((issue) => {
18
- const path = formatIssuePath(issue.path);
19
- return path ? `${path}: ${issue.message}` : issue.message;
20
- });
21
- const uniqueParts = [
22
- ...new Set(parts.filter((val) => val.trim().length > 0)),
23
- ];
24
- if (uniqueParts.length > 0)
25
- return uniqueParts.join('; ');
26
- return z.prettifyError(error).replace(/\s+/g, ' ').trim() || 'Invalid input';
27
- }
@@ -1,25 +0,0 @@
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
@@ -1 +0,0 @@
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;AAiBxB,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,CAKzB;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"}
@@ -1,59 +0,0 @@
1
- import { ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
- import { z } from 'zod';
3
- import { createMcpError } from '../lib/mcp-interop.js';
4
- import { formatZodError } from '../lib/zod.js';
5
- const MIN_TASK_TTL_MS = 1_000;
6
- const MAX_TASK_TTL_MS = 86_400_000;
7
- const relatedTaskMetaSchema = z.strictObject({
8
- taskId: z.string(),
9
- });
10
- const toolCallMetaSchema = z.looseObject({
11
- progressToken: z.union([z.string(), z.number()]).optional(),
12
- 'io.modelcontextprotocol/related-task': relatedTaskMetaSchema.optional(),
13
- });
14
- export const extendedCallToolRequestSchema = z.looseObject({
15
- method: z.literal('tools/call'),
16
- params: z.strictObject({
17
- name: z.string().min(1, 'Tool name required'),
18
- arguments: z.record(z.string(), z.unknown()).optional(),
19
- task: z
20
- .strictObject({
21
- ttl: z
22
- .number()
23
- .int()
24
- .min(MIN_TASK_TTL_MS, `Minimum ${MIN_TASK_TTL_MS}ms`)
25
- .max(MAX_TASK_TTL_MS, `Maximum ${MAX_TASK_TTL_MS}ms`)
26
- .optional(),
27
- })
28
- .optional(),
29
- _meta: toolCallMetaSchema.optional(),
30
- }),
31
- });
32
- export function parseExtendedCallToolRequest(request) {
33
- const parsed = extendedCallToolRequestSchema.safeParse(request);
34
- if (parsed.success)
35
- return parsed.data;
36
- throw createMcpError(ErrorCode.InvalidParams, formatZodError(parsed.error));
37
- }
38
- export function sanitizeToolCallMeta(meta) {
39
- if (!meta)
40
- return undefined;
41
- const sanitized = { ...meta };
42
- delete sanitized['io.modelcontextprotocol/related-task'];
43
- return Object.keys(sanitized).length > 0 ? sanitized : undefined;
44
- }
45
- export function buildRelatedTaskMeta(taskId, meta) {
46
- return {
47
- ...(sanitizeToolCallMeta(meta) ?? {}),
48
- 'io.modelcontextprotocol/related-task': { taskId },
49
- };
50
- }
51
- export function withRelatedTaskMeta(result, taskId) {
52
- return {
53
- ...result,
54
- _meta: {
55
- ...result._meta,
56
- 'io.modelcontextprotocol/related-task': { taskId },
57
- },
58
- };
59
- }
@@ -1,16 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { type ServerResult } from '@modelcontextprotocol/sdk/types.js';
3
- import { type ExtendedCallToolRequest } from './call-contract.js';
4
- import { type CreateTaskResult, type TaskState } from './manager.js';
5
- import { type ToolCallContext } from './owner.js';
6
- export declare function abortTaskExecution(taskId: string): void;
7
- export declare function cancelTasksForOwner(ownerKey: string, statusMessage?: string): number;
8
- export declare function abortAllTaskExecutions(): void;
9
- type TaskSummary = CreateTaskResult['task'];
10
- type TaskLifecycleProjection = Pick<TaskState, 'taskId' | 'status' | 'statusMessage' | 'progress' | 'total' | 'createdAt' | 'lastUpdatedAt' | 'ttl' | 'pollInterval'>;
11
- export declare function toTaskSummary(task: TaskLifecycleProjection): TaskSummary;
12
- export declare function emitTaskStatusNotification(server: McpServer, task: TaskState): void;
13
- export declare function throwTaskNotFound(): never;
14
- export declare function handleToolCallRequest(server: McpServer, request: ExtendedCallToolRequest, context: ToolCallContext): Promise<ServerResult>;
15
- export {};
16
- //# sourceMappingURL=execution.d.ts.map
@@ -1 +0,0 @@
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;AAiB5C,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,EACrB,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;AAgKD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,CAAC,CAwFvB"}
@@ -1,241 +0,0 @@
1
- import { ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
2
- import { config } from '../lib/core.js';
3
- import { logDebug, logError, logInfo, logWarn, runWithRequestContext, } from '../lib/core.js';
4
- import { LOG_TASKS } from '../lib/logger-names.js';
5
- import { createMcpError } from '../lib/mcp-interop.js';
6
- import {} from '../lib/mcp-interop.js';
7
- import { tryReadToolErrorMessage } from '../lib/tool-errors.js';
8
- import { getErrorMessage } from '../lib/utils.js';
9
- import { isObject } from '../lib/utils.js';
10
- import { buildRelatedTaskMeta, } from './call-contract.js';
11
- import { taskManager, } from './manager.js';
12
- import { buildToolHandlerExtra, compact, } from './owner.js';
13
- import { getTaskCapableTool, getTaskCapableToolSupport, } from './registry.js';
14
- /* -------------------------------------------------------------------------------------------------
15
- * Abort-controller management for in-flight task executions
16
- * ------------------------------------------------------------------------------------------------- */
17
- // Intentionally process-global (not session-scoped): abortAllTaskExecutions() is called
18
- // during SIGTERM/SIGINT shutdown to cancel every in-flight task across all sessions.
19
- const taskAbortControllers = new Map();
20
- function attachAbortController(taskId) {
21
- taskAbortControllers.get(taskId)?.abort();
22
- if (taskAbortControllers.size >= config.tasks.maxTotal) {
23
- logWarn('Abort controller map reached task capacity — possible leak', {
24
- size: taskAbortControllers.size,
25
- maxTotal: config.tasks.maxTotal,
26
- }, LOG_TASKS);
27
- }
28
- const controller = new AbortController();
29
- taskAbortControllers.set(taskId, controller);
30
- return controller;
31
- }
32
- export function abortTaskExecution(taskId) {
33
- taskAbortControllers.get(taskId)?.abort();
34
- taskAbortControllers.delete(taskId);
35
- }
36
- export function cancelTasksForOwner(ownerKey, statusMessage = 'The task was cancelled because its owner session ended.') {
37
- if (!ownerKey)
38
- return 0;
39
- const cancelled = taskManager.cancelTasksByOwner(ownerKey, statusMessage);
40
- for (const task of cancelled) {
41
- abortTaskExecution(task.taskId);
42
- }
43
- return cancelled.length;
44
- }
45
- export function abortAllTaskExecutions() {
46
- for (const taskId of taskAbortControllers.keys())
47
- abortTaskExecution(taskId);
48
- }
49
- export function toTaskSummary(task) {
50
- return {
51
- taskId: task.taskId,
52
- status: task.status,
53
- ...(task.statusMessage ? { statusMessage: task.statusMessage } : {}),
54
- ...(task.progress !== undefined ? { progress: task.progress } : {}),
55
- ...(task.total !== undefined ? { total: task.total } : {}),
56
- createdAt: task.createdAt,
57
- lastUpdatedAt: task.lastUpdatedAt,
58
- ttl: task.ttl,
59
- pollInterval: task.pollInterval,
60
- };
61
- }
62
- export function emitTaskStatusNotification(server, task) {
63
- if (!config.tasks.emitStatusNotifications || !server.isConnected())
64
- return;
65
- void server.server
66
- .notification({
67
- method: 'notifications/tasks/status',
68
- params: { ...toTaskSummary(task) },
69
- })
70
- .catch((error) => {
71
- logError('Failed to send task status notification', {
72
- taskId: task.taskId,
73
- status: task.status,
74
- error: getErrorMessage(error),
75
- }, LOG_TASKS);
76
- });
77
- }
78
- export function throwTaskNotFound() {
79
- throw createMcpError(ErrorCode.InvalidParams, 'Task not found');
80
- }
81
- /* -------------------------------------------------------------------------------------------------
82
- * Execution pipeline
83
- * ------------------------------------------------------------------------------------------------- */
84
- function updateTaskAndEmitStatus(server, taskId, update) {
85
- taskManager.updateTask(taskId, update);
86
- const task = taskManager.getTask(taskId);
87
- if (task)
88
- emitTaskStatusNotification(server, task);
89
- }
90
- function buildTaskFailureState(error) {
91
- const mcpErrorMessage = error instanceof McpError
92
- ? (/^(?:MCP )?[Ee]rror -?\d+:\s*(.*)$/s.exec(error.message)?.[1] ??
93
- error.message)
94
- : undefined;
95
- const statusMessage = mcpErrorMessage ?? getErrorMessage(error);
96
- if (error instanceof McpError) {
97
- return {
98
- status: 'failed',
99
- statusMessage,
100
- error: {
101
- code: error.code,
102
- ...(error.data !== undefined ? { data: error.data } : {}),
103
- message: statusMessage,
104
- },
105
- };
106
- }
107
- return {
108
- status: 'failed',
109
- statusMessage,
110
- error: {
111
- code: ErrorCode.InternalError,
112
- message: statusMessage,
113
- },
114
- };
115
- }
116
- function buildTaskCompletionUpdate(result, tool) {
117
- const isError = isObject(result) && 'isError' in result && result.isError === true;
118
- return {
119
- status: isError ? 'failed' : 'completed',
120
- statusMessage: isError
121
- ? (tryReadToolErrorMessage(result) ?? 'Execution failed')
122
- : (tool.getCompletionStatusMessage?.(result) ??
123
- 'Task completed successfully.'),
124
- result,
125
- };
126
- }
127
- async function runTaskToolExecution(params) {
128
- const { server, taskId, args, tool, meta, sessionId, sendNotification } = params;
129
- return runWithRequestContext({
130
- requestId: taskId,
131
- operationId: taskId,
132
- ...(sessionId ? { sessionId } : {}),
133
- }, async () => {
134
- const controller = attachAbortController(taskId);
135
- const progressState = { closed: false };
136
- try {
137
- logInfo('Task execution started', { taskId, tool: tool.name }, LOG_TASKS);
138
- const relatedMeta = buildRelatedTaskMeta(taskId, meta);
139
- const result = await tool.execute(args, {
140
- signal: controller.signal,
141
- requestId: taskId,
142
- _meta: relatedMeta,
143
- progressState,
144
- canReportProgress: () => taskManager.getTask(taskId)?.status === 'working',
145
- ...compact({ sendNotification }),
146
- onProgress: (progress, message, total) => {
147
- const current = taskManager.getTask(taskId);
148
- if (current?.status === 'working' &&
149
- (current.statusMessage !== message ||
150
- current.progress !== progress ||
151
- (total !== undefined && current.total !== total))) {
152
- updateTaskAndEmitStatus(server, taskId, {
153
- statusMessage: message,
154
- progress,
155
- ...(total !== undefined ? { total } : {}),
156
- });
157
- }
158
- },
159
- });
160
- const completionUpdate = buildTaskCompletionUpdate(result, tool);
161
- updateTaskAndEmitStatus(server, taskId, completionUpdate);
162
- if (completionUpdate.status === 'completed') {
163
- logInfo('Task execution completed', { taskId, tool: tool.name }, LOG_TASKS);
164
- }
165
- else {
166
- logWarn('Task execution completed with tool error result', { taskId, tool: tool.name }, LOG_TASKS);
167
- }
168
- }
169
- catch (error) {
170
- logError('Task execution failed', {
171
- taskId,
172
- tool: tool.name,
173
- error: getErrorMessage(error),
174
- }, LOG_TASKS);
175
- updateTaskAndEmitStatus(server, taskId, buildTaskFailureState(error));
176
- }
177
- finally {
178
- progressState.closed = true;
179
- taskAbortControllers.delete(taskId);
180
- }
181
- });
182
- }
183
- export async function handleToolCallRequest(server, request, context) {
184
- const { params } = request;
185
- // Validate the tool name first so an unknown tool always produces MethodNotFound
186
- const tool = getTaskCapableTool(server, params.name);
187
- if (!tool) {
188
- throw createMcpError(ErrorCode.MethodNotFound, `Unknown tool: ${params.name}`);
189
- }
190
- if (params.task) {
191
- if (getTaskCapableToolSupport(server, params.name) === 'forbidden') {
192
- throw createMcpError(ErrorCode.MethodNotFound, `Task mode is not supported for tool: ${params.name}`);
193
- }
194
- const args = tool.parseArguments(params.arguments);
195
- const task = taskManager.createTask(params.task.ttl !== undefined ? { ttl: params.task.ttl } : undefined, 'Task started', context.ownerKey);
196
- logInfo('Task execution queued', {
197
- taskId: task.taskId,
198
- tool: params.name,
199
- ...(params.task.ttl !== undefined ? { ttl: params.task.ttl } : {}),
200
- }, LOG_TASKS);
201
- void runTaskToolExecution({
202
- server,
203
- taskId: task.taskId,
204
- args,
205
- tool,
206
- ...compact({
207
- meta: params._meta,
208
- sessionId: context.sessionId,
209
- sendNotification: context.sendNotification,
210
- }),
211
- });
212
- return {
213
- task: toTaskSummary(task),
214
- ...(tool.immediateResponse
215
- ? {
216
- _meta: {
217
- 'io.modelcontextprotocol/model-immediate-response': tool.immediateResponse,
218
- },
219
- }
220
- : {}),
221
- };
222
- }
223
- if (getTaskCapableToolSupport(server, params.name) === 'required') {
224
- throw createMcpError(ErrorCode.MethodNotFound, `Task mode is required for tool: ${params.name}`);
225
- }
226
- const args = tool.parseArguments(params.arguments);
227
- const progressState = { closed: false };
228
- logDebug('Executing task-capable tool inline', {
229
- tool: params.name,
230
- hasProgressToken: params._meta?.progressToken !== undefined,
231
- }, LOG_TASKS);
232
- try {
233
- return await tool.execute(args, {
234
- ...buildToolHandlerExtra(context, params._meta),
235
- progressState,
236
- });
237
- }
238
- finally {
239
- progressState.closed = true;
240
- }
241
- }
@@ -1,11 +0,0 @@
1
- import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export { cancelTasksForOwner, abortAllTaskExecutions } from './execution.js';
3
- interface TaskHandlerRegistrationOptions {
4
- requireInterception?: boolean;
5
- }
6
- interface TaskHandlerRegistrationResult {
7
- interceptedToolsCall: boolean;
8
- taskCapableToolsRegistered: boolean;
9
- }
10
- export declare function registerTaskHandlers(server: McpServer, options?: TaskHandlerRegistrationOptions): TaskHandlerRegistrationResult;
11
- //# sourceMappingURL=handlers.d.ts.map
@@ -1 +0,0 @@
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"}