@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/cli.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface CliValues {
|
|
2
2
|
readonly stdio: boolean;
|
|
3
3
|
readonly http: boolean;
|
|
4
4
|
readonly help: boolean;
|
|
@@ -12,8 +12,7 @@ interface CliParseFailure {
|
|
|
12
12
|
readonly ok: false;
|
|
13
13
|
readonly message: string;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
type CliParseResult = CliParseSuccess | CliParseFailure;
|
|
16
16
|
export declare function renderCliUsage(): string;
|
|
17
17
|
export declare function parseCliArgs(args: readonly string[]): CliParseResult;
|
|
18
18
|
export {};
|
|
19
|
-
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
|
-
import { getErrorMessage } from './lib/
|
|
2
|
+
import { getErrorMessage } from './lib/utils.js';
|
|
3
3
|
const usageLines = [
|
|
4
4
|
'Fetch URL MCP server',
|
|
5
5
|
'',
|
|
@@ -63,4 +63,3 @@ export function parseCliArgs(args) {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
//# sourceMappingURL=cli.js.map
|
package/dist/http/auth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
2
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
3
3
|
import { type RequestContext } from './helpers.js';
|
|
4
4
|
declare class CorsPolicy {
|
|
5
5
|
handle(ctx: RequestContext): boolean;
|
|
@@ -8,7 +8,9 @@ export declare const corsPolicy: CorsPolicy;
|
|
|
8
8
|
declare class HostOriginPolicy {
|
|
9
9
|
validate(ctx: RequestContext): boolean;
|
|
10
10
|
private resolveHostHeader;
|
|
11
|
-
private
|
|
11
|
+
private resolveRequestOrigin;
|
|
12
|
+
private resolveOrigin;
|
|
13
|
+
private defaultPortForScheme;
|
|
12
14
|
private reject;
|
|
13
15
|
}
|
|
14
16
|
export declare const hostOriginPolicy: HostOriginPolicy;
|
|
@@ -33,6 +35,7 @@ declare class AuthService {
|
|
|
33
35
|
private buildIntrospectionRequest;
|
|
34
36
|
private requestIntrospection;
|
|
35
37
|
private buildIntrospectionAuthInfo;
|
|
38
|
+
private assertRequiredScopes;
|
|
36
39
|
private verifyWithIntrospection;
|
|
37
40
|
}
|
|
38
41
|
export declare function applyUnauthorizedAuthHeaders(req: IncomingMessage, res: ServerResponse): void;
|
|
@@ -46,4 +49,3 @@ export declare function buildProtectedResourceMetadataDocument(req: IncomingMess
|
|
|
46
49
|
export declare function isProtectedResourceMetadataPath(pathname: string): boolean;
|
|
47
50
|
export declare const authService: AuthService;
|
|
48
51
|
export {};
|
|
49
|
-
//# sourceMappingURL=auth.d.ts.map
|
package/dist/http/auth.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { InvalidTokenError, ServerError, } from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
2
1
|
import { Buffer } from 'node:buffer';
|
|
3
2
|
import { randomBytes } from 'node:crypto';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { normalizeHost } from '../lib/
|
|
7
|
-
import {
|
|
3
|
+
import { InvalidTokenError, ServerError, } from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
4
|
+
import { config } from '../lib/core.js';
|
|
5
|
+
import { normalizeHost } from '../lib/url.js';
|
|
6
|
+
import { hmacSha256Hex, timingSafeEqualUtf8 } from '../lib/utils.js';
|
|
7
|
+
import { isObject } from '../lib/utils.js';
|
|
8
8
|
import { getHeaderValue, sendEmpty, sendError, sendJson, } from './helpers.js';
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// CORS
|
|
@@ -20,9 +20,6 @@ class CorsPolicy {
|
|
|
20
20
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
21
21
|
res.setHeader('Vary', 'Origin');
|
|
22
22
|
}
|
|
23
|
-
else {
|
|
24
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
25
|
-
}
|
|
26
23
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE');
|
|
27
24
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key, MCP-Protocol-Version, MCP-Session-ID, X-MCP-Session-ID, Last-Event-ID');
|
|
28
25
|
res.setHeader('Access-Control-Expose-Headers', 'MCP-Session-ID, X-MCP-Session-ID, MCP-Protocol-Version, WWW-Authenticate');
|
|
@@ -77,10 +74,16 @@ class HostOriginPolicy {
|
|
|
77
74
|
const originHeader = getHeaderValue(req, 'origin');
|
|
78
75
|
if (!originHeader)
|
|
79
76
|
return true;
|
|
80
|
-
const
|
|
81
|
-
|
|
77
|
+
const requestOrigin = this.resolveRequestOrigin(req);
|
|
78
|
+
const origin = this.resolveOrigin(originHeader);
|
|
79
|
+
if (!requestOrigin || !origin)
|
|
82
80
|
return this.reject(res, 403, 'Invalid Origin header');
|
|
83
|
-
if (!ALLOWED_HOSTS.has(
|
|
81
|
+
if (!ALLOWED_HOSTS.has(origin.host))
|
|
82
|
+
return this.reject(res, 403, 'Origin not allowed');
|
|
83
|
+
const isSameOrigin = requestOrigin.scheme === origin.scheme &&
|
|
84
|
+
requestOrigin.host === origin.host &&
|
|
85
|
+
requestOrigin.port === origin.port;
|
|
86
|
+
if (!isSameOrigin)
|
|
84
87
|
return this.reject(res, 403, 'Origin not allowed');
|
|
85
88
|
return true;
|
|
86
89
|
}
|
|
@@ -90,17 +93,52 @@ class HostOriginPolicy {
|
|
|
90
93
|
return null;
|
|
91
94
|
return normalizeHost(host);
|
|
92
95
|
}
|
|
93
|
-
|
|
96
|
+
resolveRequestOrigin(req) {
|
|
97
|
+
const hostHeader = getHeaderValue(req, 'host');
|
|
98
|
+
if (!hostHeader)
|
|
99
|
+
return null;
|
|
100
|
+
const isEncrypted = req.socket.encrypted === true;
|
|
101
|
+
const scheme = isEncrypted ? 'https' : 'http';
|
|
102
|
+
try {
|
|
103
|
+
const parsed = new URL(`${scheme}://${hostHeader}`);
|
|
104
|
+
const normalizedHost = normalizeHost(parsed.host);
|
|
105
|
+
if (!normalizedHost)
|
|
106
|
+
return null;
|
|
107
|
+
return {
|
|
108
|
+
scheme,
|
|
109
|
+
host: normalizedHost,
|
|
110
|
+
port: parsed.port || this.defaultPortForScheme(scheme),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
resolveOrigin(origin) {
|
|
94
118
|
if (origin === 'null')
|
|
95
119
|
return null;
|
|
96
120
|
try {
|
|
97
121
|
const parsed = new URL(origin);
|
|
98
|
-
|
|
122
|
+
const scheme = parsed.protocol === 'https:' ? 'https' : 'http';
|
|
123
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const normalizedHost = normalizeHost(parsed.host);
|
|
127
|
+
if (!normalizedHost)
|
|
128
|
+
return null;
|
|
129
|
+
return {
|
|
130
|
+
scheme,
|
|
131
|
+
host: normalizedHost,
|
|
132
|
+
port: parsed.port || this.defaultPortForScheme(scheme),
|
|
133
|
+
};
|
|
99
134
|
}
|
|
100
135
|
catch {
|
|
101
136
|
return null;
|
|
102
137
|
}
|
|
103
138
|
}
|
|
139
|
+
defaultPortForScheme(scheme) {
|
|
140
|
+
return scheme === 'https' ? '443' : '80';
|
|
141
|
+
}
|
|
104
142
|
reject(res, status, message) {
|
|
105
143
|
sendJson(res, status, { error: message });
|
|
106
144
|
return false;
|
|
@@ -293,6 +331,16 @@ class AuthService {
|
|
|
293
331
|
info.expiresAt = expiresAt;
|
|
294
332
|
return info;
|
|
295
333
|
}
|
|
334
|
+
assertRequiredScopes(tokenScopes) {
|
|
335
|
+
const { requiredScopes } = config.auth;
|
|
336
|
+
if (requiredScopes.length === 0)
|
|
337
|
+
return;
|
|
338
|
+
const tokenScopeSet = new Set(tokenScopes);
|
|
339
|
+
const missing = requiredScopes.filter((s) => !tokenScopeSet.has(s));
|
|
340
|
+
if (missing.length > 0) {
|
|
341
|
+
throw new InvalidTokenError('Insufficient scope');
|
|
342
|
+
}
|
|
343
|
+
}
|
|
296
344
|
async verifyWithIntrospection(token, signal) {
|
|
297
345
|
if (!config.auth.introspectionUrl) {
|
|
298
346
|
throw new ServerError('Introspection not configured');
|
|
@@ -302,7 +350,9 @@ class AuthService {
|
|
|
302
350
|
if (!isObject(payload) || payload['active'] !== true) {
|
|
303
351
|
throw new InvalidTokenError('Token is inactive');
|
|
304
352
|
}
|
|
305
|
-
|
|
353
|
+
const info = this.buildIntrospectionAuthInfo(token, payload);
|
|
354
|
+
this.assertRequiredScopes(info.scopes);
|
|
355
|
+
return info;
|
|
306
356
|
}
|
|
307
357
|
}
|
|
308
358
|
function resolvePublicOrigin(req) {
|
|
@@ -347,4 +397,3 @@ export function isProtectedResourceMetadataPath(pathname) {
|
|
|
347
397
|
pathname === '/.well-known/oauth-protected-resource/mcp');
|
|
348
398
|
}
|
|
349
399
|
export const authService = new AuthService();
|
|
350
|
-
//# sourceMappingURL=auth.js.map
|
package/dist/http/health.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { SessionStore } from '../lib/
|
|
1
|
+
import type { SessionStore } from '../lib/core.js';
|
|
2
2
|
import { type RequestContext } from './helpers.js';
|
|
3
3
|
export declare function resetEventLoopMonitoring(): void;
|
|
4
4
|
export declare function disableEventLoopMonitoring(): void;
|
|
5
5
|
export declare function shouldHandleHealthRoute(ctx: RequestContext): boolean;
|
|
6
6
|
export declare function sendHealthRouteResponse(store: SessionStore, ctx: RequestContext, authPresent: boolean): boolean;
|
|
7
|
-
//# sourceMappingURL=health.d.ts.map
|
package/dist/http/health.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { freemem, hostname, totalmem } from 'node:os';
|
|
2
2
|
import { monitorEventLoopDelay, performance } from 'node:perf_hooks';
|
|
3
3
|
import process from 'node:process';
|
|
4
|
-
import { keys as cacheKeys } from '../lib/
|
|
5
|
-
import { config, serverVersion } from '../lib/
|
|
4
|
+
import { keys as cacheKeys } from '../lib/core.js';
|
|
5
|
+
import { config, serverVersion } from '../lib/core.js';
|
|
6
6
|
import { getTransformPoolStats } from '../transform/transform.js';
|
|
7
7
|
import { sendJson } from './helpers.js';
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
@@ -110,20 +110,14 @@ function isVerboseHealthRequest(ctx) {
|
|
|
110
110
|
const normalized = value.trim().toLowerCase();
|
|
111
111
|
return normalized === '1' || normalized === 'true';
|
|
112
112
|
}
|
|
113
|
-
function
|
|
113
|
+
function isHealthRoute(ctx) {
|
|
114
114
|
return ctx.method === 'GET' && ctx.url.pathname === '/health';
|
|
115
115
|
}
|
|
116
116
|
function isVerboseHealthRoute(ctx) {
|
|
117
|
-
return
|
|
118
|
-
}
|
|
119
|
-
function isHealthRoute(ctx) {
|
|
120
|
-
return isGetHealthRoute(ctx);
|
|
117
|
+
return isHealthRoute(ctx) && isVerboseHealthRequest(ctx);
|
|
121
118
|
}
|
|
122
119
|
function ensureHealthAuthIfNeeded(ctx, authPresent) {
|
|
123
|
-
if (!
|
|
124
|
-
return true;
|
|
125
|
-
const isVerbose = isVerboseHealthRequest(ctx);
|
|
126
|
-
if (!isVerbose)
|
|
120
|
+
if (!isVerboseHealthRoute(ctx))
|
|
127
121
|
return true;
|
|
128
122
|
if (!config.security.allowRemote)
|
|
129
123
|
return true;
|
|
@@ -135,14 +129,10 @@ function ensureHealthAuthIfNeeded(ctx, authPresent) {
|
|
|
135
129
|
return false;
|
|
136
130
|
}
|
|
137
131
|
function resolveHealthDiagnosticsMode(ctx, authPresent) {
|
|
138
|
-
|
|
139
|
-
return false;
|
|
140
|
-
if (authPresent)
|
|
141
|
-
return true;
|
|
142
|
-
return !config.security.allowRemote;
|
|
132
|
+
return (isVerboseHealthRoute(ctx) && (authPresent || !config.security.allowRemote));
|
|
143
133
|
}
|
|
144
134
|
export function shouldHandleHealthRoute(ctx) {
|
|
145
|
-
return
|
|
135
|
+
return isHealthRoute(ctx);
|
|
146
136
|
}
|
|
147
137
|
export function sendHealthRouteResponse(store, ctx, authPresent) {
|
|
148
138
|
if (!shouldHandleHealthRoute(ctx))
|
|
@@ -153,4 +143,3 @@ export function sendHealthRouteResponse(store, ctx, authPresent) {
|
|
|
153
143
|
sendHealth(store, ctx.res, includeDiagnostics);
|
|
154
144
|
return true;
|
|
155
145
|
}
|
|
156
|
-
//# sourceMappingURL=health.js.map
|
package/dist/http/helpers.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import type { IncomingMessage, Server, ServerResponse } from 'node:http';
|
|
2
|
+
import type { Server as HttpsServer } from 'node:https';
|
|
1
3
|
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
2
4
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
5
|
import type { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
6
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
5
|
-
import type {
|
|
6
|
-
import type { Server as HttpsServer } from 'node:https';
|
|
7
|
-
import type { JsonRpcId } from '../lib/mcp-validator.js';
|
|
7
|
+
import type { JsonRpcId } from '../lib/mcp-tools.js';
|
|
8
8
|
export type NetworkServer = Server | HttpsServer;
|
|
9
9
|
export interface RequestContext {
|
|
10
10
|
req: IncomingMessage;
|
|
@@ -48,4 +48,3 @@ declare class JsonBodyReader {
|
|
|
48
48
|
}
|
|
49
49
|
export declare const jsonBodyReader: JsonBodyReader;
|
|
50
50
|
export {};
|
|
51
|
-
//# sourceMappingURL=helpers.d.ts.map
|
package/dist/http/helpers.js
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { Writable } from 'node:stream';
|
|
3
3
|
import { pipeline } from 'node:stream/promises';
|
|
4
|
-
import { config } from '../lib/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import { config } from '../lib/core.js';
|
|
5
|
+
import { logWarn } from '../lib/core.js';
|
|
6
|
+
import { composeCloseHandlers } from '../lib/core.js';
|
|
7
|
+
import { createDefaultBlockList, normalizeIpForBlockList } from '../lib/url.js';
|
|
8
|
+
import { getErrorMessage, toError } from '../lib/utils.js';
|
|
9
|
+
function abortControllerBestEffort(controller) {
|
|
10
|
+
if (!controller.signal.aborted)
|
|
11
|
+
controller.abort();
|
|
12
|
+
}
|
|
13
|
+
function destroyRequestBestEffort(req) {
|
|
14
|
+
try {
|
|
15
|
+
req.destroy();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Best-effort only.
|
|
19
|
+
}
|
|
20
|
+
}
|
|
9
21
|
// ---------------------------------------------------------------------------
|
|
10
22
|
// Response helpers
|
|
11
23
|
// ---------------------------------------------------------------------------
|
|
@@ -90,8 +102,7 @@ export function createRequestAbortSignal(req) {
|
|
|
90
102
|
const abortRequest = () => {
|
|
91
103
|
if (cleanedUp)
|
|
92
104
|
return;
|
|
93
|
-
|
|
94
|
-
controller.abort();
|
|
105
|
+
abortControllerBestEffort(controller);
|
|
95
106
|
};
|
|
96
107
|
if (req.destroyed) {
|
|
97
108
|
abortRequest();
|
|
@@ -256,12 +267,7 @@ class JsonBodyReader {
|
|
|
256
267
|
if (contentLengthHeader) {
|
|
257
268
|
const contentLength = Number.parseInt(contentLengthHeader, 10);
|
|
258
269
|
if (Number.isFinite(contentLength) && contentLength > limit) {
|
|
259
|
-
|
|
260
|
-
req.destroy();
|
|
261
|
-
}
|
|
262
|
-
catch {
|
|
263
|
-
// Best-effort only.
|
|
264
|
-
}
|
|
270
|
+
destroyRequestBestEffort(req);
|
|
265
271
|
throw new JsonBodyError('payload-too-large', 'Payload too large');
|
|
266
272
|
}
|
|
267
273
|
}
|
|
@@ -294,12 +300,7 @@ class JsonBodyReader {
|
|
|
294
300
|
if (!signal)
|
|
295
301
|
return null;
|
|
296
302
|
const listener = () => {
|
|
297
|
-
|
|
298
|
-
req.destroy();
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
// Best-effort only.
|
|
302
|
-
}
|
|
303
|
+
destroyRequestBestEffort(req);
|
|
303
304
|
};
|
|
304
305
|
if (signal.aborted) {
|
|
305
306
|
listener();
|
|
@@ -332,7 +333,7 @@ class JsonBodyReader {
|
|
|
332
333
|
const buf = this.normalizeChunk(chunk);
|
|
333
334
|
size += buf.length;
|
|
334
335
|
if (size > limit) {
|
|
335
|
-
req
|
|
336
|
+
destroyRequestBestEffort(req);
|
|
336
337
|
callback(new JsonBodyError('payload-too-large', 'Payload too large'));
|
|
337
338
|
return;
|
|
338
339
|
}
|
|
@@ -369,4 +370,3 @@ class JsonBodyReader {
|
|
|
369
370
|
}
|
|
370
371
|
}
|
|
371
372
|
export const jsonBodyReader = new JsonBodyReader();
|
|
372
|
-
//# sourceMappingURL=helpers.js.map
|
package/dist/http/native.d.ts
CHANGED
package/dist/http/native.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
2
|
-
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
3
1
|
import { randomUUID } from 'node:crypto';
|
|
4
2
|
import { readFileSync } from 'node:fs';
|
|
5
3
|
import { createServer, } from 'node:http';
|
|
6
4
|
import { createServer as createHttpsServer, } from 'node:https';
|
|
7
5
|
import { hostname } from 'node:os';
|
|
8
6
|
import process from 'node:process';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
7
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
8
|
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { config, enableHttpMode } from '../lib/core.js';
|
|
10
|
+
import { logError, logInfo, registerMcpSessionServer, resolveMcpSessionIdByServer, runWithRequestContext, unregisterMcpSessionServer, unregisterMcpSessionServerByServer, } from '../lib/core.js';
|
|
11
|
+
import { composeCloseHandlers, createSessionStore, createSlotTracker, ensureSessionCapacity, reserveSessionSlot, startSessionCleanupLoop, } from '../lib/core.js';
|
|
12
|
+
import { handleDownload } from '../lib/http.js';
|
|
13
|
+
import { acceptsEventStream, acceptsJsonAndEventStream, isJsonRpcBatchRequest, isMcpRequestBody, } from '../lib/mcp-tools.js';
|
|
14
|
+
import { cancelTasksForOwner } from '../lib/mcp-tools.js';
|
|
15
|
+
import { toError } from '../lib/utils.js';
|
|
16
|
+
import { applyHttpServerTuning, drainConnectionsOnShutdown, } from '../lib/utils.js';
|
|
17
|
+
import { isObject } from '../lib/utils.js';
|
|
18
18
|
import { createMcpServerForHttpSession } from '../server.js';
|
|
19
19
|
import { applyUnauthorizedAuthHeaders, assertHttpModeConfiguration, authService, buildAuthFingerprint, buildProtectedResourceMetadataDocument, corsPolicy, ensureMcpProtocolVersion, hostOriginPolicy, isProtectedResourceMetadataPath, SUPPORTED_MCP_PROTOCOL_VERSIONS, } from './auth.js';
|
|
20
20
|
import { disableEventLoopMonitoring, resetEventLoopMonitoring, sendHealthRouteResponse, shouldHandleHealthRoute, } from './health.js';
|
|
@@ -37,7 +37,7 @@ function resolveRequestedProtocolVersion(body) {
|
|
|
37
37
|
if (normalized.length === 0)
|
|
38
38
|
return DEFAULT_MCP_PROTOCOL_VERSION;
|
|
39
39
|
if (!SUPPORTED_MCP_PROTOCOL_VERSIONS.has(normalized)) {
|
|
40
|
-
return
|
|
40
|
+
return null;
|
|
41
41
|
}
|
|
42
42
|
return normalized;
|
|
43
43
|
}
|
|
@@ -71,10 +71,10 @@ class McpSessionGateway {
|
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
const requestId = body.id ?? null;
|
|
74
|
-
const
|
|
75
|
-
const isInitNotification =
|
|
74
|
+
const isInitializedMethod = isInitializedNotification(body.method);
|
|
75
|
+
const isInitNotification = isInitializedMethod && body.id === undefined;
|
|
76
76
|
const sessionId = getMcpSessionId(ctx.req);
|
|
77
|
-
if (
|
|
77
|
+
if (isInitializedMethod && !isInitNotification) {
|
|
78
78
|
sendError(ctx.res, -32600, 'notifications/initialized must be sent as a notification', 400, requestId);
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
@@ -185,6 +185,10 @@ class McpSessionGateway {
|
|
|
185
185
|
return null;
|
|
186
186
|
}
|
|
187
187
|
const negotiatedProtocolVersion = resolveRequestedProtocolVersion(ctx.body);
|
|
188
|
+
if (!negotiatedProtocolVersion) {
|
|
189
|
+
sendError(ctx.res, -32602, `Unsupported protocolVersion; supported versions: ${[...SUPPORTED_MCP_PROTOCOL_VERSIONS].join(', ')}`, 400, requestId);
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
188
192
|
return this.createNewSession(ctx, requestId, negotiatedProtocolVersion);
|
|
189
193
|
}
|
|
190
194
|
getExistingTransport(sessionId, authFingerprint, res, requestId) {
|
|
@@ -294,6 +298,7 @@ class McpSessionGateway {
|
|
|
294
298
|
sendError(res, -32000, 'Server busy', 503, requestId);
|
|
295
299
|
return false;
|
|
296
300
|
}
|
|
301
|
+
// Double-check: capacity may have changed during the async eviction window above.
|
|
297
302
|
if (!reserveSessionSlot(this.store, config.server.maxSessions)) {
|
|
298
303
|
sendError(res, -32000, 'Server busy', 503, requestId);
|
|
299
304
|
return false;
|
|
@@ -366,7 +371,7 @@ class HttpDispatcher {
|
|
|
366
371
|
if (!auth)
|
|
367
372
|
return;
|
|
368
373
|
const authCtx = { ...ctx, auth };
|
|
369
|
-
if (this.tryHandleDownloadRoute(
|
|
374
|
+
if (this.tryHandleDownloadRoute(authCtx))
|
|
370
375
|
return;
|
|
371
376
|
if (ctx.url.pathname === '/mcp') {
|
|
372
377
|
const handled = await this.handleMcpRoutes(authCtx);
|
|
@@ -404,9 +409,7 @@ class HttpDispatcher {
|
|
|
404
409
|
}
|
|
405
410
|
catch (err) {
|
|
406
411
|
applyUnauthorizedAuthHeaders(ctx.req, ctx.res);
|
|
407
|
-
|
|
408
|
-
error: err instanceof Error ? err.message : 'Unauthorized',
|
|
409
|
-
});
|
|
412
|
+
sendError(ctx.res, -32000, err instanceof Error ? err.message : 'Unauthorized', 401);
|
|
410
413
|
return null;
|
|
411
414
|
}
|
|
412
415
|
}
|
|
@@ -524,12 +527,18 @@ function createNetworkServer(listener) {
|
|
|
524
527
|
if (!keyFile || !certFile) {
|
|
525
528
|
throw new Error('HTTPS enabled but SERVER_TLS_KEY_FILE / SERVER_TLS_CERT_FILE are missing');
|
|
526
529
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
530
|
+
let tlsOptions;
|
|
531
|
+
try {
|
|
532
|
+
tlsOptions = {
|
|
533
|
+
key: readFileSync(keyFile),
|
|
534
|
+
cert: readFileSync(certFile),
|
|
535
|
+
};
|
|
536
|
+
if (caFile) {
|
|
537
|
+
tlsOptions.ca = readFileSync(caFile);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
catch (err) {
|
|
541
|
+
throw new Error(`Failed to read TLS files (key=${keyFile}, cert=${certFile}): ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
533
542
|
}
|
|
534
543
|
return createHttpsServer(tlsOptions, listener);
|
|
535
544
|
}
|
|
@@ -629,4 +638,3 @@ export async function startHttpServer() {
|
|
|
629
638
|
}),
|
|
630
639
|
};
|
|
631
640
|
}
|
|
632
|
-
//# sourceMappingURL=native.js.map
|
package/dist/http/rate-limit.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { startAbortableIntervalLoop } from '../lib/
|
|
1
|
+
import { logWarn } from '../lib/core.js';
|
|
2
|
+
import { isAbortError } from '../lib/utils.js';
|
|
3
|
+
import { startAbortableIntervalLoop } from '../lib/utils.js';
|
|
4
4
|
import { sendJson } from './helpers.js';
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
// Rate limiter
|
|
@@ -85,4 +85,3 @@ class RateLimiter {
|
|
|
85
85
|
export function createRateLimitManagerImpl(options) {
|
|
86
86
|
return new RateLimiter(options);
|
|
87
87
|
}
|
|
88
|
-
//# sourceMappingURL=rate-limit.js.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import process from 'node:process';
|
|
3
|
+
import { serverVersion } from './lib/core.js';
|
|
4
|
+
import { logError } from './lib/core.js';
|
|
5
|
+
import { toError } from './lib/utils.js';
|
|
3
6
|
import { parseCliArgs, renderCliUsage } from './cli.js';
|
|
4
7
|
import { startHttpServer } from './http/native.js';
|
|
5
|
-
import { serverVersion } from './lib/config.js';
|
|
6
|
-
import { toError } from './lib/errors.js';
|
|
7
|
-
import { logError } from './lib/observability.js';
|
|
8
8
|
import { startStdioServer } from './server.js';
|
|
9
9
|
const FORCE_EXIT_TIMEOUT_MS = 10_000;
|
|
10
10
|
let forcedExitTimer;
|
|
11
|
+
function writeAndExit(stream, text, code) {
|
|
12
|
+
stream.write(text);
|
|
13
|
+
process.exit(code);
|
|
14
|
+
}
|
|
11
15
|
function scheduleForcedExit(reason) {
|
|
12
16
|
if (forcedExitTimer)
|
|
13
17
|
return;
|
|
@@ -19,18 +23,14 @@ function scheduleForcedExit(reason) {
|
|
|
19
23
|
}
|
|
20
24
|
const parseResult = parseCliArgs(process.argv.slice(2));
|
|
21
25
|
if (!parseResult.ok) {
|
|
22
|
-
process.stderr
|
|
23
|
-
process.stderr.write(renderCliUsage());
|
|
24
|
-
process.exit(1);
|
|
26
|
+
writeAndExit(process.stderr, `Invalid arguments: ${parseResult.message}\n\n${renderCliUsage()}`, 1);
|
|
25
27
|
}
|
|
26
28
|
const { values } = parseResult;
|
|
27
29
|
if (values.help) {
|
|
28
|
-
process.stdout
|
|
29
|
-
process.exit(0);
|
|
30
|
+
writeAndExit(process.stdout, renderCliUsage(), 0);
|
|
30
31
|
}
|
|
31
32
|
if (values.version) {
|
|
32
|
-
process.stdout
|
|
33
|
-
process.exit(0);
|
|
33
|
+
writeAndExit(process.stdout, `${serverVersion}\n`, 0);
|
|
34
34
|
}
|
|
35
35
|
const isStdioMode = !values.http;
|
|
36
36
|
let isShuttingDown = false;
|
|
@@ -45,19 +45,19 @@ function attemptShutdown(signal) {
|
|
|
45
45
|
process.stderr.write('Attempting graceful shutdown...\n');
|
|
46
46
|
void shutdownHandlerRef.current(signal);
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
function registerSignalHandlers(signals, handler) {
|
|
49
|
+
for (const signal of signals) {
|
|
50
|
+
process.once(signal, () => {
|
|
51
|
+
handler(signal);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
52
54
|
}
|
|
53
55
|
function registerHttpSignalHandlers() {
|
|
54
56
|
const tryShutdown = (signal) => {
|
|
55
57
|
if (shouldAttemptShutdown())
|
|
56
58
|
attemptShutdown(signal);
|
|
57
59
|
};
|
|
58
|
-
|
|
59
|
-
registerOnceSignal(signal, tryShutdown);
|
|
60
|
-
}
|
|
60
|
+
registerSignalHandlers(['SIGINT', 'SIGTERM'], tryShutdown);
|
|
61
61
|
}
|
|
62
62
|
function writeStartupError(error) {
|
|
63
63
|
logError('Failed to start server', error);
|
|
@@ -95,4 +95,3 @@ try {
|
|
|
95
95
|
catch (error) {
|
|
96
96
|
writeStartupError(toError(error));
|
|
97
97
|
}
|
|
98
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type MetadataBlock } from '../transform/types.js';
|
|
2
|
+
export declare function removeNoiseFromHtml(html: string, document?: Document, baseUrl?: string, signal?: AbortSignal): string;
|
|
3
|
+
export declare function resolveLanguageFromAttributes(className: string, dataLang: string): string | undefined;
|
|
4
|
+
export declare function detectLanguageFromCode(code: string): string | undefined;
|
|
2
5
|
interface CleanupOptions {
|
|
3
6
|
signal?: AbortSignal;
|
|
4
7
|
url?: string;
|
|
@@ -9,4 +12,3 @@ export declare function addSourceToMarkdown(content: string, url: string): strin
|
|
|
9
12
|
export declare function isRawTextContent(content: string): boolean;
|
|
10
13
|
export declare function buildMetadataFooter(metadata?: MetadataBlock, fallbackUrl?: string): string;
|
|
11
14
|
export {};
|
|
12
|
-
//# sourceMappingURL=markdown-cleanup.d.ts.map
|