@j0hanz/fetch-url-mcp 1.12.13 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +815 -809
- package/dist/assets/logo.svg +53 -53
- package/dist/http/auth.d.ts +7 -3
- package/dist/http/auth.d.ts.map +1 -1
- package/dist/http/auth.js +65 -37
- package/dist/http/helpers.d.ts +21 -0
- package/dist/http/helpers.d.ts.map +1 -0
- package/dist/http/helpers.js +31 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -0
- package/dist/http/native.d.ts +6 -24
- package/dist/http/native.d.ts.map +1 -1
- package/dist/http/native.js +141 -86
- package/dist/http/rate-limit.d.ts +1 -1
- package/dist/http/rate-limit.d.ts.map +1 -1
- package/dist/http/rate-limit.js +3 -9
- package/dist/index.js +0 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +1 -0
- package/dist/lib/core.d.ts +4 -3
- package/dist/lib/core.d.ts.map +1 -1
- package/dist/lib/core.js +4 -1
- package/dist/lib/error/classify.d.ts.map +1 -1
- package/dist/lib/error/classify.js +27 -6
- package/dist/lib/error/payload.d.ts +2 -2
- package/dist/lib/error/payload.d.ts.map +1 -1
- package/dist/lib/error/payload.js +2 -2
- package/dist/lib/mcp-interop.d.ts +9 -108
- package/dist/lib/mcp-interop.d.ts.map +1 -1
- package/dist/lib/mcp-interop.js +39 -240
- package/dist/lib/net/http.d.ts.map +1 -1
- package/dist/lib/net/http.js +4 -17
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +67 -60
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +39 -34
- package/dist/tasks/adapter.d.ts +15 -0
- package/dist/tasks/adapter.d.ts.map +1 -0
- package/dist/tasks/adapter.js +138 -0
- package/dist/tasks/manager.d.ts +14 -82
- package/dist/tasks/manager.d.ts.map +1 -1
- package/dist/tasks/manager.js +48 -607
- package/dist/tasks/store.d.ts +33 -19
- package/dist/tasks/store.d.ts.map +1 -1
- package/dist/tasks/store.js +143 -80
- package/dist/tools/index.d.ts +7 -13
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +153 -58
- package/dist/transform/index.js +1 -1
- package/package.json +110 -108
package/dist/tasks/store.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type TaskStatus = '
|
|
1
|
+
export type TaskStatus = 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled';
|
|
2
2
|
export interface TaskError {
|
|
3
3
|
code: number;
|
|
4
4
|
message: string;
|
|
@@ -9,18 +9,25 @@ export interface TaskState {
|
|
|
9
9
|
ownerKey: string;
|
|
10
10
|
status: TaskStatus;
|
|
11
11
|
statusMessage?: string;
|
|
12
|
-
progress?: number;
|
|
13
|
-
total?: number;
|
|
14
12
|
createdAt: string;
|
|
15
13
|
lastUpdatedAt: string;
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
ttl: number | null;
|
|
15
|
+
pollInterval: number;
|
|
18
16
|
result?: unknown;
|
|
19
17
|
error?: TaskError;
|
|
18
|
+
requestId?: string;
|
|
19
|
+
requestMethod?: string;
|
|
20
|
+
requestMeta?: Record<string, unknown>;
|
|
21
|
+
context?: Record<string, unknown>;
|
|
20
22
|
}
|
|
21
23
|
interface CreateTaskOptions {
|
|
22
24
|
taskId?: string;
|
|
23
|
-
|
|
25
|
+
ttl?: number | null;
|
|
26
|
+
pollInterval?: number;
|
|
27
|
+
requestId?: string;
|
|
28
|
+
requestMethod?: string;
|
|
29
|
+
requestMeta?: Record<string, unknown>;
|
|
30
|
+
context?: Record<string, unknown>;
|
|
24
31
|
}
|
|
25
32
|
export interface CreateTaskResult {
|
|
26
33
|
[key: string]: unknown;
|
|
@@ -28,20 +35,25 @@ export interface CreateTaskResult {
|
|
|
28
35
|
taskId: string;
|
|
29
36
|
status: TaskStatus;
|
|
30
37
|
statusMessage?: string;
|
|
31
|
-
progress?: number;
|
|
32
|
-
total?: number;
|
|
33
38
|
createdAt: string;
|
|
34
39
|
lastUpdatedAt: string;
|
|
35
|
-
|
|
36
|
-
pollFrequency: number;
|
|
37
|
-
ttl: number;
|
|
40
|
+
ttl: number | null;
|
|
38
41
|
pollInterval: number;
|
|
39
42
|
};
|
|
40
43
|
}
|
|
44
|
+
interface TerminalTaskErrorResult {
|
|
45
|
+
content: [{
|
|
46
|
+
type: 'text';
|
|
47
|
+
text: string;
|
|
48
|
+
}];
|
|
49
|
+
isError: true;
|
|
50
|
+
}
|
|
51
|
+
export declare function createTerminalTaskErrorResult(task: Pick<TaskState, 'taskId' | 'status' | 'statusMessage' | 'error'>): TerminalTaskErrorResult | undefined;
|
|
41
52
|
declare class TaskManager {
|
|
42
53
|
private tasks;
|
|
43
54
|
private ownerCounts;
|
|
44
55
|
private readonly waiters;
|
|
56
|
+
private readonly statusListeners;
|
|
45
57
|
private cleanupInterval;
|
|
46
58
|
private ensureCleanupLoop;
|
|
47
59
|
private stopCleanupLoop;
|
|
@@ -50,13 +62,15 @@ declare class TaskManager {
|
|
|
50
62
|
private removeTask;
|
|
51
63
|
private applyTaskUpdate;
|
|
52
64
|
private cancelActiveTask;
|
|
65
|
+
private emitStatus;
|
|
66
|
+
onStatusChange(listener: (task: TaskState) => void): () => void;
|
|
53
67
|
private releaseTaskCapacity;
|
|
54
68
|
private reserveTaskCapacity;
|
|
55
69
|
createTask(options?: CreateTaskOptions, statusMessage?: string, ownerKey?: string): TaskState;
|
|
56
70
|
private lookupActiveTask;
|
|
57
71
|
getTask(taskId: string, ownerKey?: string): TaskState | undefined;
|
|
58
|
-
updateTask(taskId: string, updates: Partial<Omit<TaskState, 'taskId' | 'createdAt'
|
|
59
|
-
cancelTask(taskId: string, ownerKey?: string): TaskState | undefined;
|
|
72
|
+
updateTask(taskId: string, updates: Partial<Omit<TaskState, 'taskId' | 'createdAt'>>, silent?: boolean): void;
|
|
73
|
+
cancelTask(taskId: string, ownerKey?: string, silent?: boolean): TaskState | undefined;
|
|
60
74
|
deleteTask(taskId: string, ownerKey?: string): boolean;
|
|
61
75
|
cancelTasksByOwner(ownerKey: string, statusMessage?: string): TaskState[];
|
|
62
76
|
private collectPage;
|
|
@@ -70,7 +84,7 @@ declare class TaskManager {
|
|
|
70
84
|
};
|
|
71
85
|
private resolveAnchorTaskId;
|
|
72
86
|
waitForTerminalTask(taskId: string, ownerKey: string, signal?: AbortSignal): Promise<TaskState | undefined>;
|
|
73
|
-
|
|
87
|
+
shrinkTtlAfterDelivery(taskId: string): void;
|
|
74
88
|
}
|
|
75
89
|
export declare function encodeTaskCursor(anchorTaskId: string): string;
|
|
76
90
|
export declare function decodeTaskCursor(cursor: string): {
|
|
@@ -79,15 +93,15 @@ export declare function decodeTaskCursor(cursor: string): {
|
|
|
79
93
|
interface WaitableTask {
|
|
80
94
|
taskId: string;
|
|
81
95
|
ownerKey: string;
|
|
82
|
-
status:
|
|
83
|
-
|
|
96
|
+
status: TaskStatus;
|
|
97
|
+
ttl: number | null;
|
|
84
98
|
_createdAtMs: number;
|
|
85
99
|
}
|
|
86
100
|
type TaskWaiter<TTask extends WaitableTask> = (task: TTask) => void;
|
|
87
101
|
export declare class TaskWaiterRegistry<TTask extends WaitableTask> {
|
|
88
|
-
private readonly
|
|
102
|
+
private readonly isTerminal;
|
|
89
103
|
private waiters;
|
|
90
|
-
constructor(
|
|
104
|
+
constructor(isTerminal: (status: TTask['status']) => boolean);
|
|
91
105
|
add(taskId: string, waiter: TaskWaiter<TTask>): void;
|
|
92
106
|
remove(taskId: string, waiter: TaskWaiter<TTask> | null): void;
|
|
93
107
|
notify(task: TTask): void;
|
|
@@ -99,7 +113,7 @@ export declare function waitForTerminalTask<TTask extends WaitableTask>(options:
|
|
|
99
113
|
lookupTask: (taskId: string, ownerKey: string) => TTask | undefined;
|
|
100
114
|
removeTask: (taskId: string) => void;
|
|
101
115
|
registry: TaskWaiterRegistry<TTask>;
|
|
102
|
-
|
|
116
|
+
isTerminal: (status: TTask['status']) => boolean;
|
|
103
117
|
}): Promise<TTask | undefined>;
|
|
104
118
|
export declare const taskManager: TaskManager;
|
|
105
119
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/tasks/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/tasks/store.ts"],"names":[],"mappings":"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"}
|
package/dist/tasks/store.js
CHANGED
|
@@ -1,52 +1,75 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isTerminal, ProtocolErrorCode, SdkErrorCode, } from '@modelcontextprotocol/server';
|
|
2
2
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
3
|
import { createHmac, randomBytes, randomUUID } from 'node:crypto';
|
|
4
4
|
import { config } from '../lib/config.js';
|
|
5
5
|
import { Loggers, logInfo, logWarn } from '../lib/core.js';
|
|
6
6
|
import { toError } from '../lib/error/index.js';
|
|
7
|
-
import {
|
|
7
|
+
import { createProtocolError } from '../lib/mcp-interop.js';
|
|
8
8
|
import { createUnrefTimeout, isObject, timingSafeEqualUtf8, } from '../lib/utils.js';
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
9
|
+
const DEFAULT_TTL_MS = 60_000;
|
|
10
|
+
const MIN_TTL_MS = 1_000;
|
|
11
|
+
const MAX_TTL_MS = 86_400_000;
|
|
12
|
+
const DEFAULT_POLL_INTERVAL_MS = 1_000;
|
|
13
13
|
const DEFAULT_OWNER_KEY = 'default';
|
|
14
14
|
const DEFAULT_PAGE_SIZE = 50;
|
|
15
15
|
const CLEANUP_INTERVAL_MS = 60_000;
|
|
16
16
|
const RESULT_DELIVERY_GRACE_MS = 10_000;
|
|
17
|
+
const CONNECTION_CLOSED_ERROR_CODE = -32000;
|
|
17
18
|
const TASK_STATUS_VALUES = new Set([
|
|
18
|
-
'submitted',
|
|
19
19
|
'working',
|
|
20
20
|
'input_required',
|
|
21
21
|
'completed',
|
|
22
22
|
'failed',
|
|
23
23
|
'cancelled',
|
|
24
24
|
]);
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
export function createTerminalTaskErrorResult(task) {
|
|
26
|
+
if (task.status !== 'cancelled' && task.status !== 'failed') {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const message = task.statusMessage ??
|
|
30
|
+
(task.status === 'cancelled' ? 'The task was cancelled.' : 'Task failed.');
|
|
31
|
+
const payload = {
|
|
32
|
+
error: message,
|
|
33
|
+
taskId: task.taskId,
|
|
34
|
+
status: task.status,
|
|
35
|
+
};
|
|
36
|
+
if (task.error?.code !== undefined)
|
|
37
|
+
payload['code'] = task.error.code;
|
|
38
|
+
if (task.error?.data !== undefined)
|
|
39
|
+
payload['data'] = task.error.data;
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
42
|
+
isError: true,
|
|
43
|
+
};
|
|
32
44
|
}
|
|
33
45
|
function resolveNextTaskStatus(task, updates) {
|
|
34
46
|
const nextStatus = updates.status;
|
|
35
47
|
if (!nextStatus || nextStatus === task.status)
|
|
36
48
|
return task.status;
|
|
37
49
|
if (!TASK_STATUS_VALUES.has(nextStatus)) {
|
|
38
|
-
throw
|
|
50
|
+
throw createProtocolError(ProtocolErrorCode.InternalError, `Invalid task status: ${nextStatus}`);
|
|
39
51
|
}
|
|
40
|
-
if (
|
|
41
|
-
throw
|
|
52
|
+
if (isTerminal(task.status)) {
|
|
53
|
+
throw createProtocolError(ProtocolErrorCode.InternalError, `Cannot transition task from ${task.status} to ${nextStatus}`);
|
|
42
54
|
}
|
|
43
55
|
return nextStatus;
|
|
44
56
|
}
|
|
45
|
-
function
|
|
46
|
-
if (
|
|
47
|
-
return
|
|
57
|
+
function normalizeTtl(ttl) {
|
|
58
|
+
if (ttl === undefined || !Number.isFinite(ttl)) {
|
|
59
|
+
return DEFAULT_TTL_MS;
|
|
48
60
|
}
|
|
49
|
-
return Math.max(
|
|
61
|
+
return Math.max(MIN_TTL_MS, Math.min(Math.trunc(ttl), MAX_TTL_MS));
|
|
62
|
+
}
|
|
63
|
+
function normalizeOptionalTtl(ttl) {
|
|
64
|
+
if (ttl === null)
|
|
65
|
+
return null;
|
|
66
|
+
return normalizeTtl(ttl);
|
|
67
|
+
}
|
|
68
|
+
function normalizePollInterval(pollInterval) {
|
|
69
|
+
if (pollInterval === undefined || !Number.isFinite(pollInterval)) {
|
|
70
|
+
return DEFAULT_POLL_INTERVAL_MS;
|
|
71
|
+
}
|
|
72
|
+
return Math.max(1, Math.trunc(pollInterval));
|
|
50
73
|
}
|
|
51
74
|
function logTaskStatusTransition(task, previousStatus, nextStatus) {
|
|
52
75
|
if (previousStatus === nextStatus)
|
|
@@ -67,7 +90,8 @@ function logTaskStatusTransition(task, previousStatus, nextStatus) {
|
|
|
67
90
|
class TaskManager {
|
|
68
91
|
tasks = new Map();
|
|
69
92
|
ownerCounts = new Map();
|
|
70
|
-
waiters = new TaskWaiterRegistry(
|
|
93
|
+
waiters = new TaskWaiterRegistry(isTerminal);
|
|
94
|
+
statusListeners = new Set();
|
|
71
95
|
cleanupInterval = null;
|
|
72
96
|
ensureCleanupLoop() {
|
|
73
97
|
if (this.cleanupInterval)
|
|
@@ -85,10 +109,12 @@ class TaskManager {
|
|
|
85
109
|
this.cleanupInterval = null;
|
|
86
110
|
}
|
|
87
111
|
isTaskExpired(task, nowMs) {
|
|
112
|
+
if (task.ttl === null)
|
|
113
|
+
return false;
|
|
88
114
|
if (task._terminalAtMs !== undefined) {
|
|
89
|
-
return nowMs - task._terminalAtMs > task.
|
|
115
|
+
return nowMs - task._terminalAtMs > task.ttl;
|
|
90
116
|
}
|
|
91
|
-
return nowMs - task._createdAtMs >
|
|
117
|
+
return nowMs - task._createdAtMs > MAX_TTL_MS;
|
|
92
118
|
}
|
|
93
119
|
removeExpiredTasks() {
|
|
94
120
|
const now = Date.now();
|
|
@@ -107,11 +133,12 @@ class TaskManager {
|
|
|
107
133
|
const task = this.tasks.get(taskId);
|
|
108
134
|
if (!task)
|
|
109
135
|
return;
|
|
110
|
-
if (!
|
|
136
|
+
if (!isTerminal(task.status)) {
|
|
111
137
|
this.applyTaskUpdate(task, {
|
|
112
138
|
status: 'failed',
|
|
113
139
|
statusMessage: 'Task removed due to expiration',
|
|
114
140
|
});
|
|
141
|
+
this.emitStatus(task);
|
|
115
142
|
this.waiters.notify(task);
|
|
116
143
|
}
|
|
117
144
|
this.tasks.delete(taskId);
|
|
@@ -121,23 +148,42 @@ class TaskManager {
|
|
|
121
148
|
Object.assign(task, updates);
|
|
122
149
|
task.lastUpdatedAt = new Date().toISOString();
|
|
123
150
|
if (updates.status &&
|
|
124
|
-
|
|
151
|
+
isTerminal(updates.status) &&
|
|
125
152
|
task._terminalAtMs === undefined) {
|
|
126
153
|
task._terminalAtMs = Date.now();
|
|
127
154
|
}
|
|
128
155
|
}
|
|
129
|
-
cancelActiveTask(task, statusMessage) {
|
|
156
|
+
cancelActiveTask(task, statusMessage, silent) {
|
|
157
|
+
const error = {
|
|
158
|
+
code: CONNECTION_CLOSED_ERROR_CODE,
|
|
159
|
+
message: statusMessage,
|
|
160
|
+
data: { code: 'ABORTED', sdkCode: SdkErrorCode.ConnectionClosed },
|
|
161
|
+
};
|
|
130
162
|
this.applyTaskUpdate(task, {
|
|
131
163
|
status: 'cancelled',
|
|
132
164
|
statusMessage,
|
|
133
|
-
error
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
165
|
+
error,
|
|
166
|
+
result: createTerminalTaskErrorResult({
|
|
167
|
+
taskId: task.taskId,
|
|
168
|
+
status: 'cancelled',
|
|
169
|
+
statusMessage,
|
|
170
|
+
error,
|
|
171
|
+
}),
|
|
138
172
|
});
|
|
173
|
+
if (!silent)
|
|
174
|
+
this.emitStatus(task);
|
|
139
175
|
this.waiters.notify(task);
|
|
140
176
|
}
|
|
177
|
+
emitStatus(task) {
|
|
178
|
+
for (const listener of this.statusListeners)
|
|
179
|
+
listener(task);
|
|
180
|
+
}
|
|
181
|
+
onStatusChange(listener) {
|
|
182
|
+
this.statusListeners.add(listener);
|
|
183
|
+
return () => {
|
|
184
|
+
this.statusListeners.delete(listener);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
141
187
|
releaseTaskCapacity(task) {
|
|
142
188
|
const nextCount = (this.ownerCounts.get(task.ownerKey) ?? 0) - 1;
|
|
143
189
|
if (nextCount > 0) {
|
|
@@ -149,10 +195,10 @@ class TaskManager {
|
|
|
149
195
|
reserveTaskCapacity(ownerKey) {
|
|
150
196
|
const { maxPerOwner, maxTotal } = config.tasks;
|
|
151
197
|
if (this.tasks.size >= maxTotal) {
|
|
152
|
-
throw
|
|
198
|
+
throw createProtocolError(ProtocolErrorCode.InvalidRequest, `Server task limit reached (${maxTotal})`);
|
|
153
199
|
}
|
|
154
200
|
if ((this.ownerCounts.get(ownerKey) ?? 0) >= maxPerOwner) {
|
|
155
|
-
throw
|
|
201
|
+
throw createProtocolError(ProtocolErrorCode.InvalidRequest, `Task limit reached for this session (${maxPerOwner})`);
|
|
156
202
|
}
|
|
157
203
|
this.ownerCounts.set(ownerKey, (this.ownerCounts.get(ownerKey) ?? 0) + 1);
|
|
158
204
|
}
|
|
@@ -160,7 +206,7 @@ class TaskManager {
|
|
|
160
206
|
this.removeExpiredTasks();
|
|
161
207
|
const taskId = options?.taskId ?? randomUUID();
|
|
162
208
|
if (this.tasks.has(taskId)) {
|
|
163
|
-
throw
|
|
209
|
+
throw createProtocolError(ProtocolErrorCode.InvalidRequest, `Task already exists: ${taskId}`);
|
|
164
210
|
}
|
|
165
211
|
this.reserveTaskCapacity(ownerKey);
|
|
166
212
|
const now = new Date();
|
|
@@ -168,12 +214,18 @@ class TaskManager {
|
|
|
168
214
|
const task = {
|
|
169
215
|
taskId,
|
|
170
216
|
ownerKey,
|
|
171
|
-
status: '
|
|
217
|
+
status: 'working',
|
|
172
218
|
statusMessage,
|
|
173
219
|
createdAt,
|
|
174
220
|
lastUpdatedAt: createdAt,
|
|
175
|
-
|
|
176
|
-
|
|
221
|
+
ttl: normalizeOptionalTtl(options?.ttl),
|
|
222
|
+
pollInterval: normalizePollInterval(options?.pollInterval),
|
|
223
|
+
...(options?.requestId ? { requestId: options.requestId } : {}),
|
|
224
|
+
...(options?.requestMethod
|
|
225
|
+
? { requestMethod: options.requestMethod }
|
|
226
|
+
: {}),
|
|
227
|
+
...(options?.requestMeta ? { requestMeta: options.requestMeta } : {}),
|
|
228
|
+
...(options?.context ? { context: options.context } : {}),
|
|
177
229
|
_createdAtMs: now.getTime(),
|
|
178
230
|
};
|
|
179
231
|
this.tasks.set(task.taskId, task);
|
|
@@ -181,8 +233,10 @@ class TaskManager {
|
|
|
181
233
|
logInfo('Task created', {
|
|
182
234
|
taskId: task.taskId,
|
|
183
235
|
ownerKey,
|
|
184
|
-
|
|
236
|
+
ttl: task.ttl,
|
|
237
|
+
pollInterval: task.pollInterval,
|
|
185
238
|
}, Loggers.LOG_TASKS);
|
|
239
|
+
this.emitStatus(task);
|
|
186
240
|
return task;
|
|
187
241
|
}
|
|
188
242
|
lookupActiveTask(taskId, ownerKey) {
|
|
@@ -200,13 +254,13 @@ class TaskManager {
|
|
|
200
254
|
getTask(taskId, ownerKey) {
|
|
201
255
|
return this.lookupActiveTask(taskId, ownerKey);
|
|
202
256
|
}
|
|
203
|
-
updateTask(taskId, updates) {
|
|
257
|
+
updateTask(taskId, updates, silent) {
|
|
204
258
|
const task = this.tasks.get(taskId);
|
|
205
259
|
if (!task) {
|
|
206
260
|
logWarn('updateTask called for unknown task', { taskId }, Loggers.LOG_TASKS);
|
|
207
261
|
return;
|
|
208
262
|
}
|
|
209
|
-
if (
|
|
263
|
+
if (isTerminal(task.status)) {
|
|
210
264
|
logWarn('updateTask called for terminal task', {
|
|
211
265
|
taskId,
|
|
212
266
|
currentStatus: task.status,
|
|
@@ -220,16 +274,18 @@ class TaskManager {
|
|
|
220
274
|
...(updates.status === undefined ? {} : { status: nextStatus }),
|
|
221
275
|
});
|
|
222
276
|
logTaskStatusTransition(task, previousStatus, task.status);
|
|
277
|
+
if (!silent)
|
|
278
|
+
this.emitStatus(task);
|
|
223
279
|
this.waiters.notify(task);
|
|
224
280
|
}
|
|
225
|
-
cancelTask(taskId, ownerKey) {
|
|
281
|
+
cancelTask(taskId, ownerKey, silent) {
|
|
226
282
|
const task = this.lookupActiveTask(taskId, ownerKey);
|
|
227
283
|
if (!task)
|
|
228
284
|
return undefined;
|
|
229
|
-
if (
|
|
230
|
-
throw
|
|
285
|
+
if (isTerminal(task.status)) {
|
|
286
|
+
throw createProtocolError(ProtocolErrorCode.InvalidParams, `Cannot cancel task: already ${task.status}`);
|
|
231
287
|
}
|
|
232
|
-
this.cancelActiveTask(task, 'The task was cancelled by request.');
|
|
288
|
+
this.cancelActiveTask(task, 'The task was cancelled by request.', silent);
|
|
233
289
|
logInfo('Task cancelled by request', {
|
|
234
290
|
taskId: task.taskId,
|
|
235
291
|
ownerKey: task.ownerKey,
|
|
@@ -240,8 +296,8 @@ class TaskManager {
|
|
|
240
296
|
const task = this.lookupActiveTask(taskId, ownerKey);
|
|
241
297
|
if (!task)
|
|
242
298
|
return false;
|
|
243
|
-
if (!
|
|
244
|
-
throw
|
|
299
|
+
if (!isTerminal(task.status)) {
|
|
300
|
+
throw createProtocolError(ProtocolErrorCode.InvalidParams, `Cannot delete task: status is ${task.status}`);
|
|
245
301
|
}
|
|
246
302
|
this.tasks.delete(taskId);
|
|
247
303
|
this.releaseTaskCapacity(task);
|
|
@@ -253,7 +309,7 @@ class TaskManager {
|
|
|
253
309
|
return [];
|
|
254
310
|
const cancelled = [];
|
|
255
311
|
for (const task of this.tasks.values()) {
|
|
256
|
-
if (task.ownerKey !== ownerKey ||
|
|
312
|
+
if (task.ownerKey !== ownerKey || isTerminal(task.status))
|
|
257
313
|
continue;
|
|
258
314
|
this.cancelActiveTask(task, statusMessage);
|
|
259
315
|
cancelled.push(task);
|
|
@@ -281,7 +337,7 @@ class TaskManager {
|
|
|
281
337
|
}
|
|
282
338
|
const anchorIndex = validTasks.findIndex((task) => task.taskId === anchorTaskId);
|
|
283
339
|
if (anchorIndex === -1) {
|
|
284
|
-
throw
|
|
340
|
+
throw createProtocolError(ProtocolErrorCode.InvalidParams, 'Invalid cursor');
|
|
285
341
|
}
|
|
286
342
|
return validTasks.slice(anchorIndex + 1, anchorIndex + 1 + pageSize + 1);
|
|
287
343
|
}
|
|
@@ -301,7 +357,7 @@ class TaskManager {
|
|
|
301
357
|
return null;
|
|
302
358
|
const decoded = decodeTaskCursor(cursor);
|
|
303
359
|
if (!decoded) {
|
|
304
|
-
throw
|
|
360
|
+
throw createProtocolError(ProtocolErrorCode.InvalidParams, 'Invalid cursor');
|
|
305
361
|
}
|
|
306
362
|
return decoded.anchorTaskId;
|
|
307
363
|
}
|
|
@@ -315,15 +371,17 @@ class TaskManager {
|
|
|
315
371
|
this.removeTask(id);
|
|
316
372
|
},
|
|
317
373
|
registry: this.waiters,
|
|
318
|
-
|
|
374
|
+
isTerminal,
|
|
319
375
|
});
|
|
320
376
|
}
|
|
321
|
-
|
|
377
|
+
shrinkTtlAfterDelivery(taskId) {
|
|
322
378
|
const task = this.tasks.get(taskId);
|
|
323
|
-
if (!task || !
|
|
379
|
+
if (!task || !isTerminal(task.status))
|
|
380
|
+
return;
|
|
381
|
+
if (task.ttl === null)
|
|
324
382
|
return;
|
|
325
|
-
if (RESULT_DELIVERY_GRACE_MS < task.
|
|
326
|
-
task.
|
|
383
|
+
if (RESULT_DELIVERY_GRACE_MS < task.ttl) {
|
|
384
|
+
task.ttl = RESULT_DELIVERY_GRACE_MS;
|
|
327
385
|
task.lastUpdatedAt = new Date().toISOString();
|
|
328
386
|
}
|
|
329
387
|
}
|
|
@@ -369,10 +427,10 @@ export function decodeTaskCursor(cursor) {
|
|
|
369
427
|
}
|
|
370
428
|
}
|
|
371
429
|
export class TaskWaiterRegistry {
|
|
372
|
-
|
|
430
|
+
isTerminal;
|
|
373
431
|
waiters = new Map();
|
|
374
|
-
constructor(
|
|
375
|
-
this.
|
|
432
|
+
constructor(isTerminal) {
|
|
433
|
+
this.isTerminal = isTerminal;
|
|
376
434
|
}
|
|
377
435
|
add(taskId, waiter) {
|
|
378
436
|
let set = this.waiters.get(taskId);
|
|
@@ -394,7 +452,7 @@ export class TaskWaiterRegistry {
|
|
|
394
452
|
}
|
|
395
453
|
}
|
|
396
454
|
notify(task) {
|
|
397
|
-
if (!this.
|
|
455
|
+
if (!this.isTerminal(task.status))
|
|
398
456
|
return;
|
|
399
457
|
const waiters = this.waiters.get(task.taskId);
|
|
400
458
|
if (!waiters)
|
|
@@ -408,9 +466,12 @@ export async function waitForTerminalTask(options) {
|
|
|
408
466
|
const task = options.lookupTask(options.taskId, options.ownerKey);
|
|
409
467
|
if (!task)
|
|
410
468
|
return undefined;
|
|
411
|
-
if (options.
|
|
469
|
+
if (options.isTerminal(task.status))
|
|
412
470
|
return task;
|
|
413
|
-
|
|
471
|
+
return createWaitForTaskPromise(options, task.ttl === null ? null : task._createdAtMs + task.ttl);
|
|
472
|
+
}
|
|
473
|
+
// eslint-disable-next-line sonarjs/no-invariant-returns -- this helper intentionally returns the shared pending promise it wires up below.
|
|
474
|
+
function createWaitForTaskPromise(options, deadlineMs) {
|
|
414
475
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
415
476
|
const resolveInContext = AsyncLocalStorage.bind((value) => {
|
|
416
477
|
resolve(value);
|
|
@@ -431,16 +492,16 @@ export async function waitForTerminalTask(options) {
|
|
|
431
492
|
}
|
|
432
493
|
};
|
|
433
494
|
const settleOnce = (fn) => {
|
|
434
|
-
if (settled)
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
495
|
+
if (!settled) {
|
|
496
|
+
settled = true;
|
|
497
|
+
fn();
|
|
498
|
+
}
|
|
438
499
|
};
|
|
439
500
|
const onAbort = () => {
|
|
440
501
|
settleOnce(() => {
|
|
441
502
|
cleanup();
|
|
442
503
|
options.registry.remove(options.taskId, waiter);
|
|
443
|
-
rejectInContext(
|
|
504
|
+
rejectInContext(createProtocolError(CONNECTION_CLOSED_ERROR_CODE, 'Request was cancelled'));
|
|
444
505
|
});
|
|
445
506
|
};
|
|
446
507
|
waiter = (updated) => {
|
|
@@ -461,20 +522,22 @@ export async function waitForTerminalTask(options) {
|
|
|
461
522
|
if (options.signal) {
|
|
462
523
|
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
463
524
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
.
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
525
|
+
if (deadlineMs !== null) {
|
|
526
|
+
const timeoutMs = Math.max(0, deadlineMs - Date.now());
|
|
527
|
+
deadlineTimeout = createUnrefTimeout(timeoutMs, { timeout: true });
|
|
528
|
+
void deadlineTimeout.promise
|
|
529
|
+
.then(() => {
|
|
530
|
+
settleOnce(() => {
|
|
531
|
+
cleanup();
|
|
532
|
+
options.registry.remove(options.taskId, waiter);
|
|
533
|
+
options.removeTask(options.taskId);
|
|
534
|
+
rejectInContext(createProtocolError(ProtocolErrorCode.InvalidParams, 'Task expired', {
|
|
535
|
+
taskId: options.taskId,
|
|
536
|
+
}));
|
|
537
|
+
});
|
|
538
|
+
})
|
|
539
|
+
.catch(rejectInContext);
|
|
540
|
+
}
|
|
478
541
|
return promise;
|
|
479
542
|
}
|
|
480
543
|
export const taskManager = new TaskManager();
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,32 +1,26 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type ContentBlock, type ServerResult } from '@modelcontextprotocol/sdk/types.js';
|
|
1
|
+
import { type CallToolResult, type ContentBlock, type McpServer, type ServerContext, type ServerResult } from '@modelcontextprotocol/server';
|
|
3
2
|
import type { z } from 'zod';
|
|
4
|
-
import { type ProgressReporter
|
|
3
|
+
import { type ProgressReporter } from '../lib/mcp-interop.js';
|
|
5
4
|
import { type SharedFetchStage } from '../lib/net/index.js';
|
|
6
5
|
import { fetchUrlInputSchema } from '../schemas.js';
|
|
7
|
-
import { type TaskCapableToolSupport } from '../tasks/index.js';
|
|
8
6
|
type FetchUrlInput = z.infer<typeof fetchUrlInputSchema>;
|
|
9
|
-
interface ToolResponseBase {
|
|
10
|
-
[key: string]: unknown;
|
|
11
|
-
content: ContentBlock[];
|
|
12
|
-
structuredContent?: Record<string, unknown> | undefined;
|
|
13
|
-
isError?: boolean;
|
|
14
|
-
}
|
|
15
7
|
export declare const FETCH_URL_TOOL_NAME = "fetch-url";
|
|
16
8
|
export declare function buildFetchUrlContentBlocks(structuredContent: Record<string, unknown>): ContentBlock[];
|
|
17
9
|
export declare function getFetchCompletionStatusMessage(result: ServerResult): string | undefined;
|
|
18
10
|
export declare class FetchUrlProgressPlan {
|
|
19
11
|
private readonly reporter;
|
|
20
12
|
private readonly context;
|
|
13
|
+
private readonly onStatusMessage?;
|
|
21
14
|
private readonly total;
|
|
22
|
-
constructor(reporter: ProgressReporter, context: string);
|
|
15
|
+
constructor(reporter: ProgressReporter, context: string, onStatusMessage?: ((message: string) => void) | undefined);
|
|
23
16
|
reportStart(): void;
|
|
24
17
|
reportStage(stage: SharedFetchStage): void;
|
|
25
18
|
reportSuccess(contentSize: number): void;
|
|
26
|
-
reportFailure(cancelled: boolean): void;
|
|
19
|
+
reportFailure(cancelled: boolean, error?: unknown): void;
|
|
27
20
|
private mapStage;
|
|
28
21
|
}
|
|
29
|
-
export declare function fetchUrlToolHandler(input: FetchUrlInput,
|
|
22
|
+
export declare function fetchUrlToolHandler(input: FetchUrlInput, ctx?: ServerContext): Promise<CallToolResult>;
|
|
23
|
+
type TaskCapableToolSupport = 'required' | 'optional' | 'forbidden';
|
|
30
24
|
export interface ToolRegistrationControls {
|
|
31
25
|
setTaskSupport: (support: TaskCapableToolSupport) => void;
|
|
32
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,SAAS,EAEd,KAAK,aAAa,EAClB,KAAK,YAAY,EAElB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAW7B,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAOL,KAAK,gBAAgB,EAEtB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,mBAAmB,EAIpB,MAAM,eAAe,CAAC;AAUvB,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEzD,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAkF/C,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACzC,YAAY,EAAE,CAUhB;AA0CD,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,YAAY,GACnB,MAAM,GAAG,SAAS,CAUpB;AAED,qBAAa,oBAAoB;IAI7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IALnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;gBAGhB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,aAAA;IAG9D,WAAW,IAAI,IAAI;IAUnB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAW1C,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAUxC,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI;IAiBxD,OAAO,CAAC,QAAQ;CAoCjB;AAoID,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,aAAa,EACpB,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAAC,cAAc,CAAC,CAazB;AAqBD,KAAK,sBAAsB,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,wBAAwB,CA+GzE"}
|