@j0hanz/fetch-url-mcp 1.11.2 → 1.11.4
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/helpers.d.ts +1 -1
- package/dist/http/helpers.d.ts.map +1 -1
- package/dist/http/helpers.js +1 -1
- package/dist/http/native.js +1 -1
- package/dist/lib/config.d.ts +152 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +478 -0
- package/dist/lib/core.d.ts +1 -150
- package/dist/lib/core.d.ts.map +1 -1
- package/dist/lib/core.js +3 -459
- package/dist/lib/{mcp-tools.d.ts → mcp-interop.d.ts} +49 -1
- package/dist/lib/mcp-interop.d.ts.map +1 -0
- package/dist/lib/mcp-interop.js +354 -0
- package/dist/lib/task-handlers.js +1 -1
- package/dist/resources/index.js +1 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +1 -1
- package/dist/server.js +2 -2
- package/dist/tasks/handlers.d.ts +11 -0
- package/dist/tasks/handlers.d.ts.map +1 -0
- package/dist/tasks/handlers.js +151 -0
- package/dist/tasks/owner.d.ts +1 -2
- package/dist/tasks/owner.d.ts.map +1 -1
- package/dist/tasks/registry.d.ts +1 -1
- package/dist/tasks/registry.d.ts.map +1 -1
- package/dist/tools/fetch-url.d.ts +2 -3
- package/dist/tools/fetch-url.d.ts.map +1 -1
- package/dist/tools/fetch-url.js +3 -4
- package/dist/{lib → transform}/dom-prep.d.ts +1 -1
- package/dist/transform/dom-prep.d.ts.map +1 -0
- package/dist/{lib → transform}/dom-prep.js +2 -2
- package/dist/transform/html-translators.d.ts.map +1 -1
- package/dist/transform/html-translators.js +1 -1
- package/dist/transform/metadata.d.ts +8 -0
- package/dist/transform/metadata.d.ts.map +1 -1
- package/dist/transform/metadata.js +66 -0
- package/dist/transform/transform.d.ts.map +1 -1
- package/dist/transform/transform.js +2 -3
- package/package.json +1 -1
- package/dist/lib/dom-prep.d.ts.map +0 -1
- package/dist/lib/mcp-tools.d.ts.map +0 -1
- package/dist/lib/mcp-tools.js +0 -129
- package/dist/lib/progress.d.ts +0 -32
- package/dist/lib/progress.d.ts.map +0 -1
- package/dist/lib/progress.js +0 -143
- package/dist/lib/sdk-interop.d.ts +0 -20
- package/dist/lib/sdk-interop.d.ts.map +0 -1
- package/dist/lib/sdk-interop.js +0 -85
- package/dist/transform/title-policy.d.ts +0 -9
- package/dist/transform/title-policy.d.ts.map +0 -1
- package/dist/transform/title-policy.js +0 -67
package/dist/http/helpers.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
|
4
4
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
5
|
import type { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
6
6
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
7
|
-
import type { JsonRpcId } from '../lib/mcp-
|
|
7
|
+
import type { JsonRpcId } from '../lib/mcp-interop.js';
|
|
8
8
|
export type NetworkServer = Server | HttpsServer;
|
|
9
9
|
export interface RequestContext {
|
|
10
10
|
req: IncomingMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/http/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,YAAY,CAAC;AAKxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACxG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAQ/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/http/helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/http/helpers.js
CHANGED
|
@@ -2,9 +2,9 @@ import { Writable } from 'node:stream';
|
|
|
2
2
|
import { pipeline } from 'node:stream/promises';
|
|
3
3
|
import { composeCloseHandlers, config, logWarn } from '../lib/core.js';
|
|
4
4
|
import { resolveMcpSessionIdByServer, unregisterMcpSessionServer, unregisterMcpSessionServerByServer, } from '../lib/core.js';
|
|
5
|
-
import { cancelTasksForOwner } from '../lib/task-handlers.js';
|
|
6
5
|
import { createDefaultBlockList, normalizeIpForBlockList } from '../lib/url.js';
|
|
7
6
|
import { getErrorMessage, toError } from '../lib/utils.js';
|
|
7
|
+
import { cancelTasksForOwner } from '../tasks/handlers.js';
|
|
8
8
|
function abortControllerBestEffort(controller) {
|
|
9
9
|
if (!controller.signal.aborted)
|
|
10
10
|
controller.abort();
|
package/dist/http/native.js
CHANGED
|
@@ -11,7 +11,7 @@ import { config, enableHttpMode } from '../lib/core.js';
|
|
|
11
11
|
import { logError, logInfo, registerMcpSessionServer, runWithRequestContext, } from '../lib/core.js';
|
|
12
12
|
import { composeCloseHandlers, createSessionStore, createSlotTracker, ensureSessionCapacity, reserveSessionSlot, startSessionCleanupLoop, } from '../lib/core.js';
|
|
13
13
|
import { handleDownload } from '../lib/http.js';
|
|
14
|
-
import { acceptsEventStream, acceptsJsonAndEventStream, isJsonRpcBatchRequest, isMcpMessageBody, isMcpRequestBody, } from '../lib/mcp-
|
|
14
|
+
import { acceptsEventStream, acceptsJsonAndEventStream, isJsonRpcBatchRequest, isMcpMessageBody, isMcpRequestBody, } from '../lib/mcp-interop.js';
|
|
15
15
|
import { toError } from '../lib/utils.js';
|
|
16
16
|
import { applyHttpServerTuning, drainConnectionsOnShutdown, } from '../lib/utils.js';
|
|
17
17
|
import { isObject } from '../lib/utils.js';
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
export declare const serverVersion: string;
|
|
2
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
3
|
+
type TransformWorkerMode = 'threads' | 'process';
|
|
4
|
+
type AuthMode = 'oauth' | 'static';
|
|
5
|
+
interface WorkerResourceLimits {
|
|
6
|
+
maxOldGenerationSizeMb?: number;
|
|
7
|
+
maxYoungGenerationSizeMb?: number;
|
|
8
|
+
codeRangeSizeMb?: number;
|
|
9
|
+
stackSizeMb?: number;
|
|
10
|
+
}
|
|
11
|
+
interface AuthConfig {
|
|
12
|
+
mode: AuthMode;
|
|
13
|
+
issuerUrl: URL | undefined;
|
|
14
|
+
authorizationUrl: URL | undefined;
|
|
15
|
+
tokenUrl: URL | undefined;
|
|
16
|
+
revocationUrl: URL | undefined;
|
|
17
|
+
registrationUrl: URL | undefined;
|
|
18
|
+
introspectionUrl: URL | undefined;
|
|
19
|
+
resourceUrl: URL;
|
|
20
|
+
requiredScopes: string[];
|
|
21
|
+
clientId: string | undefined;
|
|
22
|
+
clientSecret: string | undefined;
|
|
23
|
+
introspectionTimeoutMs: number;
|
|
24
|
+
staticTokens: string[];
|
|
25
|
+
}
|
|
26
|
+
interface HttpsConfig {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
keyFile: string | undefined;
|
|
29
|
+
certFile: string | undefined;
|
|
30
|
+
caFile: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
interface RuntimeState {
|
|
33
|
+
httpMode: boolean;
|
|
34
|
+
}
|
|
35
|
+
interface AppServerHttpConfig {
|
|
36
|
+
headersTimeoutMs: number | undefined;
|
|
37
|
+
requestTimeoutMs: number | undefined;
|
|
38
|
+
keepAliveTimeoutMs: number | undefined;
|
|
39
|
+
keepAliveTimeoutBufferMs: number | undefined;
|
|
40
|
+
maxHeadersCount: number | undefined;
|
|
41
|
+
maxConnections: number;
|
|
42
|
+
blockPrivateConnections: boolean;
|
|
43
|
+
shutdownCloseIdleConnections: boolean;
|
|
44
|
+
shutdownCloseAllConnections: boolean;
|
|
45
|
+
}
|
|
46
|
+
interface AppServerConfig {
|
|
47
|
+
name: string;
|
|
48
|
+
version: string;
|
|
49
|
+
port: number;
|
|
50
|
+
host: string;
|
|
51
|
+
https: HttpsConfig;
|
|
52
|
+
sessionTtlMs: number;
|
|
53
|
+
sessionInitTimeoutMs: number;
|
|
54
|
+
maxSessions: number;
|
|
55
|
+
http: AppServerHttpConfig;
|
|
56
|
+
}
|
|
57
|
+
interface AppFetcherConfig {
|
|
58
|
+
timeout: number;
|
|
59
|
+
maxRedirects: number;
|
|
60
|
+
userAgent: string;
|
|
61
|
+
maxContentLength: number;
|
|
62
|
+
}
|
|
63
|
+
interface AppTransformConfig {
|
|
64
|
+
timeoutMs: number;
|
|
65
|
+
stageWarnRatio: number;
|
|
66
|
+
metadataFormat: string;
|
|
67
|
+
maxWorkerScale: number;
|
|
68
|
+
cancelAckTimeoutMs: number;
|
|
69
|
+
workerMode: TransformWorkerMode;
|
|
70
|
+
workerResourceLimits: WorkerResourceLimits | undefined;
|
|
71
|
+
}
|
|
72
|
+
interface AppTasksConfig {
|
|
73
|
+
maxTotal: number;
|
|
74
|
+
maxPerOwner: number;
|
|
75
|
+
emitStatusNotifications: boolean;
|
|
76
|
+
requireInterception: boolean;
|
|
77
|
+
}
|
|
78
|
+
interface AppCacheConfig {
|
|
79
|
+
enabled: boolean;
|
|
80
|
+
ttl: number;
|
|
81
|
+
maxKeys: number;
|
|
82
|
+
maxSizeBytes: number;
|
|
83
|
+
}
|
|
84
|
+
interface AppNoiseRemovalConfig {
|
|
85
|
+
extraTokens: string[];
|
|
86
|
+
extraSelectors: string[];
|
|
87
|
+
enabledCategories: string[];
|
|
88
|
+
debug: boolean;
|
|
89
|
+
aggressiveMode: boolean;
|
|
90
|
+
preserveSvgCanvas: boolean;
|
|
91
|
+
weights: {
|
|
92
|
+
hidden: number;
|
|
93
|
+
structural: number;
|
|
94
|
+
promo: number;
|
|
95
|
+
stickyFixed: number;
|
|
96
|
+
threshold: number;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
interface AppMarkdownCleanupConfig {
|
|
100
|
+
promoteOrphanHeadings: boolean;
|
|
101
|
+
removeSkipLinks: boolean;
|
|
102
|
+
removeTocBlocks: boolean;
|
|
103
|
+
removeTypeDocComments: boolean;
|
|
104
|
+
headingKeywords: string[];
|
|
105
|
+
}
|
|
106
|
+
export declare const config: {
|
|
107
|
+
server: AppServerConfig;
|
|
108
|
+
fetcher: AppFetcherConfig;
|
|
109
|
+
transform: AppTransformConfig;
|
|
110
|
+
tools: {
|
|
111
|
+
enabled: string[];
|
|
112
|
+
timeoutMs: number;
|
|
113
|
+
};
|
|
114
|
+
tasks: AppTasksConfig;
|
|
115
|
+
cache: AppCacheConfig;
|
|
116
|
+
extraction: {
|
|
117
|
+
maxBlockLength: number;
|
|
118
|
+
minParagraphLength: number;
|
|
119
|
+
};
|
|
120
|
+
noiseRemoval: AppNoiseRemovalConfig;
|
|
121
|
+
markdownCleanup: AppMarkdownCleanupConfig;
|
|
122
|
+
i18n: {
|
|
123
|
+
locale: string | undefined;
|
|
124
|
+
};
|
|
125
|
+
logging: {
|
|
126
|
+
level: LogLevel;
|
|
127
|
+
format: string;
|
|
128
|
+
};
|
|
129
|
+
constants: {
|
|
130
|
+
maxHtmlBytes: number;
|
|
131
|
+
maxUrlLength: number;
|
|
132
|
+
maxInlineContentChars: number;
|
|
133
|
+
};
|
|
134
|
+
security: {
|
|
135
|
+
blockedHosts: Set<string>;
|
|
136
|
+
allowedHosts: Set<string>;
|
|
137
|
+
apiKey: string | undefined;
|
|
138
|
+
allowRemote: boolean;
|
|
139
|
+
allowLocalFetch: boolean;
|
|
140
|
+
};
|
|
141
|
+
auth: AuthConfig;
|
|
142
|
+
rateLimit: {
|
|
143
|
+
enabled: boolean;
|
|
144
|
+
maxRequests: number;
|
|
145
|
+
windowMs: number;
|
|
146
|
+
cleanupIntervalMs: number;
|
|
147
|
+
};
|
|
148
|
+
runtime: RuntimeState;
|
|
149
|
+
};
|
|
150
|
+
export declare function enableHttpMode(): void;
|
|
151
|
+
export {};
|
|
152
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { findPackageJSON } from 'node:module';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { buildIpv4, isIP, normalizeHostname, stripTrailingDots, } from './url.js';
|
|
6
|
+
import { getErrorMessage } from './utils.js';
|
|
7
|
+
// ── Version ─────────────────────────────────────────────────────────
|
|
8
|
+
function hasPackageJsonVersion(value) {
|
|
9
|
+
return (typeof value === 'object' &&
|
|
10
|
+
value !== null &&
|
|
11
|
+
typeof value.version === 'string');
|
|
12
|
+
}
|
|
13
|
+
function readServerVersion(moduleUrl) {
|
|
14
|
+
const packageJsonPath = findPackageJSON(moduleUrl);
|
|
15
|
+
if (!packageJsonPath)
|
|
16
|
+
throw new Error('package.json not found');
|
|
17
|
+
let packageJson;
|
|
18
|
+
try {
|
|
19
|
+
packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw new Error(`Failed to parse package.json at ${packageJsonPath}: ${getErrorMessage(error)}`, { cause: error });
|
|
23
|
+
}
|
|
24
|
+
if (!hasPackageJsonVersion(packageJson)) {
|
|
25
|
+
throw new Error(`package.json version is missing at ${packageJsonPath}`);
|
|
26
|
+
}
|
|
27
|
+
return packageJson.version;
|
|
28
|
+
}
|
|
29
|
+
export const serverVersion = readServerVersion(import.meta.url);
|
|
30
|
+
const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
|
|
31
|
+
const ALLOWED_LOG_LEVELS = new Set(LOG_LEVELS);
|
|
32
|
+
const DEFAULT_HEADING_KEYWORDS = [
|
|
33
|
+
'overview',
|
|
34
|
+
'introduction',
|
|
35
|
+
'summary',
|
|
36
|
+
'conclusion',
|
|
37
|
+
'prerequisites',
|
|
38
|
+
'requirements',
|
|
39
|
+
'installation',
|
|
40
|
+
'configuration',
|
|
41
|
+
'usage',
|
|
42
|
+
'features',
|
|
43
|
+
'limitations',
|
|
44
|
+
'troubleshooting',
|
|
45
|
+
'faq',
|
|
46
|
+
'resources',
|
|
47
|
+
'references',
|
|
48
|
+
'changelog',
|
|
49
|
+
'license',
|
|
50
|
+
'acknowledgments',
|
|
51
|
+
'appendix',
|
|
52
|
+
];
|
|
53
|
+
class ConfigError extends Error {
|
|
54
|
+
name = 'ConfigError';
|
|
55
|
+
}
|
|
56
|
+
// ── Environment ─────────────────────────────────────────────────────
|
|
57
|
+
function isMissingEnvFileError(error) {
|
|
58
|
+
if (!error || typeof error !== 'object')
|
|
59
|
+
return false;
|
|
60
|
+
const { code } = error;
|
|
61
|
+
return code === 'ENOENT' || code === 'ERR_ENV_FILE_NOT_FOUND';
|
|
62
|
+
}
|
|
63
|
+
function loadEnvFileIfAvailable() {
|
|
64
|
+
try {
|
|
65
|
+
process.loadEnvFile();
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (isMissingEnvFileError(error))
|
|
69
|
+
return;
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
loadEnvFileIfAvailable();
|
|
74
|
+
const { env } = process;
|
|
75
|
+
const ENV_BOOLEAN_SCHEMA = z.stringbool({
|
|
76
|
+
truthy: ['true', '1', 'yes', 'on'],
|
|
77
|
+
falsy: ['false', '0', 'no', 'off'],
|
|
78
|
+
});
|
|
79
|
+
// ── Host parsing helpers ────────────────────────────────────────────
|
|
80
|
+
function tryParseUrlHost(raw) {
|
|
81
|
+
if (raw.includes('://')) {
|
|
82
|
+
const hostname = URL.parse(raw)?.hostname;
|
|
83
|
+
if (hostname)
|
|
84
|
+
return normalizeHostname(hostname);
|
|
85
|
+
}
|
|
86
|
+
const candidateHostname = URL.parse(`http://${raw}`)?.hostname;
|
|
87
|
+
if (candidateHostname)
|
|
88
|
+
return normalizeHostname(candidateHostname);
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
function tryParseIpv6Bracket(lowered) {
|
|
92
|
+
if (!lowered.startsWith('['))
|
|
93
|
+
return undefined;
|
|
94
|
+
const end = lowered.indexOf(']');
|
|
95
|
+
return end === -1 ? null : normalizeHostname(lowered.slice(1, end));
|
|
96
|
+
}
|
|
97
|
+
function tryParseHostPort(lowered) {
|
|
98
|
+
const firstColon = lowered.indexOf(':');
|
|
99
|
+
if (firstColon === -1)
|
|
100
|
+
return normalizeHostname(lowered);
|
|
101
|
+
if (lowered.includes(':', firstColon + 1))
|
|
102
|
+
return null;
|
|
103
|
+
const host = lowered.slice(0, firstColon);
|
|
104
|
+
return host ? normalizeHostname(host) : null;
|
|
105
|
+
}
|
|
106
|
+
// ── EnvParser ───────────────────────────────────────────────────────
|
|
107
|
+
const EnvParser = {
|
|
108
|
+
integerValue(envValue, opts) {
|
|
109
|
+
if (!envValue)
|
|
110
|
+
return null;
|
|
111
|
+
const parsed = Number.parseInt(envValue, 10);
|
|
112
|
+
if (Number.isNaN(parsed)) {
|
|
113
|
+
if (opts?.envName)
|
|
114
|
+
process.stderr.write(`Warning: ignoring invalid ${opts.envName} value "${envValue}" (not an integer)\n`);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if ((opts?.min !== undefined && parsed < opts.min) ||
|
|
118
|
+
(opts?.max !== undefined && parsed > opts.max)) {
|
|
119
|
+
if (opts.envName)
|
|
120
|
+
process.stderr.write(`Warning: ignoring out-of-range ${opts.envName} value ${parsed} (allowed: ${opts.min ?? '-∞'}..${opts.max ?? '∞'})\n`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return parsed;
|
|
124
|
+
},
|
|
125
|
+
optionalInteger(envValue, opts) {
|
|
126
|
+
return EnvParser.integerValue(envValue, opts) ?? undefined;
|
|
127
|
+
},
|
|
128
|
+
integer(envValue, defaultValue, opts) {
|
|
129
|
+
return EnvParser.integerValue(envValue, opts) ?? defaultValue;
|
|
130
|
+
},
|
|
131
|
+
boolean(envValue, defaultValue, envName) {
|
|
132
|
+
const trimmed = envValue?.trim();
|
|
133
|
+
if (!trimmed)
|
|
134
|
+
return defaultValue;
|
|
135
|
+
const parsed = ENV_BOOLEAN_SCHEMA.safeParse(trimmed);
|
|
136
|
+
if (!parsed.success) {
|
|
137
|
+
if (envName)
|
|
138
|
+
process.stderr.write(`Warning: ignoring invalid ${envName} value "${envValue ?? ''}" (expected true/false, 1/0, yes/no, or on/off)\n`);
|
|
139
|
+
return defaultValue;
|
|
140
|
+
}
|
|
141
|
+
return parsed.data;
|
|
142
|
+
},
|
|
143
|
+
list(envValue, defaultValue) {
|
|
144
|
+
if (!envValue)
|
|
145
|
+
return defaultValue ? [...defaultValue] : [];
|
|
146
|
+
const parsed = envValue
|
|
147
|
+
.split(/[\s,]+/)
|
|
148
|
+
.map((e) => e.trim())
|
|
149
|
+
.filter(Boolean);
|
|
150
|
+
return parsed.length > 0 || !defaultValue ? parsed : [...defaultValue];
|
|
151
|
+
},
|
|
152
|
+
locale(value) {
|
|
153
|
+
const lowered = value?.trim().toLowerCase();
|
|
154
|
+
return !lowered || lowered === 'system' || lowered === 'default'
|
|
155
|
+
? undefined
|
|
156
|
+
: lowered;
|
|
157
|
+
},
|
|
158
|
+
logLevel(envValue) {
|
|
159
|
+
const level = envValue?.toLowerCase();
|
|
160
|
+
if (!level || !ALLOWED_LOG_LEVELS.has(level)) {
|
|
161
|
+
if (envValue)
|
|
162
|
+
process.stderr.write(`Warning: ignoring invalid LOG_LEVEL value "${envValue}", using default "info"\n`);
|
|
163
|
+
return 'info';
|
|
164
|
+
}
|
|
165
|
+
return level;
|
|
166
|
+
},
|
|
167
|
+
transformWorkerMode(envValue) {
|
|
168
|
+
const normalized = envValue?.trim().toLowerCase();
|
|
169
|
+
return normalized === 'process' || normalized === 'fork'
|
|
170
|
+
? 'process'
|
|
171
|
+
: 'threads';
|
|
172
|
+
},
|
|
173
|
+
url(value, name) {
|
|
174
|
+
if (!value)
|
|
175
|
+
return undefined;
|
|
176
|
+
const parsed = URL.parse(value);
|
|
177
|
+
if (!parsed)
|
|
178
|
+
throw new ConfigError(`Invalid ${name} value: ${value}`);
|
|
179
|
+
return parsed;
|
|
180
|
+
},
|
|
181
|
+
allowedHosts(envValue) {
|
|
182
|
+
return new Set(EnvParser.list(envValue)
|
|
183
|
+
.map((h) => EnvParser.normalizeHostValue(h))
|
|
184
|
+
.filter((h) => h !== null));
|
|
185
|
+
},
|
|
186
|
+
optionalFilePath(value) {
|
|
187
|
+
const trimmed = value?.trim();
|
|
188
|
+
return trimmed === '' ? undefined : trimmed;
|
|
189
|
+
},
|
|
190
|
+
normalizeHostValue(value) {
|
|
191
|
+
const raw = value.trim();
|
|
192
|
+
if (!raw)
|
|
193
|
+
return null;
|
|
194
|
+
const fromUrl = tryParseUrlHost(raw);
|
|
195
|
+
if (fromUrl !== undefined)
|
|
196
|
+
return fromUrl;
|
|
197
|
+
const lowered = raw.toLowerCase();
|
|
198
|
+
const fromBracket = tryParseIpv6Bracket(lowered);
|
|
199
|
+
if (fromBracket !== undefined)
|
|
200
|
+
return fromBracket;
|
|
201
|
+
if (isIP(lowered) === 6)
|
|
202
|
+
return stripTrailingDots(lowered);
|
|
203
|
+
return tryParseHostPort(lowered);
|
|
204
|
+
},
|
|
205
|
+
formatHostForUrl(hostname) {
|
|
206
|
+
if (hostname.includes(':') && !hostname.startsWith('['))
|
|
207
|
+
return `[${hostname}]`;
|
|
208
|
+
return hostname;
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
// ── Constants ───────────────────────────────────────────────────────
|
|
212
|
+
const MAX_HTML_BYTES = 10 * 1024 * 1024;
|
|
213
|
+
const MAX_INLINE_CONTENT_CHARS = EnvParser.integer(env['MAX_INLINE_CONTENT_CHARS'], 0, { min: 0, max: MAX_HTML_BYTES, envName: 'MAX_INLINE_CONTENT_CHARS' });
|
|
214
|
+
const DEFAULT_SESSION_TTL_MS = 30 * 60 * 1000;
|
|
215
|
+
const DEFAULT_SESSION_INIT_TIMEOUT_MS = 10000;
|
|
216
|
+
const DEFAULT_MAX_SESSIONS = 200;
|
|
217
|
+
const DEFAULT_USER_AGENT = `fetch-url-mcp/${serverVersion}`;
|
|
218
|
+
const DEFAULT_TOOL_TIMEOUT_PADDING_MS = 5000;
|
|
219
|
+
const DEFAULT_TRANSFORM_TIMEOUT_MS = 30000;
|
|
220
|
+
const DEFAULT_FETCH_TIMEOUT_MS = EnvParser.integer(env['FETCH_TIMEOUT_MS'], 15000, { min: 1000, max: 60000, envName: 'FETCH_TIMEOUT_MS' });
|
|
221
|
+
const DEFAULT_TOOL_TIMEOUT_MS = DEFAULT_FETCH_TIMEOUT_MS +
|
|
222
|
+
DEFAULT_TRANSFORM_TIMEOUT_MS +
|
|
223
|
+
DEFAULT_TOOL_TIMEOUT_PADDING_MS;
|
|
224
|
+
const DEFAULT_TASKS_MAX_TOTAL = EnvParser.integer(env['TASKS_MAX_TOTAL'], 5000, { min: 1, envName: 'TASKS_MAX_TOTAL' });
|
|
225
|
+
const DEFAULT_TASKS_MAX_PER_OWNER = EnvParser.integer(env['TASKS_MAX_PER_OWNER'], 1000, { min: 1, envName: 'TASKS_MAX_PER_OWNER' });
|
|
226
|
+
const RESOLVED_TASKS_MAX_PER_OWNER = Math.min(DEFAULT_TASKS_MAX_PER_OWNER, DEFAULT_TASKS_MAX_TOTAL);
|
|
227
|
+
function resolveWorkerResourceLimits() {
|
|
228
|
+
const limits = {};
|
|
229
|
+
const keys = {
|
|
230
|
+
maxOldGenerationSizeMb: 'TRANSFORM_WORKER_MAX_OLD_GENERATION_MB',
|
|
231
|
+
maxYoungGenerationSizeMb: 'TRANSFORM_WORKER_MAX_YOUNG_GENERATION_MB',
|
|
232
|
+
codeRangeSizeMb: 'TRANSFORM_WORKER_CODE_RANGE_MB',
|
|
233
|
+
stackSizeMb: 'TRANSFORM_WORKER_STACK_MB',
|
|
234
|
+
};
|
|
235
|
+
let hasLimits = false;
|
|
236
|
+
for (const [prop, envKey] of Object.entries(keys)) {
|
|
237
|
+
const val = EnvParser.optionalInteger(env[envKey], {
|
|
238
|
+
min: 1,
|
|
239
|
+
envName: envKey,
|
|
240
|
+
});
|
|
241
|
+
if (val !== undefined) {
|
|
242
|
+
limits[prop] = val;
|
|
243
|
+
hasLimits = true;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (hasLimits)
|
|
247
|
+
return limits;
|
|
248
|
+
// If no explicit limits are set, apply a default maxOldGenerationSizeMb based on constrained memory if available.
|
|
249
|
+
// This helps prevent OOM crashes in memory-constrained environments like AWS Lambda.
|
|
250
|
+
const constrained = process.constrainedMemory();
|
|
251
|
+
if (constrained > 0) {
|
|
252
|
+
const availableMb = Math.floor(constrained / (1024 * 1024));
|
|
253
|
+
// Set maxOldGenerationSizeMb to 75% of available memory, but no less than 64MB to allow some headroom for the young generation and code.
|
|
254
|
+
limits.maxOldGenerationSizeMb = Math.max(64, Math.floor(availableMb * 0.75));
|
|
255
|
+
return limits;
|
|
256
|
+
}
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
function buildAuthConfig(baseUrl) {
|
|
260
|
+
const parseUrl = (key) => EnvParser.url(env[key], key);
|
|
261
|
+
const issuerUrl = parseUrl('OAUTH_ISSUER_URL');
|
|
262
|
+
const authorizationUrl = parseUrl('OAUTH_AUTHORIZATION_URL');
|
|
263
|
+
const tokenUrl = parseUrl('OAUTH_TOKEN_URL');
|
|
264
|
+
const revocationUrl = parseUrl('OAUTH_REVOCATION_URL');
|
|
265
|
+
const registrationUrl = parseUrl('OAUTH_REGISTRATION_URL');
|
|
266
|
+
const introspectionUrl = parseUrl('OAUTH_INTROSPECTION_URL');
|
|
267
|
+
const resourceUrl = new URL('/mcp', baseUrl);
|
|
268
|
+
const oauthConfigured = issuerUrl !== undefined ||
|
|
269
|
+
authorizationUrl !== undefined ||
|
|
270
|
+
tokenUrl !== undefined ||
|
|
271
|
+
introspectionUrl !== undefined;
|
|
272
|
+
const tokens = EnvParser.list(env['ACCESS_TOKENS']);
|
|
273
|
+
if (env['API_KEY'])
|
|
274
|
+
tokens.push(env['API_KEY']);
|
|
275
|
+
return {
|
|
276
|
+
mode: oauthConfigured ? 'oauth' : 'static',
|
|
277
|
+
issuerUrl,
|
|
278
|
+
authorizationUrl,
|
|
279
|
+
tokenUrl,
|
|
280
|
+
revocationUrl,
|
|
281
|
+
registrationUrl,
|
|
282
|
+
introspectionUrl,
|
|
283
|
+
resourceUrl,
|
|
284
|
+
requiredScopes: EnvParser.list(env['OAUTH_REQUIRED_SCOPES']),
|
|
285
|
+
clientId: env['OAUTH_CLIENT_ID'],
|
|
286
|
+
clientSecret: env['OAUTH_CLIENT_SECRET'],
|
|
287
|
+
introspectionTimeoutMs: 5000,
|
|
288
|
+
staticTokens: [...new Set(tokens)],
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function buildHttpsConfig() {
|
|
292
|
+
const keyFile = EnvParser.optionalFilePath(env['SERVER_TLS_KEY_FILE']);
|
|
293
|
+
const certFile = EnvParser.optionalFilePath(env['SERVER_TLS_CERT_FILE']);
|
|
294
|
+
const caFile = EnvParser.optionalFilePath(env['SERVER_TLS_CA_FILE']);
|
|
295
|
+
if ((keyFile && !certFile) || (!keyFile && certFile)) {
|
|
296
|
+
throw new ConfigError('Both SERVER_TLS_KEY_FILE and SERVER_TLS_CERT_FILE must be set together');
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
enabled: Boolean(keyFile && certFile),
|
|
300
|
+
keyFile,
|
|
301
|
+
certFile,
|
|
302
|
+
caFile,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
// ── Blocked hosts ───────────────────────────────────────────────────
|
|
306
|
+
const LOOPBACK_V4 = buildIpv4([127, 0, 0, 1]);
|
|
307
|
+
const ANY_V4 = buildIpv4([0, 0, 0, 0]);
|
|
308
|
+
const METADATA_V4_AWS = buildIpv4([169, 254, 169, 254]);
|
|
309
|
+
const METADATA_V4_AZURE = buildIpv4([100, 100, 100, 200]);
|
|
310
|
+
const BLOCKED_HOSTS = new Set([
|
|
311
|
+
'localhost',
|
|
312
|
+
LOOPBACK_V4,
|
|
313
|
+
ANY_V4,
|
|
314
|
+
'::1',
|
|
315
|
+
METADATA_V4_AWS,
|
|
316
|
+
'metadata.google.internal',
|
|
317
|
+
'metadata.azure.com',
|
|
318
|
+
METADATA_V4_AZURE,
|
|
319
|
+
'instance-data',
|
|
320
|
+
]);
|
|
321
|
+
// ── Server binding ──────────────────────────────────────────────────
|
|
322
|
+
const host = (env['HOST'] ?? LOOPBACK_V4).trim();
|
|
323
|
+
const port = env['PORT']?.trim() === '0'
|
|
324
|
+
? 0
|
|
325
|
+
: EnvParser.integer(env['PORT'], 3000, {
|
|
326
|
+
min: 1024,
|
|
327
|
+
max: 65535,
|
|
328
|
+
envName: 'PORT',
|
|
329
|
+
});
|
|
330
|
+
const httpsConfig = buildHttpsConfig();
|
|
331
|
+
const allowRemote = EnvParser.boolean(env['ALLOW_REMOTE'], false, 'ALLOW_REMOTE');
|
|
332
|
+
const baseUrl = new URL(`${httpsConfig.enabled ? 'https' : 'http'}://${EnvParser.formatHostForUrl(host)}:${port}`);
|
|
333
|
+
const runtimeState = {
|
|
334
|
+
httpMode: false,
|
|
335
|
+
};
|
|
336
|
+
function buildServerConfig() {
|
|
337
|
+
const parseOptInt = (key, min = 1) => EnvParser.optionalInteger(env[key], { min, envName: key });
|
|
338
|
+
return {
|
|
339
|
+
name: 'fetch-url-mcp',
|
|
340
|
+
version: serverVersion,
|
|
341
|
+
port,
|
|
342
|
+
host,
|
|
343
|
+
https: httpsConfig,
|
|
344
|
+
sessionTtlMs: DEFAULT_SESSION_TTL_MS,
|
|
345
|
+
sessionInitTimeoutMs: DEFAULT_SESSION_INIT_TIMEOUT_MS,
|
|
346
|
+
maxSessions: DEFAULT_MAX_SESSIONS,
|
|
347
|
+
http: {
|
|
348
|
+
headersTimeoutMs: parseOptInt('SERVER_HEADERS_TIMEOUT_MS'),
|
|
349
|
+
requestTimeoutMs: parseOptInt('SERVER_REQUEST_TIMEOUT_MS', 0),
|
|
350
|
+
keepAliveTimeoutMs: parseOptInt('SERVER_KEEP_ALIVE_TIMEOUT_MS'),
|
|
351
|
+
keepAliveTimeoutBufferMs: parseOptInt('SERVER_KEEP_ALIVE_TIMEOUT_BUFFER_MS', 0),
|
|
352
|
+
maxHeadersCount: parseOptInt('SERVER_MAX_HEADERS_COUNT'),
|
|
353
|
+
maxConnections: EnvParser.integer(env['SERVER_MAX_CONNECTIONS'], 0, {
|
|
354
|
+
min: 0,
|
|
355
|
+
envName: 'SERVER_MAX_CONNECTIONS',
|
|
356
|
+
}),
|
|
357
|
+
blockPrivateConnections: EnvParser.boolean(env['SERVER_BLOCK_PRIVATE_CONNECTIONS'], false, 'SERVER_BLOCK_PRIVATE_CONNECTIONS'),
|
|
358
|
+
shutdownCloseIdleConnections: true,
|
|
359
|
+
shutdownCloseAllConnections: false,
|
|
360
|
+
},
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function buildFetcherConfig() {
|
|
364
|
+
return {
|
|
365
|
+
timeout: DEFAULT_FETCH_TIMEOUT_MS,
|
|
366
|
+
maxRedirects: 5,
|
|
367
|
+
userAgent: env['USER_AGENT'] ?? DEFAULT_USER_AGENT,
|
|
368
|
+
maxContentLength: MAX_HTML_BYTES,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function buildTransformConfig() {
|
|
372
|
+
return {
|
|
373
|
+
timeoutMs: DEFAULT_TRANSFORM_TIMEOUT_MS,
|
|
374
|
+
stageWarnRatio: 0.5,
|
|
375
|
+
metadataFormat: 'markdown',
|
|
376
|
+
maxWorkerScale: 4,
|
|
377
|
+
cancelAckTimeoutMs: EnvParser.integer(env['TRANSFORM_CANCEL_ACK_TIMEOUT_MS'], 200, { min: 50, max: 5000, envName: 'TRANSFORM_CANCEL_ACK_TIMEOUT_MS' }),
|
|
378
|
+
workerMode: EnvParser.transformWorkerMode(env['TRANSFORM_WORKER_MODE']),
|
|
379
|
+
workerResourceLimits: resolveWorkerResourceLimits(),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function buildTasksConfig() {
|
|
383
|
+
return {
|
|
384
|
+
maxTotal: DEFAULT_TASKS_MAX_TOTAL,
|
|
385
|
+
maxPerOwner: RESOLVED_TASKS_MAX_PER_OWNER,
|
|
386
|
+
emitStatusNotifications: EnvParser.boolean(env['TASKS_STATUS_NOTIFICATIONS'], false, 'TASKS_STATUS_NOTIFICATIONS'),
|
|
387
|
+
requireInterception: EnvParser.boolean(env['TASKS_REQUIRE_INTERCEPTION'], true, 'TASKS_REQUIRE_INTERCEPTION'),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function buildCacheConfig() {
|
|
391
|
+
return {
|
|
392
|
+
enabled: EnvParser.boolean(env['CACHE_ENABLED'], true, 'CACHE_ENABLED'),
|
|
393
|
+
ttl: 86400,
|
|
394
|
+
maxKeys: 100,
|
|
395
|
+
maxSizeBytes: 50 * 1024 * 1024,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function buildNoiseRemovalConfig() {
|
|
399
|
+
return {
|
|
400
|
+
extraTokens: EnvParser.list(env['FETCH_URL_MCP_EXTRA_NOISE_TOKENS']),
|
|
401
|
+
extraSelectors: EnvParser.list(env['FETCH_URL_MCP_EXTRA_NOISE_SELECTORS']),
|
|
402
|
+
enabledCategories: [
|
|
403
|
+
'cookie-banners',
|
|
404
|
+
'newsletters',
|
|
405
|
+
'social-share',
|
|
406
|
+
'nav-footer',
|
|
407
|
+
'author-blocks',
|
|
408
|
+
'related-content',
|
|
409
|
+
],
|
|
410
|
+
debug: false,
|
|
411
|
+
aggressiveMode: false,
|
|
412
|
+
preserveSvgCanvas: false,
|
|
413
|
+
weights: {
|
|
414
|
+
hidden: 50,
|
|
415
|
+
structural: 50,
|
|
416
|
+
promo: 35,
|
|
417
|
+
stickyFixed: 30,
|
|
418
|
+
threshold: 50,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function buildMarkdownCleanupConfig() {
|
|
423
|
+
return {
|
|
424
|
+
promoteOrphanHeadings: true,
|
|
425
|
+
removeSkipLinks: true,
|
|
426
|
+
removeTocBlocks: true,
|
|
427
|
+
removeTypeDocComments: true,
|
|
428
|
+
headingKeywords: EnvParser.list(env['MARKDOWN_HEADING_KEYWORDS'], DEFAULT_HEADING_KEYWORDS),
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
// ── Config object ───────────────────────────────────────────────────
|
|
432
|
+
export const config = {
|
|
433
|
+
server: buildServerConfig(),
|
|
434
|
+
fetcher: buildFetcherConfig(),
|
|
435
|
+
transform: buildTransformConfig(),
|
|
436
|
+
tools: {
|
|
437
|
+
enabled: ['fetch-url'],
|
|
438
|
+
timeoutMs: DEFAULT_TOOL_TIMEOUT_MS,
|
|
439
|
+
},
|
|
440
|
+
tasks: buildTasksConfig(),
|
|
441
|
+
cache: buildCacheConfig(),
|
|
442
|
+
extraction: {
|
|
443
|
+
maxBlockLength: 5000,
|
|
444
|
+
minParagraphLength: 10,
|
|
445
|
+
},
|
|
446
|
+
noiseRemoval: buildNoiseRemovalConfig(),
|
|
447
|
+
markdownCleanup: buildMarkdownCleanupConfig(),
|
|
448
|
+
i18n: {
|
|
449
|
+
locale: EnvParser.locale(env['FETCH_URL_MCP_LOCALE']),
|
|
450
|
+
},
|
|
451
|
+
logging: {
|
|
452
|
+
level: EnvParser.logLevel(env['LOG_LEVEL']),
|
|
453
|
+
format: env['LOG_FORMAT']?.toLowerCase() === 'json' ? 'json' : 'text',
|
|
454
|
+
},
|
|
455
|
+
constants: {
|
|
456
|
+
maxHtmlBytes: MAX_HTML_BYTES,
|
|
457
|
+
maxUrlLength: 2048,
|
|
458
|
+
maxInlineContentChars: MAX_INLINE_CONTENT_CHARS,
|
|
459
|
+
},
|
|
460
|
+
security: {
|
|
461
|
+
blockedHosts: BLOCKED_HOSTS,
|
|
462
|
+
allowedHosts: EnvParser.allowedHosts(env['ALLOWED_HOSTS']),
|
|
463
|
+
apiKey: env['API_KEY'],
|
|
464
|
+
allowRemote,
|
|
465
|
+
allowLocalFetch: EnvParser.boolean(env['ALLOW_LOCAL_FETCH'], false, 'ALLOW_LOCAL_FETCH'),
|
|
466
|
+
},
|
|
467
|
+
auth: buildAuthConfig(baseUrl),
|
|
468
|
+
rateLimit: {
|
|
469
|
+
enabled: true,
|
|
470
|
+
maxRequests: 100,
|
|
471
|
+
windowMs: 60000,
|
|
472
|
+
cleanupIntervalMs: 60000,
|
|
473
|
+
},
|
|
474
|
+
runtime: runtimeState,
|
|
475
|
+
};
|
|
476
|
+
export function enableHttpMode() {
|
|
477
|
+
runtimeState.httpMode = true;
|
|
478
|
+
}
|