@j0hanz/fetch-url-mcp 1.4.0 → 1.5.0
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/dist/cli.d.ts +2 -3
- package/dist/cli.js +1 -2
- package/dist/http/auth.d.ts +5 -3
- package/dist/http/auth.js +64 -15
- package/dist/http/health.d.ts +1 -2
- package/dist/http/health.js +7 -18
- package/dist/http/helpers.d.ts +3 -4
- package/dist/http/helpers.js +21 -21
- package/dist/http/native.d.ts +0 -1
- package/dist/http/native.js +34 -26
- package/dist/http/rate-limit.d.ts +0 -1
- package/dist/http/rate-limit.js +3 -4
- package/dist/index.d.ts +0 -1
- package/dist/index.js +17 -18
- package/dist/lib/{markdown-cleanup.d.ts → content.d.ts} +4 -2
- package/dist/lib/content.js +1356 -0
- package/dist/lib/core.d.ts +253 -0
- package/dist/lib/core.js +1228 -0
- package/dist/lib/{tool-pipeline.d.ts → fetch-pipeline.d.ts} +1 -2
- package/dist/lib/{tool-pipeline.js → fetch-pipeline.js} +10 -19
- package/dist/lib/{fetch.d.ts → http.d.ts} +7 -9
- package/dist/lib/{fetch.js → http.js} +706 -944
- package/dist/lib/mcp-tools.d.ts +28 -0
- package/dist/lib/mcp-tools.js +107 -0
- package/dist/lib/{tool-progress.d.ts → progress.d.ts} +0 -1
- package/dist/lib/{tool-progress.js → progress.js} +8 -13
- package/dist/lib/task-handlers.d.ts +5 -0
- package/dist/lib/{mcp.js → task-handlers.js} +56 -12
- package/dist/lib/url.d.ts +70 -0
- package/dist/lib/url.js +686 -0
- package/dist/lib/utils.d.ts +58 -0
- package/dist/lib/utils.js +304 -0
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -1
- package/dist/resources/index.d.ts +0 -1
- package/dist/resources/index.js +74 -33
- package/dist/resources/instructions.d.ts +0 -1
- package/dist/resources/instructions.js +2 -2
- package/dist/schemas/inputs.d.ts +0 -1
- package/dist/schemas/inputs.js +2 -3
- package/dist/schemas/outputs.d.ts +0 -1
- package/dist/schemas/outputs.js +1 -2
- package/dist/server.d.ts +0 -1
- package/dist/server.js +16 -26
- package/dist/tasks/execution.d.ts +0 -1
- package/dist/tasks/execution.js +27 -24
- package/dist/tasks/manager.d.ts +7 -3
- package/dist/tasks/manager.js +53 -34
- package/dist/tasks/owner.d.ts +1 -2
- package/dist/tasks/owner.js +1 -2
- package/dist/tasks/tool-registry.d.ts +1 -2
- package/dist/tasks/tool-registry.js +0 -1
- package/dist/tools/fetch-url.d.ts +1 -2
- package/dist/tools/fetch-url.js +39 -31
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/transform/html-translators.d.ts +1 -0
- package/dist/transform/html-translators.js +454 -0
- package/dist/transform/metadata.d.ts +4 -0
- package/dist/transform/metadata.js +183 -0
- package/dist/transform/transform.d.ts +0 -1
- package/dist/transform/transform.js +24 -641
- package/dist/transform/types.d.ts +9 -11
- package/dist/transform/types.js +0 -1
- package/dist/transform/worker-pool.d.ts +0 -1
- package/dist/transform/worker-pool.js +7 -16
- package/dist/transform/workers/shared.d.ts +0 -1
- package/dist/transform/workers/shared.js +1 -2
- package/dist/transform/workers/transform-child.d.ts +0 -1
- package/dist/transform/workers/transform-child.js +0 -1
- package/dist/transform/workers/transform-worker.d.ts +0 -1
- package/dist/transform/workers/transform-worker.js +0 -1
- package/package.json +6 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/http/auth.d.ts.map +0 -1
- package/dist/http/auth.js.map +0 -1
- package/dist/http/health.d.ts.map +0 -1
- package/dist/http/health.js.map +0 -1
- package/dist/http/helpers.d.ts.map +0 -1
- package/dist/http/helpers.js.map +0 -1
- package/dist/http/native.d.ts.map +0 -1
- package/dist/http/native.js.map +0 -1
- package/dist/http/rate-limit.d.ts.map +0 -1
- package/dist/http/rate-limit.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/cache.d.ts +0 -54
- package/dist/lib/cache.d.ts.map +0 -1
- package/dist/lib/cache.js +0 -264
- package/dist/lib/cache.js.map +0 -1
- package/dist/lib/config.d.ts +0 -143
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/config.js +0 -476
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/crypto.d.ts +0 -4
- package/dist/lib/crypto.d.ts.map +0 -1
- package/dist/lib/crypto.js +0 -56
- package/dist/lib/crypto.js.map +0 -1
- package/dist/lib/dom-noise-removal.d.ts +0 -2
- package/dist/lib/dom-noise-removal.d.ts.map +0 -1
- package/dist/lib/dom-noise-removal.js +0 -494
- package/dist/lib/dom-noise-removal.js.map +0 -1
- package/dist/lib/download.d.ts +0 -4
- package/dist/lib/download.d.ts.map +0 -1
- package/dist/lib/download.js +0 -106
- package/dist/lib/download.js.map +0 -1
- package/dist/lib/errors.d.ts +0 -14
- package/dist/lib/errors.d.ts.map +0 -1
- package/dist/lib/errors.js +0 -72
- package/dist/lib/errors.js.map +0 -1
- package/dist/lib/fetch-content.d.ts +0 -5
- package/dist/lib/fetch-content.d.ts.map +0 -1
- package/dist/lib/fetch-content.js +0 -164
- package/dist/lib/fetch-content.js.map +0 -1
- package/dist/lib/fetch-stream.d.ts +0 -5
- package/dist/lib/fetch-stream.d.ts.map +0 -1
- package/dist/lib/fetch-stream.js +0 -29
- package/dist/lib/fetch-stream.js.map +0 -1
- package/dist/lib/fetch.d.ts.map +0 -1
- package/dist/lib/fetch.js.map +0 -1
- package/dist/lib/host-normalization.d.ts +0 -2
- package/dist/lib/host-normalization.d.ts.map +0 -1
- package/dist/lib/host-normalization.js +0 -91
- package/dist/lib/host-normalization.js.map +0 -1
- package/dist/lib/ip-blocklist.d.ts +0 -9
- package/dist/lib/ip-blocklist.d.ts.map +0 -1
- package/dist/lib/ip-blocklist.js +0 -79
- package/dist/lib/ip-blocklist.js.map +0 -1
- package/dist/lib/json.d.ts +0 -2
- package/dist/lib/json.d.ts.map +0 -1
- package/dist/lib/json.js +0 -45
- package/dist/lib/json.js.map +0 -1
- package/dist/lib/language-detection.d.ts +0 -3
- package/dist/lib/language-detection.d.ts.map +0 -1
- package/dist/lib/language-detection.js +0 -355
- package/dist/lib/language-detection.js.map +0 -1
- package/dist/lib/markdown-cleanup.d.ts.map +0 -1
- package/dist/lib/markdown-cleanup.js +0 -532
- package/dist/lib/markdown-cleanup.js.map +0 -1
- package/dist/lib/mcp-lifecycle.d.ts +0 -5
- package/dist/lib/mcp-lifecycle.d.ts.map +0 -1
- package/dist/lib/mcp-lifecycle.js +0 -51
- package/dist/lib/mcp-lifecycle.js.map +0 -1
- package/dist/lib/mcp-validator.d.ts +0 -17
- package/dist/lib/mcp-validator.d.ts.map +0 -1
- package/dist/lib/mcp-validator.js +0 -45
- package/dist/lib/mcp-validator.js.map +0 -1
- package/dist/lib/mcp.d.ts +0 -4
- package/dist/lib/mcp.d.ts.map +0 -1
- package/dist/lib/mcp.js.map +0 -1
- package/dist/lib/observability.d.ts +0 -23
- package/dist/lib/observability.d.ts.map +0 -1
- package/dist/lib/observability.js +0 -238
- package/dist/lib/observability.js.map +0 -1
- package/dist/lib/server-tuning.d.ts +0 -15
- package/dist/lib/server-tuning.d.ts.map +0 -1
- package/dist/lib/server-tuning.js +0 -49
- package/dist/lib/server-tuning.js.map +0 -1
- package/dist/lib/session.d.ts +0 -45
- package/dist/lib/session.d.ts.map +0 -1
- package/dist/lib/session.js +0 -263
- package/dist/lib/session.js.map +0 -1
- package/dist/lib/timer-utils.d.ts +0 -13
- package/dist/lib/timer-utils.d.ts.map +0 -1
- package/dist/lib/timer-utils.js +0 -44
- package/dist/lib/timer-utils.js.map +0 -1
- package/dist/lib/tool-errors.d.ts +0 -12
- package/dist/lib/tool-errors.d.ts.map +0 -1
- package/dist/lib/tool-errors.js +0 -55
- package/dist/lib/tool-errors.js.map +0 -1
- package/dist/lib/tool-pipeline.d.ts.map +0 -1
- package/dist/lib/tool-pipeline.js.map +0 -1
- package/dist/lib/tool-progress.d.ts.map +0 -1
- package/dist/lib/tool-progress.js.map +0 -1
- package/dist/lib/type-guards.d.ts +0 -16
- package/dist/lib/type-guards.d.ts.map +0 -1
- package/dist/lib/type-guards.js +0 -13
- package/dist/lib/type-guards.js.map +0 -1
- package/dist/prompts/index.d.ts.map +0 -1
- package/dist/prompts/index.js.map +0 -1
- package/dist/resources/index.d.ts.map +0 -1
- package/dist/resources/index.js.map +0 -1
- package/dist/resources/instructions.d.ts.map +0 -1
- package/dist/resources/instructions.js.map +0 -1
- package/dist/schemas/inputs.d.ts.map +0 -1
- package/dist/schemas/inputs.js.map +0 -1
- package/dist/schemas/outputs.d.ts.map +0 -1
- package/dist/schemas/outputs.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/tasks/execution.d.ts.map +0 -1
- package/dist/tasks/execution.js.map +0 -1
- package/dist/tasks/manager.d.ts.map +0 -1
- package/dist/tasks/manager.js.map +0 -1
- package/dist/tasks/owner.d.ts.map +0 -1
- package/dist/tasks/owner.js.map +0 -1
- package/dist/tasks/tool-registry.d.ts.map +0 -1
- package/dist/tasks/tool-registry.js.map +0 -1
- package/dist/tools/fetch-url.d.ts.map +0 -1
- package/dist/tools/fetch-url.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/transform/transform.d.ts.map +0 -1
- package/dist/transform/transform.js.map +0 -1
- package/dist/transform/types.d.ts.map +0 -1
- package/dist/transform/types.js.map +0 -1
- package/dist/transform/worker-pool.d.ts.map +0 -1
- package/dist/transform/worker-pool.js.map +0 -1
- package/dist/transform/workers/shared.d.ts.map +0 -1
- package/dist/transform/workers/shared.js.map +0 -1
- package/dist/transform/workers/transform-child.d.ts.map +0 -1
- package/dist/transform/workers/transform-child.js.map +0 -1
- package/dist/transform/workers/transform-worker.d.ts.map +0 -1
- package/dist/transform/workers/transform-worker.js.map +0 -1
package/dist/server.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
1
|
import * as fs from 'node:fs/promises';
|
|
4
2
|
import process from 'node:process';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { logError, logInfo, setLogLevel, setMcpServer
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { SetLevelRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { config } from './lib/core.js';
|
|
7
|
+
import { logError, logInfo, setLogLevel, setMcpServer } from './lib/core.js';
|
|
8
|
+
import { abortAllTaskExecutions, registerTaskHandlers, } from './lib/mcp-tools.js';
|
|
9
|
+
import { toError } from './lib/utils.js';
|
|
10
10
|
import { registerGetHelpPrompt } from './prompts/index.js';
|
|
11
11
|
import { registerCacheResourceTemplate, registerInstructionResource, } from './resources/index.js';
|
|
12
12
|
import { buildServerInstructions } from './resources/instructions.js';
|
|
@@ -65,6 +65,10 @@ function createServerInfo(icons) {
|
|
|
65
65
|
export async function createMcpServer() {
|
|
66
66
|
return createMcpServerWithOptions({ registerObservabilityServer: true });
|
|
67
67
|
}
|
|
68
|
+
const SHUTDOWN_SIGNALS = ['SIGINT', 'SIGTERM'];
|
|
69
|
+
function shouldRegisterObservabilityServer(options) {
|
|
70
|
+
return options?.registerObservabilityServer ?? true;
|
|
71
|
+
}
|
|
68
72
|
async function createMcpServerWithOptions(options) {
|
|
69
73
|
const localIcon = await getLocalIconInfo();
|
|
70
74
|
const serverConfig = {
|
|
@@ -75,13 +79,16 @@ async function createMcpServerWithOptions(options) {
|
|
|
75
79
|
}
|
|
76
80
|
const serverInfo = createServerInfo(localIcon ? [localIcon] : undefined);
|
|
77
81
|
const server = new McpServer(serverInfo, serverConfig);
|
|
78
|
-
if (options
|
|
82
|
+
if (shouldRegisterObservabilityServer(options)) {
|
|
79
83
|
setMcpServer(server);
|
|
80
84
|
}
|
|
81
85
|
registerAllTools(server);
|
|
82
86
|
registerGetHelpPrompt(server, serverInstructions, localIcon);
|
|
83
87
|
registerInstructionResource(server, serverInstructions, localIcon);
|
|
84
88
|
registerCacheResourceTemplate(server, localIcon);
|
|
89
|
+
// NOTE: Internally patches server.close and server.server.onclose for cleanup
|
|
90
|
+
// callbacks, and intercepts tools/call via Reflect.get on private SDK state.
|
|
91
|
+
// See src/lib/task-handlers.ts for risk documentation (S-2, S-3).
|
|
85
92
|
registerTaskHandlers(server);
|
|
86
93
|
registerLoggingSetLevelHandler(server);
|
|
87
94
|
attachServerErrorHandler(server);
|
|
@@ -91,22 +98,6 @@ export async function createMcpServerForHttpSession() {
|
|
|
91
98
|
return createMcpServerWithOptions({ registerObservabilityServer: false });
|
|
92
99
|
}
|
|
93
100
|
function registerLoggingSetLevelHandler(server) {
|
|
94
|
-
const LoggingLevelSchema = z.enum([
|
|
95
|
-
'debug',
|
|
96
|
-
'info',
|
|
97
|
-
'notice',
|
|
98
|
-
'warning',
|
|
99
|
-
'error',
|
|
100
|
-
'critical',
|
|
101
|
-
'alert',
|
|
102
|
-
'emergency',
|
|
103
|
-
]);
|
|
104
|
-
const SetLevelRequestSchema = z
|
|
105
|
-
.object({
|
|
106
|
-
method: z.literal('logging/setLevel'),
|
|
107
|
-
params: z.object({ level: LoggingLevelSchema }).loose(),
|
|
108
|
-
})
|
|
109
|
-
.loose();
|
|
110
101
|
server.server.setRequestHandler(SetLevelRequestSchema, (request) => {
|
|
111
102
|
setLogLevel(request.params.level);
|
|
112
103
|
return {};
|
|
@@ -151,7 +142,7 @@ function createShutdownHandler(server) {
|
|
|
151
142
|
};
|
|
152
143
|
}
|
|
153
144
|
function registerSignalHandlers(handler) {
|
|
154
|
-
for (const signal of
|
|
145
|
+
for (const signal of SHUTDOWN_SIGNALS) {
|
|
155
146
|
process.once(signal, () => {
|
|
156
147
|
handler(signal);
|
|
157
148
|
});
|
|
@@ -175,4 +166,3 @@ export async function startStdioServer() {
|
|
|
175
166
|
registerSignalHandlers(createShutdownHandler(server));
|
|
176
167
|
await connectStdioServer(server, transport);
|
|
177
168
|
}
|
|
178
|
-
//# sourceMappingURL=server.js.map
|
|
@@ -39,4 +39,3 @@ export declare function emitTaskStatusNotification(server: McpServer, task: Task
|
|
|
39
39
|
export declare function throwTaskNotFound(): never;
|
|
40
40
|
export declare function handleToolCallRequest(server: McpServer, request: ExtendedCallToolRequest, context: ToolCallContext): Promise<ServerResult>;
|
|
41
41
|
export {};
|
|
42
|
-
//# sourceMappingURL=execution.d.ts.map
|
package/dist/tasks/execution.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import { config } from '../lib/
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { isObject } from '../lib/
|
|
2
|
+
import { config } from '../lib/core.js';
|
|
3
|
+
import { logWarn, runWithRequestContext } from '../lib/core.js';
|
|
4
|
+
import { getErrorMessage, RESOURCE_NOT_FOUND_ERROR_CODE, } from '../lib/utils.js';
|
|
5
|
+
import { isObject } from '../lib/utils.js';
|
|
6
6
|
import { taskManager, } from './manager.js';
|
|
7
7
|
import { compact, tryReadToolStructuredError, } from './owner.js';
|
|
8
8
|
import { getTaskCapableTool, hasTaskCapableTool, } from './tool-registry.js';
|
|
@@ -82,8 +82,9 @@ export function emitTaskStatusNotification(server, task) {
|
|
|
82
82
|
return;
|
|
83
83
|
if (!server.isConnected())
|
|
84
84
|
return;
|
|
85
|
-
// NOTE: 'notifications/tasks/status' is
|
|
86
|
-
//
|
|
85
|
+
// NOTE: 'notifications/tasks/status' is a non-spec extension (not in MCP v2025-11-25).
|
|
86
|
+
// Gated by config.tasks.emitStatusNotifications (TASKS_STATUS_NOTIFICATIONS env var).
|
|
87
|
+
// Clients should NOT depend on this for interoperability — behavior may change.
|
|
87
88
|
void server.server
|
|
88
89
|
.notification({
|
|
89
90
|
method: 'notifications/tasks/status',
|
|
@@ -161,10 +162,7 @@ function updateWorkingTaskStatus(server, taskId, statusMessage) {
|
|
|
161
162
|
return;
|
|
162
163
|
if (current.statusMessage === statusMessage)
|
|
163
164
|
return;
|
|
164
|
-
|
|
165
|
-
const updated = taskManager.getTask(taskId);
|
|
166
|
-
if (updated)
|
|
167
|
-
emitTaskStatusNotification(server, updated);
|
|
165
|
+
updateTaskAndEmitStatus(server, taskId, { statusMessage });
|
|
168
166
|
}
|
|
169
167
|
function updateTaskAndEmitStatus(server, taskId, update) {
|
|
170
168
|
taskManager.updateTask(taskId, update);
|
|
@@ -192,6 +190,21 @@ function buildTaskFailureState(error) {
|
|
|
192
190
|
},
|
|
193
191
|
};
|
|
194
192
|
}
|
|
193
|
+
function resolveToolAndArgs(params) {
|
|
194
|
+
const tool = resolveTaskCapableTool(params.name);
|
|
195
|
+
const args = tool.parseArguments(params.arguments);
|
|
196
|
+
return { tool, args };
|
|
197
|
+
}
|
|
198
|
+
function buildTaskCompletionUpdate(result) {
|
|
199
|
+
const isToolError = isObject(result) && 'isError' in result && result.isError === true;
|
|
200
|
+
return {
|
|
201
|
+
status: isToolError ? 'failed' : 'completed',
|
|
202
|
+
statusMessage: isToolError
|
|
203
|
+
? (tryReadToolStructuredError(result) ?? 'Tool execution failed')
|
|
204
|
+
: 'Task completed successfully.',
|
|
205
|
+
result,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
195
208
|
async function runTaskToolExecution(params) {
|
|
196
209
|
const { server, taskId, args, tool, meta, sessionId, sendNotification } = params;
|
|
197
210
|
return runWithRequestContext({
|
|
@@ -211,14 +224,7 @@ async function runTaskToolExecution(params) {
|
|
|
211
224
|
updateWorkingTaskStatus(server, taskId, message);
|
|
212
225
|
},
|
|
213
226
|
});
|
|
214
|
-
|
|
215
|
-
updateTaskAndEmitStatus(server, taskId, {
|
|
216
|
-
status: isToolError ? 'failed' : 'completed',
|
|
217
|
-
statusMessage: isToolError
|
|
218
|
-
? (tryReadToolStructuredError(result) ?? 'Tool execution failed')
|
|
219
|
-
: 'Task completed successfully.',
|
|
220
|
-
result,
|
|
221
|
-
});
|
|
227
|
+
updateTaskAndEmitStatus(server, taskId, buildTaskCompletionUpdate(result));
|
|
222
228
|
}
|
|
223
229
|
catch (error) {
|
|
224
230
|
const failure = buildTaskFailureState(error);
|
|
@@ -234,13 +240,12 @@ async function runTaskToolExecution(params) {
|
|
|
234
240
|
});
|
|
235
241
|
}
|
|
236
242
|
function handleTaskToolCall(server, params, context) {
|
|
237
|
-
const tool =
|
|
238
|
-
const validArgs = tool.parseArguments(params.arguments);
|
|
243
|
+
const { tool, args } = resolveToolAndArgs(params);
|
|
239
244
|
const task = taskManager.createTask(params.task?.ttl !== undefined ? { ttl: params.task.ttl } : undefined, 'Task started', context.ownerKey);
|
|
240
245
|
void runTaskToolExecution({
|
|
241
246
|
server,
|
|
242
247
|
taskId: task.taskId,
|
|
243
|
-
args
|
|
248
|
+
args,
|
|
244
249
|
tool,
|
|
245
250
|
...compact({
|
|
246
251
|
meta: params._meta,
|
|
@@ -251,8 +256,7 @@ function handleTaskToolCall(server, params, context) {
|
|
|
251
256
|
return buildCreateTaskResult(toTaskSummary(task));
|
|
252
257
|
}
|
|
253
258
|
async function handleDirectToolCall(params, context) {
|
|
254
|
-
const tool =
|
|
255
|
-
const args = tool.parseArguments(params.arguments);
|
|
259
|
+
const { tool, args } = resolveToolAndArgs(params);
|
|
256
260
|
const extra = compact({
|
|
257
261
|
signal: context.signal,
|
|
258
262
|
requestId: context.requestId,
|
|
@@ -271,4 +275,3 @@ export async function handleToolCallRequest(server, request, context) {
|
|
|
271
275
|
}
|
|
272
276
|
return handleDirectToolCall(params, context);
|
|
273
277
|
}
|
|
274
|
-
//# sourceMappingURL=execution.js.map
|
package/dist/tasks/manager.d.ts
CHANGED
|
@@ -40,10 +40,13 @@ declare class TaskManager {
|
|
|
40
40
|
private tasks;
|
|
41
41
|
private ownerCounts;
|
|
42
42
|
private waiters;
|
|
43
|
-
|
|
44
|
-
private
|
|
43
|
+
private cleanupInterval;
|
|
44
|
+
private ensureCleanupLoop;
|
|
45
|
+
private stopCleanupLoop;
|
|
45
46
|
private removeExpiredTasks;
|
|
46
47
|
private removeTask;
|
|
48
|
+
private applyTaskUpdate;
|
|
49
|
+
private cancelActiveTask;
|
|
47
50
|
private releaseOwnerCount;
|
|
48
51
|
private countTasksForOwner;
|
|
49
52
|
private incrementOwnerCount;
|
|
@@ -64,11 +67,13 @@ declare class TaskManager {
|
|
|
64
67
|
tasks: TaskState[];
|
|
65
68
|
nextCursor?: string;
|
|
66
69
|
};
|
|
70
|
+
private resolveAnchorTaskId;
|
|
67
71
|
private addWaiter;
|
|
68
72
|
private removeWaiter;
|
|
69
73
|
waitForTerminalTask(taskId: string, ownerKey: string, signal?: AbortSignal): Promise<TaskState | undefined>;
|
|
70
74
|
private notifyWaiters;
|
|
71
75
|
private isExpired;
|
|
76
|
+
private maybeUpdateLastUpdatedAt;
|
|
72
77
|
shrinkTtlAfterDelivery(taskId: string): void;
|
|
73
78
|
private encodeCursor;
|
|
74
79
|
private decodeCursor;
|
|
@@ -76,4 +81,3 @@ declare class TaskManager {
|
|
|
76
81
|
}
|
|
77
82
|
export declare const taskManager: TaskManager;
|
|
78
83
|
export {};
|
|
79
|
-
//# sourceMappingURL=manager.d.ts.map
|
package/dist/tasks/manager.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
2
|
import { Buffer } from 'node:buffer';
|
|
4
3
|
import { randomUUID } from 'node:crypto';
|
|
5
4
|
import { setInterval } from 'node:timers';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { config } from '../lib/core.js';
|
|
7
|
+
import { RESOURCE_NOT_FOUND_ERROR_CODE, toError } from '../lib/utils.js';
|
|
8
|
+
import { createUnrefTimeout } from '../lib/utils.js';
|
|
9
9
|
const DEFAULT_TTL_MS = 60_000;
|
|
10
10
|
const MIN_TTL_MS = 1_000;
|
|
11
11
|
const MAX_TTL_MS = 86_400_000;
|
|
@@ -32,14 +32,23 @@ class TaskManager {
|
|
|
32
32
|
tasks = new Map();
|
|
33
33
|
ownerCounts = new Map();
|
|
34
34
|
waiters = new Map();
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
cleanupInterval = null;
|
|
36
|
+
ensureCleanupLoop() {
|
|
37
|
+
if (this.cleanupInterval)
|
|
38
|
+
return;
|
|
39
|
+
this.cleanupInterval = setInterval(() => {
|
|
40
40
|
this.removeExpiredTasks();
|
|
41
|
+
if (this.tasks.size === 0) {
|
|
42
|
+
this.stopCleanupLoop();
|
|
43
|
+
}
|
|
41
44
|
}, CLEANUP_INTERVAL_MS);
|
|
42
|
-
|
|
45
|
+
this.cleanupInterval.unref();
|
|
46
|
+
}
|
|
47
|
+
stopCleanupLoop() {
|
|
48
|
+
if (!this.cleanupInterval)
|
|
49
|
+
return;
|
|
50
|
+
clearInterval(this.cleanupInterval);
|
|
51
|
+
this.cleanupInterval = null;
|
|
43
52
|
}
|
|
44
53
|
removeExpiredTasks() {
|
|
45
54
|
const now = Date.now();
|
|
@@ -57,6 +66,18 @@ class TaskManager {
|
|
|
57
66
|
this.releaseOwnerCount(task);
|
|
58
67
|
return true;
|
|
59
68
|
}
|
|
69
|
+
applyTaskUpdate(task, updates) {
|
|
70
|
+
Object.assign(task, updates);
|
|
71
|
+
task.lastUpdatedAt = new Date().toISOString();
|
|
72
|
+
}
|
|
73
|
+
cancelActiveTask(task, statusMessage) {
|
|
74
|
+
this.applyTaskUpdate(task, {
|
|
75
|
+
status: 'cancelled',
|
|
76
|
+
statusMessage,
|
|
77
|
+
});
|
|
78
|
+
this.notifyWaiters(task);
|
|
79
|
+
this.releaseOwnerCount(task);
|
|
80
|
+
}
|
|
60
81
|
releaseOwnerCount(task) {
|
|
61
82
|
const internal = task;
|
|
62
83
|
if (internal._ownerCounted === false)
|
|
@@ -109,6 +130,7 @@ class TaskManager {
|
|
|
109
130
|
};
|
|
110
131
|
this.tasks.set(task.taskId, task);
|
|
111
132
|
this.incrementOwnerCount(ownerKey);
|
|
133
|
+
this.ensureCleanupLoop();
|
|
112
134
|
return task;
|
|
113
135
|
}
|
|
114
136
|
lookupActiveTask(taskId, ownerKey) {
|
|
@@ -132,22 +154,17 @@ class TaskManager {
|
|
|
132
154
|
return;
|
|
133
155
|
if (isTerminalStatus(task.status))
|
|
134
156
|
return;
|
|
135
|
-
|
|
136
|
-
task.lastUpdatedAt = new Date().toISOString();
|
|
157
|
+
this.applyTaskUpdate(task, updates);
|
|
137
158
|
this.notifyWaiters(task);
|
|
138
159
|
}
|
|
139
160
|
cancelTask(taskId, ownerKey) {
|
|
140
|
-
const task = this.
|
|
161
|
+
const task = this.lookupActiveTask(taskId, ownerKey);
|
|
141
162
|
if (!task)
|
|
142
163
|
return undefined;
|
|
143
164
|
if (isTerminalStatus(task.status)) {
|
|
144
165
|
throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task: already in terminal status '${task.status}'`);
|
|
145
166
|
}
|
|
146
|
-
this.
|
|
147
|
-
status: 'cancelled',
|
|
148
|
-
statusMessage: 'The task was cancelled by request.',
|
|
149
|
-
});
|
|
150
|
-
this.releaseOwnerCount(task);
|
|
167
|
+
this.cancelActiveTask(task, 'The task was cancelled by request.');
|
|
151
168
|
return this.tasks.get(taskId);
|
|
152
169
|
}
|
|
153
170
|
cancelTasksByOwner(ownerKey, statusMessage = 'The task was cancelled because its owner is no longer active.') {
|
|
@@ -159,11 +176,7 @@ class TaskManager {
|
|
|
159
176
|
continue;
|
|
160
177
|
if (isTerminalStatus(task.status))
|
|
161
178
|
continue;
|
|
162
|
-
this.
|
|
163
|
-
status: 'cancelled',
|
|
164
|
-
statusMessage,
|
|
165
|
-
});
|
|
166
|
-
this.releaseOwnerCount(task);
|
|
179
|
+
this.cancelActiveTask(task, statusMessage);
|
|
167
180
|
cancelled.push(task);
|
|
168
181
|
}
|
|
169
182
|
return cancelled;
|
|
@@ -202,14 +215,7 @@ class TaskManager {
|
|
|
202
215
|
listTasks(options) {
|
|
203
216
|
const { ownerKey, cursor, limit } = options;
|
|
204
217
|
const pageSize = limit && limit > 0 ? limit : DEFAULT_PAGE_SIZE;
|
|
205
|
-
|
|
206
|
-
if (cursor !== undefined) {
|
|
207
|
-
const decoded = this.decodeCursor(cursor);
|
|
208
|
-
if (decoded === null) {
|
|
209
|
-
throw new McpError(ErrorCode.InvalidParams, 'Invalid cursor');
|
|
210
|
-
}
|
|
211
|
-
({ anchorTaskId } = decoded);
|
|
212
|
-
}
|
|
218
|
+
const anchorTaskId = this.resolveAnchorTaskId(cursor);
|
|
213
219
|
const page = this.collectPage(ownerKey, anchorTaskId, pageSize);
|
|
214
220
|
const hasMore = page.length > pageSize;
|
|
215
221
|
if (hasMore) {
|
|
@@ -218,6 +224,15 @@ class TaskManager {
|
|
|
218
224
|
const nextCursor = this.resolveNextCursor(page, hasMore);
|
|
219
225
|
return nextCursor ? { tasks: page, nextCursor } : { tasks: page };
|
|
220
226
|
}
|
|
227
|
+
resolveAnchorTaskId(cursor) {
|
|
228
|
+
if (cursor === undefined)
|
|
229
|
+
return null;
|
|
230
|
+
const decoded = this.decodeCursor(cursor);
|
|
231
|
+
if (decoded === null) {
|
|
232
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid cursor');
|
|
233
|
+
}
|
|
234
|
+
return decoded.anchorTaskId;
|
|
235
|
+
}
|
|
221
236
|
addWaiter(taskId, waiter) {
|
|
222
237
|
let set = this.waiters.get(taskId);
|
|
223
238
|
if (!set) {
|
|
@@ -305,7 +320,9 @@ class TaskManager {
|
|
|
305
320
|
cleanup();
|
|
306
321
|
this.removeWaiter(taskId, waiter);
|
|
307
322
|
this.removeTask(taskId);
|
|
308
|
-
|
|
323
|
+
rejectInContext(new McpError(RESOURCE_NOT_FOUND_ERROR_CODE, 'Task expired', {
|
|
324
|
+
taskId,
|
|
325
|
+
}));
|
|
309
326
|
});
|
|
310
327
|
})
|
|
311
328
|
.catch(rejectInContext);
|
|
@@ -324,6 +341,9 @@ class TaskManager {
|
|
|
324
341
|
isExpired(task, now = Date.now()) {
|
|
325
342
|
return now - task._createdAtMs > task.ttl;
|
|
326
343
|
}
|
|
344
|
+
maybeUpdateLastUpdatedAt(task) {
|
|
345
|
+
task.lastUpdatedAt = new Date().toISOString();
|
|
346
|
+
}
|
|
327
347
|
shrinkTtlAfterDelivery(taskId) {
|
|
328
348
|
const task = this.tasks.get(taskId);
|
|
329
349
|
if (!task)
|
|
@@ -334,7 +354,7 @@ class TaskManager {
|
|
|
334
354
|
const newTtl = elapsed + RESULT_DELIVERY_GRACE_MS;
|
|
335
355
|
if (newTtl < task.ttl) {
|
|
336
356
|
task.ttl = newTtl;
|
|
337
|
-
|
|
357
|
+
this.maybeUpdateLastUpdatedAt(task);
|
|
338
358
|
}
|
|
339
359
|
}
|
|
340
360
|
encodeCursor(taskId) {
|
|
@@ -381,4 +401,3 @@ function isValidBase64UrlCursor(cursor) {
|
|
|
381
401
|
return cursor.length % 4 !== 1;
|
|
382
402
|
}
|
|
383
403
|
export const taskManager = new TaskManager();
|
|
384
|
-
//# sourceMappingURL=manager.js.map
|
package/dist/tasks/owner.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ServerResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import type { ProgressNotification } from '../lib/
|
|
2
|
+
import type { ProgressNotification } from '../lib/mcp-tools.js';
|
|
3
3
|
interface HandlerExtra {
|
|
4
4
|
sessionId?: string;
|
|
5
5
|
authInfo?: {
|
|
@@ -30,4 +30,3 @@ export declare function resolveToolCallContext(extra?: HandlerExtra): ToolCallCo
|
|
|
30
30
|
export declare function isServerResult(value: unknown): value is ServerResult;
|
|
31
31
|
export declare function tryReadToolStructuredError(value: unknown): string | undefined;
|
|
32
32
|
export {};
|
|
33
|
-
//# sourceMappingURL=owner.d.ts.map
|
package/dist/tasks/owner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { isObject } from '../lib/
|
|
2
|
+
import { isObject } from '../lib/utils.js';
|
|
3
3
|
export function compact(obj) {
|
|
4
4
|
const result = {};
|
|
5
5
|
for (const key of Object.keys(obj)) {
|
|
@@ -90,4 +90,3 @@ export function tryReadToolStructuredError(value) {
|
|
|
90
90
|
return undefined;
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
//# sourceMappingURL=owner.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ServerResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import type { ToolHandlerExtra } from '../lib/
|
|
2
|
+
import type { ToolHandlerExtra } from '../lib/mcp-tools.js';
|
|
3
3
|
export interface TaskCapableToolDescriptor<TArgs = unknown> {
|
|
4
4
|
name: string;
|
|
5
5
|
parseArguments: (args: unknown) => TArgs;
|
|
@@ -9,4 +9,3 @@ export declare function registerTaskCapableTool<TArgs>(descriptor: TaskCapableTo
|
|
|
9
9
|
export declare function unregisterTaskCapableTool(name: string): void;
|
|
10
10
|
export declare function getTaskCapableTool(name: string): TaskCapableToolDescriptor | undefined;
|
|
11
11
|
export declare function hasTaskCapableTool(name: string): boolean;
|
|
12
|
-
//# sourceMappingURL=tool-registry.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { ContentBlock } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
-
import { type ToolHandlerExtra } from '../lib/
|
|
3
|
+
import { type ToolHandlerExtra } from '../lib/mcp-tools.js';
|
|
4
4
|
interface FetchUrlInput {
|
|
5
5
|
url: string;
|
|
6
6
|
skipNoiseRemoval?: boolean | undefined;
|
|
@@ -26,4 +26,3 @@ export declare function fetchUrlToolHandler(input: FetchUrlInput, extra?: ToolHa
|
|
|
26
26
|
export declare function withRequestContextIfMissing<TParams, TResult, TExtra = unknown>(handler: (params: TParams, extra?: TExtra) => Promise<TResult>): (params: TParams, extra?: TExtra) => Promise<TResult>;
|
|
27
27
|
export declare function registerTools(server: McpServer): void;
|
|
28
28
|
export {};
|
|
29
|
-
//# sourceMappingURL=fetch-url.d.ts.map
|
package/dist/tools/fetch-url.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import * as cache from '../lib/
|
|
5
|
-
import { config } from '../lib/
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { isObject } from '../lib/type-guards.js';
|
|
4
|
+
import * as cache from '../lib/core.js';
|
|
5
|
+
import { config } from '../lib/core.js';
|
|
6
|
+
import { getRequestId, logDebug, logError, logWarn, runWithRequestContext, } from '../lib/core.js';
|
|
7
|
+
import { generateSafeFilename } from '../lib/http.js';
|
|
8
|
+
import { handleToolError } from '../lib/mcp-tools.js';
|
|
9
|
+
import { appendTruncationMarker, markdownTransform, parseCachedMarkdownResult, performSharedFetch, readNestedRecord, readString, serializeMarkdownResult, TRUNCATION_MARKER, withSignal, } from '../lib/mcp-tools.js';
|
|
10
|
+
import { createProgressReporter, } from '../lib/mcp-tools.js';
|
|
11
|
+
import { isAbortError, isObject, toError } from '../lib/utils.js';
|
|
13
12
|
import { fetchUrlInputSchema } from '../schemas/inputs.js';
|
|
14
13
|
import { fetchUrlOutputSchema } from '../schemas/outputs.js';
|
|
15
14
|
import { registerTaskCapableTool, unregisterTaskCapableTool, } from '../tasks/tool-registry.js';
|
|
@@ -83,11 +82,11 @@ function appendIfPresent(items, value) {
|
|
|
83
82
|
/* -------------------------------------------------------------------------------------------------
|
|
84
83
|
* Tool abort signal
|
|
85
84
|
* ------------------------------------------------------------------------------------------------- */
|
|
85
|
+
const HARD_TOOL_TIMEOUT_MS = 300_000;
|
|
86
86
|
function buildToolAbortSignal(extraSignal) {
|
|
87
87
|
const { timeoutMs } = config.tools;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
88
|
+
const effectiveTimeout = timeoutMs > 0 ? timeoutMs : HARD_TOOL_TIMEOUT_MS;
|
|
89
|
+
const timeoutSignal = AbortSignal.timeout(effectiveTimeout);
|
|
91
90
|
if (!extraSignal)
|
|
92
91
|
return timeoutSignal;
|
|
93
92
|
return AbortSignal.any([extraSignal, timeoutSignal]);
|
|
@@ -107,11 +106,11 @@ const METADATA_FIELD_LIMITS = {
|
|
|
107
106
|
};
|
|
108
107
|
function truncateMetadata(metadata) {
|
|
109
108
|
const result = { ...metadata };
|
|
110
|
-
for (const [
|
|
111
|
-
const
|
|
112
|
-
const value = result[
|
|
109
|
+
for (const [field, limit] of Object.entries(METADATA_FIELD_LIMITS)) {
|
|
110
|
+
const key = field;
|
|
111
|
+
const value = result[key];
|
|
113
112
|
if (typeof value === 'string' && value.length > limit) {
|
|
114
|
-
result[
|
|
113
|
+
result[key] = value.slice(0, limit);
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
return result;
|
|
@@ -228,11 +227,7 @@ function getUrlContext(urlStr) {
|
|
|
228
227
|
}
|
|
229
228
|
}
|
|
230
229
|
async function fetchPipeline(url, signal, progress, skipNoiseRemoval, forceRefresh, maxInlineChars) {
|
|
231
|
-
const
|
|
232
|
-
if (!progress)
|
|
233
|
-
return;
|
|
234
|
-
progress.report(step, message);
|
|
235
|
-
};
|
|
230
|
+
const contextStr = getUrlContext(url);
|
|
236
231
|
return performSharedFetch({
|
|
237
232
|
url,
|
|
238
233
|
...withSignal(signal),
|
|
@@ -240,33 +235,43 @@ async function fetchPipeline(url, signal, progress, skipNoiseRemoval, forceRefre
|
|
|
240
235
|
...(forceRefresh ? { forceRefresh: true } : {}),
|
|
241
236
|
...(maxInlineChars !== undefined ? { maxInlineChars } : {}),
|
|
242
237
|
transform: async ({ buffer, encoding, truncated }, normalizedUrl) => {
|
|
243
|
-
|
|
244
|
-
reportProgress(2, `fetch-url: ${contextStr} [converting to Markdown]`);
|
|
238
|
+
reportFetchProgress(progress, 2, contextStr, 'converting to Markdown');
|
|
245
239
|
return markdownTransform({ buffer, encoding, ...(truncated ? { truncated } : {}) }, normalizedUrl, signal, skipNoiseRemoval);
|
|
246
240
|
},
|
|
247
241
|
serialize: serializeMarkdownResult,
|
|
248
242
|
deserialize: parseCachedMarkdownResult,
|
|
249
243
|
});
|
|
250
244
|
}
|
|
245
|
+
function buildFetchProgressMessage(context, state) {
|
|
246
|
+
if (state === 'completed' || state === 'cancelled' || state === 'failed') {
|
|
247
|
+
return `fetch-url: ${context} • ${state}`;
|
|
248
|
+
}
|
|
249
|
+
return `fetch-url: ${context} [${state}]`;
|
|
250
|
+
}
|
|
251
|
+
function reportFetchProgress(progress, step, context, state) {
|
|
252
|
+
if (!progress)
|
|
253
|
+
return;
|
|
254
|
+
progress.report(step, buildFetchProgressMessage(context, state));
|
|
255
|
+
}
|
|
251
256
|
async function executeFetch(input, extra) {
|
|
252
257
|
const { url } = input;
|
|
253
258
|
const signal = buildToolAbortSignal(extra?.signal);
|
|
254
259
|
const progress = createProgressReporter(extra);
|
|
255
260
|
const contextStr = getUrlContext(url);
|
|
256
|
-
progress
|
|
261
|
+
reportFetchProgress(progress, 0, contextStr, 'starting');
|
|
257
262
|
logDebug('Fetching URL', { url });
|
|
258
263
|
try {
|
|
259
|
-
progress
|
|
264
|
+
reportFetchProgress(progress, 1, contextStr, 'fetching HTML');
|
|
260
265
|
const { pipeline, inlineResult } = await fetchPipeline(url, signal, progress, input.skipNoiseRemoval, input.forceRefresh, input.maxInlineChars);
|
|
261
266
|
if (pipeline.fromCache) {
|
|
262
|
-
progress
|
|
267
|
+
reportFetchProgress(progress, 3, contextStr, 'loaded from cache');
|
|
263
268
|
}
|
|
264
|
-
progress
|
|
269
|
+
reportFetchProgress(progress, 4, contextStr, 'completed');
|
|
265
270
|
return buildResponse(pipeline, inlineResult, url);
|
|
266
271
|
}
|
|
267
272
|
catch (error) {
|
|
268
273
|
const isAbort = isAbortError(error);
|
|
269
|
-
progress
|
|
274
|
+
reportFetchProgress(progress, 4, contextStr, isAbort ? 'cancelled' : 'failed');
|
|
270
275
|
throw error;
|
|
271
276
|
}
|
|
272
277
|
}
|
|
@@ -359,7 +364,11 @@ export function registerTools(server) {
|
|
|
359
364
|
parseArguments: (args) => {
|
|
360
365
|
const parsed = fetchUrlInputSchema.safeParse(args);
|
|
361
366
|
if (!parsed.success) {
|
|
362
|
-
|
|
367
|
+
const flat = z.flattenError(parsed.error);
|
|
368
|
+
const details = Object.entries(flat.fieldErrors)
|
|
369
|
+
.map(([k, v]) => `${k}: ${v.join(', ')}`)
|
|
370
|
+
.join('; ') || flat.formErrors.join('; ');
|
|
371
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${FETCH_URL_TOOL_NAME}: ${details}`);
|
|
363
372
|
}
|
|
364
373
|
return parsed.data;
|
|
365
374
|
},
|
|
@@ -378,4 +387,3 @@ export function registerTools(server) {
|
|
|
378
387
|
// registered tool type includes this field.
|
|
379
388
|
applyRegisteredToolExecutionMetadata(registeredTool, TOOL_DEFINITION.execution);
|
|
380
389
|
}
|
|
381
|
-
//# sourceMappingURL=fetch-url.js.map
|
package/dist/tools/index.d.ts
CHANGED
package/dist/tools/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function translateHtmlFragmentToMarkdown(html: string): string;
|