@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.
- package/dist/http/auth.d.ts +2 -2
- package/dist/http/auth.d.ts.map +1 -1
- package/dist/http/auth.js +15 -16
- package/dist/http/index.d.ts +6 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +5 -0
- package/dist/http/native.d.ts +73 -0
- package/dist/http/native.d.ts.map +1 -1
- package/dist/http/native.js +585 -62
- 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 +5 -6
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -8
- package/dist/lib/config.js +2 -2
- package/dist/lib/core.d.ts +56 -4
- package/dist/lib/core.d.ts.map +1 -1
- package/dist/lib/core.js +162 -11
- package/dist/lib/error/classes.d.ts +19 -0
- package/dist/lib/error/classes.d.ts.map +1 -0
- package/dist/lib/error/classes.js +107 -0
- package/dist/lib/error/classify.d.ts +4 -0
- package/dist/lib/error/classify.d.ts.map +1 -0
- package/dist/lib/error/classify.js +154 -0
- package/dist/lib/error/codes.d.ts +23 -0
- package/dist/lib/error/codes.d.ts.map +1 -0
- package/dist/lib/error/codes.js +22 -0
- package/dist/lib/error/index.d.ts +6 -0
- package/dist/lib/error/index.d.ts.map +1 -0
- package/dist/lib/error/index.js +5 -0
- package/dist/lib/{error-messages.d.ts → error/messages.d.ts} +2 -2
- package/dist/lib/error/messages.d.ts.map +1 -0
- package/dist/lib/{error-messages.js → error/messages.js} +13 -13
- package/dist/lib/{tool-errors.d.ts → error/payload.d.ts} +7 -13
- package/dist/lib/error/payload.d.ts.map +1 -0
- package/dist/lib/error/payload.js +108 -0
- package/dist/lib/mcp-interop.d.ts +1 -0
- package/dist/lib/mcp-interop.d.ts.map +1 -1
- package/dist/lib/mcp-interop.js +17 -9
- package/dist/lib/net/http.d.ts.map +1 -0
- package/dist/lib/{http.js → net/http.js} +11 -14
- package/dist/lib/net/index.d.ts +4 -0
- package/dist/lib/net/index.d.ts.map +1 -0
- package/dist/lib/net/index.js +3 -0
- package/dist/lib/{fetch-pipeline.d.ts → net/pipeline.d.ts} +3 -3
- package/dist/lib/net/pipeline.d.ts.map +1 -0
- package/dist/lib/{fetch-pipeline.js → net/pipeline.js} +7 -9
- package/dist/lib/{url.d.ts → net/url.d.ts} +2 -2
- package/dist/lib/net/url.d.ts.map +1 -0
- package/dist/lib/{url.js → net/url.js} +6 -8
- package/dist/lib/utils.d.ts +3 -18
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +33 -105
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +6 -3
- package/dist/schemas.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +12 -14
- package/dist/tasks/index.d.ts +2 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +1 -0
- package/dist/tasks/manager.d.ts +123 -1
- package/dist/tasks/manager.d.ts.map +1 -1
- package/dist/tasks/manager.js +753 -18
- package/dist/tools/{fetch-url.d.ts → index.d.ts} +4 -5
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/{fetch-url.js → index.js} +14 -31
- package/dist/transform/index.d.ts +279 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/index.js +5234 -0
- package/package.json +2 -2
- package/dist/cli.d.ts +0 -19
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -65
- package/dist/http/health.d.ts +0 -8
- package/dist/http/health.d.ts.map +0 -1
- package/dist/http/health.js +0 -152
- package/dist/http/helpers.d.ts +0 -68
- package/dist/http/helpers.d.ts.map +0 -1
- package/dist/http/helpers.js +0 -404
- package/dist/lib/error-codes.d.ts +0 -11
- package/dist/lib/error-codes.d.ts.map +0 -1
- package/dist/lib/error-codes.js +0 -15
- package/dist/lib/error-messages.d.ts.map +0 -1
- package/dist/lib/fetch-pipeline.d.ts.map +0 -1
- package/dist/lib/http.d.ts.map +0 -1
- package/dist/lib/logger-names.d.ts +0 -14
- package/dist/lib/logger-names.d.ts.map +0 -1
- package/dist/lib/logger-names.js +0 -13
- package/dist/lib/session.d.ts +0 -44
- package/dist/lib/session.d.ts.map +0 -1
- package/dist/lib/session.js +0 -137
- package/dist/lib/tool-errors.d.ts.map +0 -1
- package/dist/lib/tool-errors.js +0 -252
- package/dist/lib/url.d.ts.map +0 -1
- package/dist/lib/zod.d.ts +0 -3
- package/dist/lib/zod.d.ts.map +0 -1
- package/dist/lib/zod.js +0 -27
- package/dist/tasks/call-contract.d.ts +0 -25
- package/dist/tasks/call-contract.d.ts.map +0 -1
- package/dist/tasks/call-contract.js +0 -59
- package/dist/tasks/execution.d.ts +0 -16
- package/dist/tasks/execution.d.ts.map +0 -1
- package/dist/tasks/execution.js +0 -241
- package/dist/tasks/handlers.d.ts +0 -11
- package/dist/tasks/handlers.d.ts.map +0 -1
- package/dist/tasks/handlers.js +0 -157
- package/dist/tasks/owner.d.ts +0 -43
- package/dist/tasks/owner.d.ts.map +0 -1
- package/dist/tasks/owner.js +0 -144
- package/dist/tasks/registry.d.ts +0 -20
- package/dist/tasks/registry.d.ts.map +0 -1
- package/dist/tasks/registry.js +0 -40
- package/dist/tasks/waiters.d.ts +0 -27
- package/dist/tasks/waiters.d.ts.map +0 -1
- package/dist/tasks/waiters.js +0 -114
- package/dist/tools/fetch-url.d.ts.map +0 -1
- package/dist/transform/dom-prep.d.ts +0 -16
- package/dist/transform/dom-prep.d.ts.map +0 -1
- package/dist/transform/dom-prep.js +0 -1287
- package/dist/transform/html-translators.d.ts +0 -5
- package/dist/transform/html-translators.d.ts.map +0 -1
- package/dist/transform/html-translators.js +0 -697
- package/dist/transform/markdown-cleanup.d.ts +0 -10
- package/dist/transform/markdown-cleanup.d.ts.map +0 -1
- package/dist/transform/markdown-cleanup.js +0 -542
- package/dist/transform/metadata.d.ts +0 -18
- package/dist/transform/metadata.d.ts.map +0 -1
- package/dist/transform/metadata.js +0 -462
- package/dist/transform/next-flight.d.ts +0 -2
- package/dist/transform/next-flight.d.ts.map +0 -1
- package/dist/transform/next-flight.js +0 -374
- package/dist/transform/shared.d.ts +0 -8
- package/dist/transform/shared.d.ts.map +0 -1
- package/dist/transform/shared.js +0 -137
- package/dist/transform/transform.d.ts +0 -38
- package/dist/transform/transform.d.ts.map +0 -1
- package/dist/transform/transform.js +0 -1041
- package/dist/transform/types.d.ts +0 -124
- package/dist/transform/types.d.ts.map +0 -1
- package/dist/transform/types.js +0 -5
- package/dist/transform/worker-pool.d.ts +0 -76
- package/dist/transform/worker-pool.d.ts.map +0 -1
- package/dist/transform/worker-pool.js +0 -725
- /package/dist/lib/{http.d.ts → net/http.d.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/http/rate-limit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/http/rate-limit.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,aAAa,CAAC;AAY5D,UAAU,eAAe;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC;IACpC,IAAI,IAAI,IAAI,CAAC;CACd;AA+FD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,eAAe,GACvB,oBAAoB,CAGtB"}
|
package/dist/http/rate-limit.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { logWarn } from '../lib/core.js';
|
|
2
|
-
import {
|
|
3
|
-
import { isAbortError } from '../lib/utils.js';
|
|
1
|
+
import { Loggers, logWarn } from '../lib/core.js';
|
|
2
|
+
import { isAbortError } from '../lib/error/index.js';
|
|
4
3
|
import { startAbortableIntervalLoop } from '../lib/utils.js';
|
|
5
|
-
import { sendJson } from './
|
|
4
|
+
import { sendJson } from './native.js';
|
|
6
5
|
// ---------------------------------------------------------------------------
|
|
7
6
|
// Rate limiter
|
|
8
7
|
// ---------------------------------------------------------------------------
|
|
@@ -22,7 +21,7 @@ class RateLimiter {
|
|
|
22
21
|
},
|
|
23
22
|
onError: (err) => {
|
|
24
23
|
if (!isAbortError(err)) {
|
|
25
|
-
logWarn('Rate limit cleanup failed', { error: err }, LOG_RATE_LIMIT);
|
|
24
|
+
logWarn('Rate limit cleanup failed', { error: err }, Loggers.LOG_RATE_LIMIT);
|
|
26
25
|
}
|
|
27
26
|
},
|
|
28
27
|
});
|
|
@@ -72,7 +71,7 @@ class RateLimiter {
|
|
|
72
71
|
this.store.set(key, entry);
|
|
73
72
|
}
|
|
74
73
|
if (entry.count > this.options.maxRequests) {
|
|
75
|
-
logWarn('Rate limit exceeded', { ip: key }, LOG_RATE_LIMIT);
|
|
74
|
+
logWarn('Rate limit exceeded', { ip: key }, Loggers.LOG_RATE_LIMIT);
|
|
76
75
|
const retryAfter = Math.max(1, Math.ceil((entry.resetTime - now) / 1000));
|
|
77
76
|
ctx.res.setHeader('Retry-After', String(retryAfter));
|
|
78
77
|
sendJson(ctx.res, 429, { error: 'Rate limit exceeded', retryAfter });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
interface CliValues {
|
|
3
|
+
readonly stdio: boolean;
|
|
4
|
+
readonly http: boolean;
|
|
5
|
+
readonly help: boolean;
|
|
6
|
+
readonly version: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface CliParseSuccess {
|
|
9
|
+
readonly ok: true;
|
|
10
|
+
readonly values: CliValues;
|
|
11
|
+
}
|
|
12
|
+
interface CliParseFailure {
|
|
13
|
+
readonly ok: false;
|
|
14
|
+
readonly message: string;
|
|
15
|
+
}
|
|
16
|
+
type CliParseResult = CliParseSuccess | CliParseFailure;
|
|
17
|
+
export declare function renderCliUsage(): string;
|
|
18
|
+
export declare function parseCliArgs(args: readonly string[]): CliParseResult;
|
|
2
19
|
export {};
|
|
3
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAUA,UAAU,SAAS;IACjB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;CAC5B;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,KAAK,cAAc,GAAG,eAAe,GAAG,eAAe,CAAC;AA2CxD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,CA2BpE"}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,73 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import process from 'node:process';
|
|
3
|
-
import {
|
|
4
|
-
import { logError } from './lib/core.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { parseCliArgs, renderCliUsage } from './cli.js';
|
|
8
|
-
import { startHttpServer } from './http/native.js';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import { logError, Loggers, serverVersion } from './lib/core.js';
|
|
5
|
+
import { getErrorMessage, toError } from './lib/error/index.js';
|
|
6
|
+
import { startHttpServer } from './http/index.js';
|
|
9
7
|
import { startStdioServer } from './server.js';
|
|
8
|
+
const usageLines = [
|
|
9
|
+
'Fetch URL MCP server',
|
|
10
|
+
'',
|
|
11
|
+
'Usage:',
|
|
12
|
+
' fetch-url-mcp [--stdio|-s | --http] [--help|-h] [--version|-v]',
|
|
13
|
+
'',
|
|
14
|
+
'Options:',
|
|
15
|
+
' --stdio, -s Run in stdio mode (default).',
|
|
16
|
+
' --http Run in Streamable HTTP mode.',
|
|
17
|
+
' --help, -h Show this help message.',
|
|
18
|
+
' --version, -v Show server version.',
|
|
19
|
+
'',
|
|
20
|
+
];
|
|
21
|
+
const optionSchema = {
|
|
22
|
+
stdio: { type: 'boolean', short: 's', default: false },
|
|
23
|
+
http: { type: 'boolean', default: false },
|
|
24
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
25
|
+
version: { type: 'boolean', short: 'v', default: false },
|
|
26
|
+
};
|
|
27
|
+
function toBoolean(value) {
|
|
28
|
+
return value === true;
|
|
29
|
+
}
|
|
30
|
+
function readCliFlag(values, key) {
|
|
31
|
+
return toBoolean(values[key]);
|
|
32
|
+
}
|
|
33
|
+
function buildCliValues(values) {
|
|
34
|
+
return {
|
|
35
|
+
stdio: readCliFlag(values, 'stdio'),
|
|
36
|
+
http: readCliFlag(values, 'http'),
|
|
37
|
+
help: readCliFlag(values, 'help'),
|
|
38
|
+
version: readCliFlag(values, 'version'),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function renderCliUsage() {
|
|
42
|
+
return `${usageLines.join('\n')}\n`;
|
|
43
|
+
}
|
|
44
|
+
export function parseCliArgs(args) {
|
|
45
|
+
try {
|
|
46
|
+
const { values } = parseArgs({
|
|
47
|
+
args: [...args],
|
|
48
|
+
options: optionSchema,
|
|
49
|
+
strict: true,
|
|
50
|
+
allowPositionals: false,
|
|
51
|
+
});
|
|
52
|
+
const cliValues = buildCliValues(values);
|
|
53
|
+
if (cliValues.stdio && cliValues.http) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
message: 'Choose either --stdio or --http, not both',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
ok: true,
|
|
61
|
+
values: cliValues,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
message: getErrorMessage(error),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
10
71
|
const FORCE_EXIT_TIMEOUT_MS = 10_000;
|
|
11
72
|
let forcedExitTimer;
|
|
12
73
|
function writeAndExit(stream, text, code) {
|
|
@@ -61,13 +122,13 @@ function registerHttpSignalHandlers() {
|
|
|
61
122
|
registerSignalHandlers(['SIGINT', 'SIGTERM'], tryShutdown);
|
|
62
123
|
}
|
|
63
124
|
function writeStartupError(error) {
|
|
64
|
-
logError('Failed to start server', error, LOG_SERVER);
|
|
125
|
+
logError('Failed to start server', error, Loggers.LOG_SERVER);
|
|
65
126
|
process.stderr.write(`Failed to start server: ${error.message}\n`);
|
|
66
127
|
process.exitCode = 1;
|
|
67
128
|
scheduleForcedExit('Startup failure');
|
|
68
129
|
}
|
|
69
130
|
function handleFatalError(label, error, signal) {
|
|
70
|
-
logError(label, error, LOG_SERVER);
|
|
131
|
+
logError(label, error, Loggers.LOG_SERVER);
|
|
71
132
|
process.stderr.write(`${label}: ${error.message}\n`);
|
|
72
133
|
process.exitCode = 1;
|
|
73
134
|
if (shouldAttemptShutdown()) {
|
package/dist/lib/config.js
CHANGED
|
@@ -2,8 +2,8 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { findPackageJSON } from 'node:module';
|
|
3
3
|
import process from 'node:process';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { getErrorMessage } from './error/index.js';
|
|
6
|
+
import { buildIpv4, isIP, normalizeHostname, stripTrailingDots, } from './net/url.js';
|
|
7
7
|
// ── Version ─────────────────────────────────────────────────────────
|
|
8
8
|
function getObjectProperty(value, key) {
|
|
9
9
|
return value[key];
|
package/dist/lib/core.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type {
|
|
3
|
-
import type { SessionStore } from './session.js';
|
|
2
|
+
import type { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
3
|
export { config, enableHttpMode, serverVersion } from './config.js';
|
|
5
4
|
type LogMetadata = Record<string, unknown>;
|
|
6
5
|
interface RequestContext {
|
|
@@ -27,10 +26,63 @@ export declare function logError(message: string, error?: Error | LogMetadata, l
|
|
|
27
26
|
export declare function logCritical(message: string, error?: Error | LogMetadata, logger?: string): void;
|
|
28
27
|
export declare function setLogLevel(level: string, sessionId?: string): void;
|
|
29
28
|
export declare function redactUrl(rawUrl: string): string;
|
|
30
|
-
export type { SessionEntry, SessionStore } from './session.js';
|
|
31
|
-
export { composeCloseHandlers, createSessionStore, createSlotTracker, ensureSessionCapacity, reserveSessionSlot, } from './session.js';
|
|
32
29
|
export declare function startSessionCleanupLoop(store: SessionStore, sessionTtlMs: number, options?: {
|
|
33
30
|
onEvictSession?: (session: SessionEntry) => Promise<void> | void;
|
|
34
31
|
cleanupIntervalMs?: number;
|
|
35
32
|
}): AbortController;
|
|
33
|
+
/**
|
|
34
|
+
* Logger names for different components of the application.
|
|
35
|
+
*/
|
|
36
|
+
export declare const Loggers: {
|
|
37
|
+
readonly LOG_AUTH: "auth";
|
|
38
|
+
readonly LOG_HTTP: "http";
|
|
39
|
+
readonly LOG_SESSION: "session";
|
|
40
|
+
readonly LOG_SERVER: "server";
|
|
41
|
+
readonly LOG_FETCH: "fetch";
|
|
42
|
+
readonly LOG_TRANSFORM: "transform";
|
|
43
|
+
readonly LOG_TASKS: "tasks";
|
|
44
|
+
readonly LOG_RATE_LIMIT: "rate-limit";
|
|
45
|
+
readonly LOG_MCP: "mcp";
|
|
46
|
+
readonly LOG_FETCH_URL: "fetch-url";
|
|
47
|
+
};
|
|
48
|
+
export interface SessionEntry {
|
|
49
|
+
readonly server: McpServer;
|
|
50
|
+
readonly transport: StreamableHTTPServerTransport;
|
|
51
|
+
createdAt: number;
|
|
52
|
+
lastSeen: number;
|
|
53
|
+
protocolInitialized: boolean;
|
|
54
|
+
negotiatedProtocolVersion: string;
|
|
55
|
+
authFingerprint: string;
|
|
56
|
+
}
|
|
57
|
+
export interface SessionStore {
|
|
58
|
+
get: (sessionId: string) => SessionEntry | undefined;
|
|
59
|
+
touch: (sessionId: string) => void;
|
|
60
|
+
set: (sessionId: string, entry: SessionEntry) => void;
|
|
61
|
+
remove: (sessionId: string) => SessionEntry | undefined;
|
|
62
|
+
size: () => number;
|
|
63
|
+
inFlight: () => number;
|
|
64
|
+
incrementInFlight: () => void;
|
|
65
|
+
decrementInFlight: () => void;
|
|
66
|
+
clear: () => SessionEntry[];
|
|
67
|
+
evictExpired: () => {
|
|
68
|
+
id: string;
|
|
69
|
+
entry: SessionEntry;
|
|
70
|
+
}[];
|
|
71
|
+
evictOldest: () => SessionEntry | undefined;
|
|
72
|
+
}
|
|
73
|
+
interface SlotTracker {
|
|
74
|
+
readonly releaseSlot: () => void;
|
|
75
|
+
readonly markInitialized: () => void;
|
|
76
|
+
readonly isInitialized: () => boolean;
|
|
77
|
+
}
|
|
78
|
+
type CloseHandler = (() => void) | undefined;
|
|
79
|
+
export declare function composeCloseHandlers(first: CloseHandler, second: CloseHandler): CloseHandler;
|
|
80
|
+
export declare function createSessionStore(sessionTtlMs: number): SessionStore;
|
|
81
|
+
export declare function createSlotTracker(store: SessionStore): SlotTracker;
|
|
82
|
+
export declare function reserveSessionSlot(store: SessionStore, maxSessions: number): boolean;
|
|
83
|
+
export declare function ensureSessionCapacity({ store, maxSessions, evictOldest, }: {
|
|
84
|
+
store: SessionStore;
|
|
85
|
+
maxSessions: number;
|
|
86
|
+
evictOldest: (store: SessionStore) => boolean;
|
|
87
|
+
}): boolean;
|
|
36
88
|
//# sourceMappingURL=core.d.ts.map
|
package/dist/lib/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/lib/core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/lib/core.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/core.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import {} from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
1
2
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
3
|
import process from 'node:process';
|
|
3
4
|
import { getSystemErrorMessage, inspect, stripVTControlCharacters, } from 'node:util';
|
|
4
|
-
import {} from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
5
|
import { config } from './config.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { getErrorMessage, isAbortError } from './error/index.js';
|
|
7
|
+
import { startAbortableIntervalLoop } from './utils.js';
|
|
8
8
|
export { config, enableHttpMode, serverVersion } from './config.js';
|
|
9
9
|
const requestContext = new AsyncLocalStorage({
|
|
10
10
|
name: 'requestContext',
|
|
@@ -502,7 +502,6 @@ export function redactUrl(rawUrl) {
|
|
|
502
502
|
url.search = '';
|
|
503
503
|
return url.toString();
|
|
504
504
|
}
|
|
505
|
-
export { composeCloseHandlers, createSessionStore, createSlotTracker, ensureSessionCapacity, reserveSessionSlot, } from './session.js';
|
|
506
505
|
const MIN_CLEANUP_INTERVAL_MS = 10_000;
|
|
507
506
|
const MAX_CLEANUP_INTERVAL_MS = 60_000;
|
|
508
507
|
const SESSION_CLOSE_BATCH_SIZE = 10;
|
|
@@ -513,12 +512,12 @@ function getCleanupIntervalMs(sessionTtlMs) {
|
|
|
513
512
|
function handleSessionCleanupError(error) {
|
|
514
513
|
if (isAbortError(error))
|
|
515
514
|
return;
|
|
516
|
-
logWarn('Session cleanup loop failed', { error: getErrorMessage(error) }, LOG_SESSION);
|
|
515
|
+
logWarn('Session cleanup loop failed', { error: getErrorMessage(error) }, Loggers.LOG_SESSION);
|
|
517
516
|
}
|
|
518
517
|
function logRejectedSettledResults(results, message) {
|
|
519
518
|
for (const result of results) {
|
|
520
519
|
if (result.status === 'rejected') {
|
|
521
|
-
logWarn(message, { error: getErrorMessage(result.reason) }, LOG_SESSION);
|
|
520
|
+
logWarn(message, { error: getErrorMessage(result.reason) }, Loggers.LOG_SESSION);
|
|
522
521
|
}
|
|
523
522
|
}
|
|
524
523
|
}
|
|
@@ -558,7 +557,7 @@ class SessionCleanupLoop {
|
|
|
558
557
|
logInfo('Expired sessions evicted', {
|
|
559
558
|
evicted: evicted.length,
|
|
560
559
|
timestamp: new Date(now).toISOString(),
|
|
561
|
-
}, LOG_SESSION);
|
|
560
|
+
}, Loggers.LOG_SESSION);
|
|
562
561
|
}
|
|
563
562
|
}
|
|
564
563
|
async closeExpiredSession(sessionId, session) {
|
|
@@ -569,7 +568,7 @@ class SessionCleanupLoop {
|
|
|
569
568
|
catch (error) {
|
|
570
569
|
logWarn('Expired session pre-close hook failed', {
|
|
571
570
|
error: getErrorMessage(error),
|
|
572
|
-
}, LOG_SESSION);
|
|
571
|
+
}, Loggers.LOG_SESSION);
|
|
573
572
|
}
|
|
574
573
|
}
|
|
575
574
|
const closePromise = Promise.allSettled([
|
|
@@ -598,7 +597,7 @@ class SessionCleanupLoop {
|
|
|
598
597
|
catch (error) {
|
|
599
598
|
logWarn('Session close operation failed or timed out', {
|
|
600
599
|
error: getErrorMessage(error),
|
|
601
|
-
}, LOG_SESSION);
|
|
600
|
+
}, Loggers.LOG_SESSION);
|
|
602
601
|
}
|
|
603
602
|
finally {
|
|
604
603
|
if (timeoutId) {
|
|
@@ -611,7 +610,7 @@ class SessionCleanupLoop {
|
|
|
611
610
|
catch (error) {
|
|
612
611
|
logWarn('Failed to unregister session server', {
|
|
613
612
|
error: getErrorMessage(error),
|
|
614
|
-
}, LOG_SESSION);
|
|
613
|
+
}, Loggers.LOG_SESSION);
|
|
615
614
|
}
|
|
616
615
|
}
|
|
617
616
|
logCloseFailure(target, error) {
|
|
@@ -619,10 +618,162 @@ class SessionCleanupLoop {
|
|
|
619
618
|
return;
|
|
620
619
|
logWarn(`Failed to close expired session ${target}`, {
|
|
621
620
|
error: getErrorMessage(error),
|
|
622
|
-
}, LOG_SESSION);
|
|
621
|
+
}, Loggers.LOG_SESSION);
|
|
623
622
|
}
|
|
624
623
|
}
|
|
625
624
|
export function startSessionCleanupLoop(store, sessionTtlMs, options) {
|
|
626
625
|
const loop = new SessionCleanupLoop(store, sessionTtlMs, options?.onEvictSession, options?.cleanupIntervalMs);
|
|
627
626
|
return loop.start();
|
|
628
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* Logger names for different components of the application.
|
|
630
|
+
*/
|
|
631
|
+
export const Loggers = {
|
|
632
|
+
LOG_AUTH: 'auth',
|
|
633
|
+
LOG_HTTP: 'http',
|
|
634
|
+
LOG_SESSION: 'session',
|
|
635
|
+
LOG_SERVER: 'server',
|
|
636
|
+
LOG_FETCH: 'fetch',
|
|
637
|
+
LOG_TRANSFORM: 'transform',
|
|
638
|
+
LOG_TASKS: 'tasks',
|
|
639
|
+
LOG_RATE_LIMIT: 'rate-limit',
|
|
640
|
+
LOG_MCP: 'mcp',
|
|
641
|
+
LOG_FETCH_URL: 'fetch-url',
|
|
642
|
+
};
|
|
643
|
+
export function composeCloseHandlers(first, second) {
|
|
644
|
+
if (!first)
|
|
645
|
+
return second;
|
|
646
|
+
if (!second)
|
|
647
|
+
return first;
|
|
648
|
+
return () => {
|
|
649
|
+
try {
|
|
650
|
+
first();
|
|
651
|
+
}
|
|
652
|
+
finally {
|
|
653
|
+
second();
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
/* -------------------------------------------------------------------------------------------------
|
|
658
|
+
* In-memory session store
|
|
659
|
+
* ------------------------------------------------------------------------------------------------- */
|
|
660
|
+
class InMemorySessionStore {
|
|
661
|
+
sessionTtlMs;
|
|
662
|
+
sessions = new Map();
|
|
663
|
+
inflight = 0;
|
|
664
|
+
constructor(sessionTtlMs) {
|
|
665
|
+
this.sessionTtlMs = sessionTtlMs;
|
|
666
|
+
}
|
|
667
|
+
get(sessionId) {
|
|
668
|
+
if (sessionId.length === 0)
|
|
669
|
+
return undefined;
|
|
670
|
+
return this.sessions.get(sessionId);
|
|
671
|
+
}
|
|
672
|
+
touch(sessionId) {
|
|
673
|
+
if (sessionId.length === 0)
|
|
674
|
+
return;
|
|
675
|
+
const session = this.sessions.get(sessionId);
|
|
676
|
+
if (!session)
|
|
677
|
+
return;
|
|
678
|
+
session.lastSeen = Date.now();
|
|
679
|
+
this.sessions.delete(sessionId);
|
|
680
|
+
this.sessions.set(sessionId, session);
|
|
681
|
+
}
|
|
682
|
+
set(sessionId, entry) {
|
|
683
|
+
if (sessionId.length === 0)
|
|
684
|
+
return;
|
|
685
|
+
this.sessions.delete(sessionId);
|
|
686
|
+
this.sessions.set(sessionId, entry);
|
|
687
|
+
}
|
|
688
|
+
remove(sessionId) {
|
|
689
|
+
if (sessionId.length === 0)
|
|
690
|
+
return undefined;
|
|
691
|
+
const session = this.sessions.get(sessionId);
|
|
692
|
+
this.sessions.delete(sessionId);
|
|
693
|
+
return session;
|
|
694
|
+
}
|
|
695
|
+
size() {
|
|
696
|
+
return this.sessions.size;
|
|
697
|
+
}
|
|
698
|
+
inFlight() {
|
|
699
|
+
return this.inflight;
|
|
700
|
+
}
|
|
701
|
+
incrementInFlight() {
|
|
702
|
+
this.inflight += 1;
|
|
703
|
+
}
|
|
704
|
+
decrementInFlight() {
|
|
705
|
+
if (this.inflight === 0)
|
|
706
|
+
return;
|
|
707
|
+
this.inflight -= 1;
|
|
708
|
+
}
|
|
709
|
+
clear() {
|
|
710
|
+
const entries = [...this.sessions.values()];
|
|
711
|
+
this.sessions.clear();
|
|
712
|
+
return entries;
|
|
713
|
+
}
|
|
714
|
+
evictExpired() {
|
|
715
|
+
const now = Date.now();
|
|
716
|
+
const evicted = [];
|
|
717
|
+
for (const [id, session] of this.sessions.entries()) {
|
|
718
|
+
if (this.sessionTtlMs > 0 && now - session.lastSeen > this.sessionTtlMs) {
|
|
719
|
+
this.sessions.delete(id);
|
|
720
|
+
evicted.push({ id, entry: session });
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return evicted;
|
|
727
|
+
}
|
|
728
|
+
evictOldest() {
|
|
729
|
+
const oldest = this.sessions.keys().next();
|
|
730
|
+
if (oldest.done)
|
|
731
|
+
return undefined;
|
|
732
|
+
const session = this.sessions.get(oldest.value);
|
|
733
|
+
this.sessions.delete(oldest.value);
|
|
734
|
+
return session;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
export function createSessionStore(sessionTtlMs) {
|
|
738
|
+
const store = new InMemorySessionStore(sessionTtlMs);
|
|
739
|
+
return store;
|
|
740
|
+
}
|
|
741
|
+
/* -------------------------------------------------------------------------------------------------
|
|
742
|
+
* Slot tracking and capacity
|
|
743
|
+
* ------------------------------------------------------------------------------------------------- */
|
|
744
|
+
export function createSlotTracker(store) {
|
|
745
|
+
let slotReleased = false;
|
|
746
|
+
let initialized = false;
|
|
747
|
+
return {
|
|
748
|
+
releaseSlot() {
|
|
749
|
+
if (slotReleased)
|
|
750
|
+
return;
|
|
751
|
+
slotReleased = true;
|
|
752
|
+
store.decrementInFlight();
|
|
753
|
+
},
|
|
754
|
+
markInitialized() {
|
|
755
|
+
initialized = true;
|
|
756
|
+
},
|
|
757
|
+
isInitialized() {
|
|
758
|
+
return initialized;
|
|
759
|
+
},
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
export function reserveSessionSlot(store, maxSessions) {
|
|
763
|
+
if (maxSessions <= 0)
|
|
764
|
+
return false;
|
|
765
|
+
if (store.size() + store.inFlight() >= maxSessions)
|
|
766
|
+
return false;
|
|
767
|
+
store.incrementInFlight();
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
export function ensureSessionCapacity({ store, maxSessions, evictOldest, }) {
|
|
771
|
+
if (maxSessions <= 0)
|
|
772
|
+
return false;
|
|
773
|
+
if (store.size() + store.inFlight() < maxSessions)
|
|
774
|
+
return true;
|
|
775
|
+
if (store.size() > 0 && evictOldest(store)) {
|
|
776
|
+
return store.size() + store.inFlight() < maxSessions;
|
|
777
|
+
}
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function isError(value: unknown): value is Error;
|
|
2
|
+
export declare class FetchError extends Error {
|
|
3
|
+
readonly url: string;
|
|
4
|
+
readonly statusCode: number;
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly details: Readonly<Record<string, unknown>>;
|
|
7
|
+
constructor(message: string, url: string, httpStatus?: number, details?: Record<string, unknown>, options?: ErrorOptions);
|
|
8
|
+
}
|
|
9
|
+
export declare class CodedError extends Error {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
constructor(message: string, code: string, options?: ErrorOptions);
|
|
12
|
+
}
|
|
13
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
14
|
+
export declare function isSystemError(error: unknown): error is NodeJS.ErrnoException;
|
|
15
|
+
export declare function getErrorMessage(error: unknown): string;
|
|
16
|
+
export declare function toError(error: unknown): Error;
|
|
17
|
+
export declare function throwIfAborted(signal: AbortSignal | undefined, url: string, stage: string): void;
|
|
18
|
+
export declare function createAbortError(url: string, stage: string): FetchError;
|
|
19
|
+
//# sourceMappingURL=classes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.d.ts","sourceRoot":"","sources":["../../../src/lib/error/classes.ts"],"names":[],"mappings":"AAWA,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAQtD;AAMD,qBAAa,UAAW,SAAQ,KAAK;IAOjC,QAAQ,CAAC,GAAG,EAAE,MAAM;IANtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;gBAGlD,OAAO,EAAE,MAAM,EACN,GAAG,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,OAAO,CAAC,EAAE,YAAY;CAezB;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAKlE;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAK5E;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAsBtD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAE7C;AAID,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,IAAI,CAYN;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAMvE"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import { isObject } from '../utils.js';
|
|
3
|
+
import { SystemErrors } from './codes.js';
|
|
4
|
+
export function isError(value) {
|
|
5
|
+
const maybeIsError = Error.isError;
|
|
6
|
+
if (typeof maybeIsError === 'function') {
|
|
7
|
+
const result = maybeIsError(value);
|
|
8
|
+
if (typeof result === 'boolean')
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
11
|
+
return value instanceof Error;
|
|
12
|
+
}
|
|
13
|
+
// ── Error classes ──────────────────────────────────────────────────
|
|
14
|
+
const DEFAULT_HTTP_STATUS = 502;
|
|
15
|
+
export class FetchError extends Error {
|
|
16
|
+
url;
|
|
17
|
+
statusCode;
|
|
18
|
+
code;
|
|
19
|
+
details;
|
|
20
|
+
constructor(message, url, httpStatus, details = {}, options) {
|
|
21
|
+
super(message, options);
|
|
22
|
+
this.url = url;
|
|
23
|
+
this.name = 'FetchError';
|
|
24
|
+
this.statusCode = httpStatus ?? DEFAULT_HTTP_STATUS;
|
|
25
|
+
this.details = Object.freeze({ url, httpStatus, ...details });
|
|
26
|
+
const explicitCode = this.details['code'];
|
|
27
|
+
if (typeof explicitCode === 'string') {
|
|
28
|
+
this.code = explicitCode;
|
|
29
|
+
}
|
|
30
|
+
else if (httpStatus) {
|
|
31
|
+
this.code = `HTTP_${httpStatus}`;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.code = SystemErrors.FETCH_ERROR;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class CodedError extends Error {
|
|
39
|
+
code;
|
|
40
|
+
constructor(message, code, options) {
|
|
41
|
+
super(message, options);
|
|
42
|
+
this.code = code;
|
|
43
|
+
this.name = 'CodedError';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ── Error guards ───────────────────────────────────────────────────
|
|
47
|
+
export function isAbortError(error) {
|
|
48
|
+
return isError(error) && error.name === 'AbortError';
|
|
49
|
+
}
|
|
50
|
+
export function isSystemError(error) {
|
|
51
|
+
if (!isError(error))
|
|
52
|
+
return false;
|
|
53
|
+
if (!('code' in error))
|
|
54
|
+
return false;
|
|
55
|
+
const { code } = error;
|
|
56
|
+
return typeof code === 'string';
|
|
57
|
+
}
|
|
58
|
+
// ── Error extraction ───────────────────────────────────────────────
|
|
59
|
+
const UNKNOWN_ERROR_MESSAGE = 'Unknown error';
|
|
60
|
+
export function getErrorMessage(error) {
|
|
61
|
+
if (isError(error))
|
|
62
|
+
return error.message;
|
|
63
|
+
if (typeof error === 'string' && error.length > 0)
|
|
64
|
+
return error;
|
|
65
|
+
if (isObject(error) &&
|
|
66
|
+
typeof error['message'] === 'string' &&
|
|
67
|
+
error['message'].length > 0) {
|
|
68
|
+
return error['message'];
|
|
69
|
+
}
|
|
70
|
+
if (error === null || error === undefined)
|
|
71
|
+
return UNKNOWN_ERROR_MESSAGE;
|
|
72
|
+
try {
|
|
73
|
+
return inspect(error, {
|
|
74
|
+
depth: 2,
|
|
75
|
+
maxStringLength: 200,
|
|
76
|
+
breakLength: Infinity,
|
|
77
|
+
compact: true,
|
|
78
|
+
colors: false,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return UNKNOWN_ERROR_MESSAGE;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function toError(error) {
|
|
86
|
+
return isError(error) ? error : new Error(getErrorMessage(error));
|
|
87
|
+
}
|
|
88
|
+
// ── Abort helpers ──────────────────────────────────────────────────
|
|
89
|
+
export function throwIfAborted(signal, url, stage) {
|
|
90
|
+
if (!signal?.aborted)
|
|
91
|
+
return;
|
|
92
|
+
if (signal.reason instanceof Error && signal.reason.name === 'TimeoutError') {
|
|
93
|
+
const error = new FetchError('Request timeout', url, 504, {
|
|
94
|
+
reason: 'timeout',
|
|
95
|
+
stage,
|
|
96
|
+
});
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
throw createAbortError(url, stage);
|
|
100
|
+
}
|
|
101
|
+
export function createAbortError(url, stage) {
|
|
102
|
+
const error = new FetchError('Request was canceled', url, 499, {
|
|
103
|
+
reason: 'aborted',
|
|
104
|
+
stage,
|
|
105
|
+
});
|
|
106
|
+
return error;
|
|
107
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type ToolErrorLogMeta, type ToolErrorResponse } from './payload.js';
|
|
2
|
+
export declare function handleToolError(error: unknown, url: string, fallbackMessage?: string): ToolErrorResponse;
|
|
3
|
+
export declare function classifyAndLogToolError(error: unknown, meta: ToolErrorLogMeta, loggerName: string, toolName: string, fallbackMessage: string): ToolErrorResponse;
|
|
4
|
+
//# sourceMappingURL=classify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../../src/lib/error/classify.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,KAAK,gBAAgB,EAErB,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AA4JtB,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,MAAM,EACX,eAAe,SAAqB,GACnC,iBAAiB,CAInB;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,GACtB,iBAAiB,CAuCnB"}
|