@j0hanz/fetch-url-mcp 1.6.0 → 1.7.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.
Files changed (108) hide show
  1. package/README.md +507 -403
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/http/auth.d.ts +12 -0
  5. package/dist/http/auth.d.ts.map +1 -0
  6. package/dist/http/auth.js +85 -6
  7. package/dist/http/health.d.ts +1 -0
  8. package/dist/http/health.d.ts.map +1 -0
  9. package/dist/http/helpers.d.ts +1 -0
  10. package/dist/http/helpers.d.ts.map +1 -0
  11. package/dist/http/native.d.ts +1 -0
  12. package/dist/http/native.d.ts.map +1 -0
  13. package/dist/http/native.js +80 -63
  14. package/dist/http/rate-limit.d.ts +1 -0
  15. package/dist/http/rate-limit.d.ts.map +1 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/lib/content.d.ts +3 -0
  19. package/dist/lib/content.d.ts.map +1 -0
  20. package/dist/lib/content.js +16 -11
  21. package/dist/lib/core.d.ts +6 -8
  22. package/dist/lib/core.d.ts.map +1 -0
  23. package/dist/lib/core.js +111 -97
  24. package/dist/lib/fetch-pipeline.d.ts +1 -1
  25. package/dist/lib/fetch-pipeline.d.ts.map +1 -0
  26. package/dist/lib/fetch-pipeline.js +54 -44
  27. package/dist/lib/http.d.ts +1 -0
  28. package/dist/lib/http.d.ts.map +1 -0
  29. package/dist/lib/mcp-tools.d.ts +45 -7
  30. package/dist/lib/mcp-tools.d.ts.map +1 -0
  31. package/dist/lib/mcp-tools.js +37 -6
  32. package/dist/lib/net-utils.d.ts +1 -0
  33. package/dist/lib/net-utils.d.ts.map +1 -0
  34. package/dist/lib/progress.d.ts +1 -0
  35. package/dist/lib/progress.d.ts.map +1 -0
  36. package/dist/lib/task-handlers.d.ts +9 -1
  37. package/dist/lib/task-handlers.d.ts.map +1 -0
  38. package/dist/lib/task-handlers.js +30 -38
  39. package/dist/lib/types.d.ts +4 -0
  40. package/dist/lib/types.d.ts.map +1 -0
  41. package/dist/lib/types.js +12 -1
  42. package/dist/lib/url.d.ts +3 -0
  43. package/dist/lib/url.d.ts.map +1 -0
  44. package/dist/lib/url.js +78 -151
  45. package/dist/lib/utils.d.ts +2 -2
  46. package/dist/lib/utils.d.ts.map +1 -0
  47. package/dist/lib/utils.js +60 -94
  48. package/dist/lib/zod.d.ts +3 -0
  49. package/dist/lib/zod.d.ts.map +1 -0
  50. package/dist/lib/zod.js +33 -0
  51. package/dist/prompts/index.d.ts +2 -1
  52. package/dist/prompts/index.d.ts.map +1 -0
  53. package/dist/prompts/index.js +2 -13
  54. package/dist/resources/index.d.ts +2 -1
  55. package/dist/resources/index.d.ts.map +1 -0
  56. package/dist/resources/index.js +5 -19
  57. package/dist/resources/instructions.d.ts +1 -0
  58. package/dist/resources/instructions.d.ts.map +1 -0
  59. package/dist/resources/instructions.js +2 -0
  60. package/dist/schemas/cache.d.ts +18 -0
  61. package/dist/schemas/cache.d.ts.map +1 -0
  62. package/dist/schemas/cache.js +19 -0
  63. package/dist/schemas/inputs.d.ts +1 -0
  64. package/dist/schemas/inputs.d.ts.map +1 -0
  65. package/dist/schemas/outputs.d.ts +6 -5
  66. package/dist/schemas/outputs.d.ts.map +1 -0
  67. package/dist/schemas/outputs.js +5 -9
  68. package/dist/server.d.ts +1 -0
  69. package/dist/server.d.ts.map +1 -0
  70. package/dist/server.js +9 -7
  71. package/dist/tasks/execution.d.ts +1 -0
  72. package/dist/tasks/execution.d.ts.map +1 -0
  73. package/dist/tasks/execution.js +3 -21
  74. package/dist/tasks/manager.d.ts +2 -6
  75. package/dist/tasks/manager.d.ts.map +1 -0
  76. package/dist/tasks/manager.js +2 -4
  77. package/dist/tasks/owner.d.ts +1 -0
  78. package/dist/tasks/owner.d.ts.map +1 -0
  79. package/dist/tasks/tool-registry.d.ts +2 -0
  80. package/dist/tasks/tool-registry.d.ts.map +1 -0
  81. package/dist/tasks/tool-registry.js +3 -0
  82. package/dist/tools/fetch-url.d.ts +4 -6
  83. package/dist/tools/fetch-url.d.ts.map +1 -0
  84. package/dist/tools/fetch-url.js +61 -59
  85. package/dist/tools/index.d.ts +1 -0
  86. package/dist/tools/index.d.ts.map +1 -0
  87. package/dist/transform/html-translators.d.ts +1 -0
  88. package/dist/transform/html-translators.d.ts.map +1 -0
  89. package/dist/transform/html-translators.js +5 -2
  90. package/dist/transform/metadata.d.ts +1 -0
  91. package/dist/transform/metadata.d.ts.map +1 -0
  92. package/dist/transform/metadata.js +1 -0
  93. package/dist/transform/{workers/shared.d.ts → shared.d.ts} +2 -1
  94. package/dist/transform/shared.d.ts.map +1 -0
  95. package/dist/transform/{workers/shared.js → shared.js} +1 -1
  96. package/dist/transform/transform.d.ts +1 -0
  97. package/dist/transform/transform.d.ts.map +1 -0
  98. package/dist/transform/transform.js +21 -14
  99. package/dist/transform/types.d.ts +1 -4
  100. package/dist/transform/types.d.ts.map +1 -0
  101. package/dist/transform/worker-pool.d.ts +3 -18
  102. package/dist/transform/worker-pool.d.ts.map +1 -0
  103. package/dist/transform/worker-pool.js +51 -167
  104. package/package.json +9 -6
  105. package/dist/transform/workers/transform-child.d.ts +0 -1
  106. package/dist/transform/workers/transform-child.js +0 -15
  107. package/dist/transform/workers/transform-worker.d.ts +0 -1
  108. package/dist/transform/workers/transform-worker.js +0 -13
package/dist/cli.d.ts CHANGED
@@ -16,3 +16,4 @@ 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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA,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"}
@@ -1,10 +1,16 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { InvalidTokenError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
2
3
  import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
3
4
  import { type RequestContext } from './helpers.js';
4
5
  declare class CorsPolicy {
5
6
  handle(ctx: RequestContext): boolean;
6
7
  }
7
8
  export declare const corsPolicy: CorsPolicy;
9
+ export declare class InsufficientScopeError extends InvalidTokenError {
10
+ readonly requiredScopes: readonly string[];
11
+ constructor(requiredScopes: readonly string[], message?: string);
12
+ }
13
+ export declare function isInsufficientScopeError(error: unknown): error is InsufficientScopeError;
8
14
  declare class HostOriginPolicy {
9
15
  validate(ctx: RequestContext): boolean;
10
16
  private resolveHostHeader;
@@ -21,6 +27,7 @@ interface McpProtocolVersionCheckOptions {
21
27
  expectedVersion?: string;
22
28
  }
23
29
  export declare function ensureMcpProtocolVersion(req: IncomingMessage, res: ServerResponse, options?: McpProtocolVersionCheckOptions): boolean;
30
+ export declare function isOAuthMetadataEnabled(): boolean;
24
31
  export declare function buildAuthFingerprint(auth: AuthInfo | undefined): string | null;
25
32
  declare class AuthService {
26
33
  private readonly staticTokenDigests;
@@ -31,6 +38,9 @@ declare class AuthService {
31
38
  private buildStaticAuthInfo;
32
39
  private verifyStaticToken;
33
40
  private stripHash;
41
+ private canonicalizeResourceUri;
42
+ private readAudienceValues;
43
+ private assertTokenAudience;
34
44
  private buildBasicAuthHeader;
35
45
  private buildIntrospectionRequest;
36
46
  private requestIntrospection;
@@ -39,6 +49,7 @@ declare class AuthService {
39
49
  private verifyWithIntrospection;
40
50
  }
41
51
  export declare function applyUnauthorizedAuthHeaders(req: IncomingMessage, res: ServerResponse): void;
52
+ export declare function applyInsufficientScopeAuthHeaders(req: IncomingMessage, res: ServerResponse, requiredScopes: readonly string[], message?: string): void;
42
53
  export declare function buildProtectedResourceMetadataDocument(req: IncomingMessage): {
43
54
  resource: string;
44
55
  resource_metadata: string;
@@ -49,3 +60,4 @@ export declare function buildProtectedResourceMetadataDocument(req: IncomingMess
49
60
  export declare function isProtectedResourceMetadataPath(pathname: string): boolean;
50
61
  export declare const authService: AuthService;
51
62
  export {};
63
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/http/auth.ts"],"names":[],"mappings":"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"}
package/dist/http/auth.js CHANGED
@@ -35,6 +35,17 @@ export const corsPolicy = new CorsPolicy();
35
35
  // ---------------------------------------------------------------------------
36
36
  const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1']);
37
37
  const WILDCARD_HOSTS = new Set(['0.0.0.0', '::']);
38
+ export class InsufficientScopeError extends InvalidTokenError {
39
+ requiredScopes;
40
+ constructor(requiredScopes, message = 'Insufficient scope') {
41
+ super(message);
42
+ this.requiredScopes = requiredScopes;
43
+ this.name = 'InsufficientScopeError';
44
+ }
45
+ }
46
+ export function isInsufficientScopeError(error) {
47
+ return error instanceof InsufficientScopeError;
48
+ }
38
49
  function hasConstantTimeMatch(candidates, input) {
39
50
  // Avoid leaking match index via early-return.
40
51
  let matched = 0;
@@ -158,6 +169,9 @@ export function assertHttpModeConfiguration() {
158
169
  if (isRemoteBinding && config.auth.mode !== 'oauth') {
159
170
  throw new Error('OAuth authentication is required for remote bindings');
160
171
  }
172
+ if (config.auth.mode === 'oauth' && !config.auth.issuerUrl) {
173
+ throw new Error('OAuth mode requires OAUTH_ISSUER_URL to serve RFC9728 metadata');
174
+ }
161
175
  if (config.auth.mode === 'static' && config.auth.staticTokens.length === 0) {
162
176
  throw new Error('Static auth requires ACCESS_TOKENS or API_KEY to be configured');
163
177
  }
@@ -180,7 +194,7 @@ function resolveMcpProtocolVersion(req) {
180
194
  }
181
195
  export function ensureMcpProtocolVersion(req, res, options) {
182
196
  const version = resolveMcpProtocolVersion(req);
183
- const requireHeader = options?.requireHeader ?? false;
197
+ const requireHeader = options?.requireHeader ?? config.security.allowRemote;
184
198
  if (!version) {
185
199
  if (!requireHeader) {
186
200
  // Permissive backward-compat fallback: clients predating MCP 2025-03-26 do not
@@ -205,6 +219,9 @@ export function ensureMcpProtocolVersion(req, res, options) {
205
219
  }
206
220
  return true;
207
221
  }
222
+ export function isOAuthMetadataEnabled() {
223
+ return config.auth.mode === 'oauth';
224
+ }
208
225
  // ---------------------------------------------------------------------------
209
226
  // Auth fingerprint
210
227
  // ---------------------------------------------------------------------------
@@ -282,6 +299,48 @@ class AuthService {
282
299
  clean.hash = '';
283
300
  return clean.href;
284
301
  }
302
+ canonicalizeResourceUri(value) {
303
+ if (!URL.canParse(value))
304
+ return null;
305
+ const url = new URL(value);
306
+ url.hash = '';
307
+ url.protocol = url.protocol.toLowerCase();
308
+ url.hostname = url.hostname.toLowerCase();
309
+ const { href } = url;
310
+ if (url.pathname === '/' && !url.search && href.endsWith('/')) {
311
+ return href.slice(0, -1);
312
+ }
313
+ return href;
314
+ }
315
+ readAudienceValues(payload) {
316
+ const values = [];
317
+ for (const key of ['aud', 'resource']) {
318
+ const raw = payload[key];
319
+ if (typeof raw === 'string') {
320
+ values.push(raw);
321
+ continue;
322
+ }
323
+ if (!Array.isArray(raw))
324
+ continue;
325
+ values.push(...raw.filter((value) => typeof value === 'string'));
326
+ }
327
+ return values;
328
+ }
329
+ assertTokenAudience(payload) {
330
+ const expected = this.canonicalizeResourceUri(this.stripHash(config.auth.resourceUrl));
331
+ if (!expected) {
332
+ throw new ServerError('Configured resource URL is invalid');
333
+ }
334
+ const audiences = this.readAudienceValues(payload)
335
+ .map((value) => this.canonicalizeResourceUri(value))
336
+ .filter((value) => value !== null);
337
+ if (audiences.length === 0) {
338
+ throw new InvalidTokenError('Token missing audience binding');
339
+ }
340
+ if (!audiences.includes(expected)) {
341
+ throw new InvalidTokenError('Token audience does not match this MCP server');
342
+ }
343
+ }
285
344
  buildBasicAuthHeader(clientId, clientSecret) {
286
345
  // Base64 is only an encoding for header transport; it is NOT encryption.
287
346
  const credentials = `${clientId}:${clientSecret ?? ''}`;
@@ -341,7 +400,7 @@ class AuthService {
341
400
  const tokenScopeSet = new Set(tokenScopes);
342
401
  const missing = requiredScopes.filter((s) => !tokenScopeSet.has(s));
343
402
  if (missing.length > 0) {
344
- throw new InvalidTokenError('Insufficient scope');
403
+ throw new InsufficientScopeError(missing);
345
404
  }
346
405
  }
347
406
  async verifyWithIntrospection(token, signal) {
@@ -353,6 +412,7 @@ class AuthService {
353
412
  if (!isObject(payload) || payload['active'] !== true) {
354
413
  throw new InvalidTokenError('Token is inactive');
355
414
  }
415
+ this.assertTokenAudience(payload);
356
416
  const info = this.buildIntrospectionAuthInfo(token, payload);
357
417
  this.assertRequiredScopes(info.scopes);
358
418
  return info;
@@ -380,17 +440,36 @@ function buildResourceMetadataUrl(req) {
380
440
  return buildRequestScopedProtectedResourceUrls(req).resourceMetadata;
381
441
  }
382
442
  export function applyUnauthorizedAuthHeaders(req, res) {
443
+ if (!isOAuthMetadataEnabled())
444
+ return;
383
445
  const resourceMetadata = buildResourceMetadataUrl(req);
384
- res.setHeader('WWW-Authenticate', `Bearer resource_metadata="${resourceMetadata}"`);
446
+ const challengeParts = [`resource_metadata="${resourceMetadata}"`];
447
+ if (config.auth.requiredScopes.length > 0) {
448
+ challengeParts.push(`scope="${config.auth.requiredScopes.join(' ')}"`);
449
+ }
450
+ res.setHeader('WWW-Authenticate', `Bearer ${challengeParts.join(', ')}`);
451
+ }
452
+ export function applyInsufficientScopeAuthHeaders(req, res, requiredScopes, message = 'Additional authorization scope is required') {
453
+ if (!isOAuthMetadataEnabled())
454
+ return;
455
+ const resourceMetadata = buildResourceMetadataUrl(req);
456
+ const challengeParts = [
457
+ 'error="insufficient_scope"',
458
+ `scope="${requiredScopes.join(' ')}"`,
459
+ `resource_metadata="${resourceMetadata}"`,
460
+ `error_description="${message.replaceAll('"', "'")}"`,
461
+ ];
462
+ res.setHeader('WWW-Authenticate', `Bearer ${challengeParts.join(', ')}`);
385
463
  }
386
464
  export function buildProtectedResourceMetadataDocument(req) {
387
465
  const urls = buildRequestScopedProtectedResourceUrls(req);
466
+ if (!config.auth.issuerUrl) {
467
+ throw new ServerError('OAuth issuer URL is required for protected resource metadata');
468
+ }
388
469
  return {
389
470
  resource: urls.resource,
390
471
  resource_metadata: urls.resourceMetadata,
391
- authorization_servers: config.auth.issuerUrl
392
- ? [config.auth.issuerUrl.href]
393
- : [],
472
+ authorization_servers: [config.auth.issuerUrl.href],
394
473
  bearer_methods_supported: ['header'],
395
474
  scopes_supported: config.auth.requiredScopes,
396
475
  };
@@ -4,3 +4,4 @@ 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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/http/health.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,cAAc,CAAC;AAY7D,wBAAgB,wBAAwB,IAAI,IAAI,CAI/C;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAiMD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAEpE;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,cAAc,EACnB,WAAW,EAAE,OAAO,GACnB,OAAO,CAOT"}
@@ -48,3 +48,4 @@ declare class JsonBodyReader {
48
48
  }
49
49
  export declare const jsonBodyReader: JsonBodyReader;
50
50
  export {};
51
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/http/helpers.ts"],"names":[],"mappings":"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"}
@@ -3,3 +3,4 @@ export declare function startHttpServer(): Promise<{
3
3
  port: number;
4
4
  host: string;
5
5
  }>;
6
+ //# sourceMappingURL=native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../src/http/native.ts"],"names":[],"mappings":"AA64BA,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CA8DD"}
@@ -10,15 +10,15 @@ import { config, enableHttpMode } from '../lib/core.js';
10
10
  import { logError, logInfo, registerMcpSessionServer, resolveMcpSessionIdByServer, runWithRequestContext, unregisterMcpSessionServer, unregisterMcpSessionServerByServer, } from '../lib/core.js';
11
11
  import { composeCloseHandlers, createSessionStore, createSlotTracker, ensureSessionCapacity, reserveSessionSlot, startSessionCleanupLoop, } from '../lib/core.js';
12
12
  import { handleDownload } from '../lib/http.js';
13
- import { acceptsEventStream, acceptsJsonAndEventStream, isJsonRpcBatchRequest, isMcpRequestBody, } from '../lib/mcp-tools.js';
13
+ import { acceptsEventStream, acceptsJsonAndEventStream, isJsonRpcBatchRequest, isMcpMessageBody, isMcpRequestBody, } from '../lib/mcp-tools.js';
14
14
  import { cancelTasksForOwner } from '../lib/mcp-tools.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';
18
18
  import { createMcpServerForHttpSession } from '../server.js';
19
- import { applyUnauthorizedAuthHeaders, assertHttpModeConfiguration, authService, buildAuthFingerprint, buildProtectedResourceMetadataDocument, corsPolicy, ensureMcpProtocolVersion, hostOriginPolicy, isProtectedResourceMetadataPath, SUPPORTED_MCP_PROTOCOL_VERSIONS, } from './auth.js';
19
+ import { applyInsufficientScopeAuthHeaders, applyUnauthorizedAuthHeaders, assertHttpModeConfiguration, authService, buildAuthFingerprint, buildProtectedResourceMetadataDocument, corsPolicy, ensureMcpProtocolVersion, hostOriginPolicy, isInsufficientScopeError, isOAuthMetadataEnabled, isProtectedResourceMetadataPath, SUPPORTED_MCP_PROTOCOL_VERSIONS, } from './auth.js';
20
20
  import { disableEventLoopMonitoring, resetEventLoopMonitoring, sendHealthRouteResponse, shouldHandleHealthRoute, } from './health.js';
21
- import { buildRequestContext, closeMcpServerBestEffort, closeTransportBestEffort, createRequestAbortSignal, createTransportAdapter, DEFAULT_BODY_LIMIT_BYTES, drainRequest, findDuplicateSingleValueHeader, getHeaderValue, getMcpSessionId, jsonBodyReader, registerInboundBlockList, sendError, sendJson, sendText, } from './helpers.js';
21
+ import { buildRequestContext, closeMcpServerBestEffort, closeTransportBestEffort, createRequestAbortSignal, createTransportAdapter, DEFAULT_BODY_LIMIT_BYTES, drainRequest, findDuplicateSingleValueHeader, getHeaderValue, getMcpSessionId, jsonBodyReader, registerInboundBlockList, sendEmpty, sendError, sendJson, } from './helpers.js';
22
22
  import { createRateLimitManagerImpl, } from './rate-limit.js';
23
23
  // ---------------------------------------------------------------------------
24
24
  // MCP session gateway
@@ -41,6 +41,13 @@ function resolveRequestedProtocolVersion(body) {
41
41
  }
42
42
  return normalized;
43
43
  }
44
+ function resolveProtocolVersionHeader(req) {
45
+ const header = getHeaderValue(req, 'mcp-protocol-version');
46
+ if (!header)
47
+ return undefined;
48
+ const normalized = header.trim();
49
+ return normalized.length > 0 ? normalized : undefined;
50
+ }
44
51
  function isInitializedNotification(method) {
45
52
  return method === 'notifications/initialized';
46
53
  }
@@ -66,36 +73,33 @@ class McpSessionGateway {
66
73
  sendError(ctx.res, -32600, 'Batch requests not supported');
67
74
  return;
68
75
  }
69
- if (!isMcpRequestBody(body)) {
76
+ if (!isMcpMessageBody(body)) {
70
77
  sendError(ctx.res, -32600, 'Invalid request body');
71
78
  return;
72
79
  }
73
80
  const requestId = body.id ?? null;
74
- const isInitializedMethod = isInitializedNotification(body.method);
81
+ const method = isMcpRequestBody(body) ? body.method : null;
82
+ const isInitializedMethod = method !== null && isInitializedNotification(method);
75
83
  const isInitNotification = isInitializedMethod && body.id === undefined;
76
84
  const sessionId = getMcpSessionId(ctx.req);
77
85
  if (isInitializedMethod && !isInitNotification) {
78
86
  sendError(ctx.res, -32600, 'notifications/initialized must be sent as a notification', 400, requestId);
79
87
  return;
80
88
  }
81
- const session = sessionId ? this.store.get(sessionId) : undefined;
82
- if (sessionId && !session) {
83
- sendError(ctx.res, -32600, 'Session not found', 404, requestId);
89
+ const session = sessionId
90
+ ? this.getAuthenticatedSessionById(sessionId, ctx.auth, ctx.res, requestId)
91
+ : undefined;
92
+ if (sessionId && !session)
84
93
  return;
85
- }
86
94
  if (!session && isInitNotification) {
87
95
  sendError(ctx.res, -32600, 'Missing session ID', 400, requestId);
88
96
  return;
89
97
  }
90
98
  if (session) {
91
- if (!ensureMcpProtocolVersion(ctx.req, ctx.res, {
92
- requireHeader: true,
93
- expectedVersion: session.negotiatedProtocolVersion,
94
- })) {
99
+ if (!this.ensureSessionProtocolVersion(ctx, session))
95
100
  return;
96
- }
97
101
  if (!session.protocolInitialized) {
98
- const isPing = isPingRequest(body.method);
102
+ const isPing = method !== null && isPingRequest(method);
99
103
  if (!isInitNotification && !isPing) {
100
104
  sendError(ctx.res, -32600, 'Session not initialized', 400, requestId);
101
105
  return;
@@ -110,16 +114,12 @@ class McpSessionGateway {
110
114
  }
111
115
  }
112
116
  if (session && isInitNotification) {
113
- if (!session.protocolInitialized) {
114
- session.protocolInitialized = true;
115
- }
116
- if (sessionId)
117
- this.store.touch(sessionId);
118
- sendText(ctx.res, 200, '');
117
+ this.markSessionInitialized(sessionId, session);
118
+ sendEmpty(ctx.res, 202);
119
119
  return;
120
120
  }
121
121
  logInfo('[MCP POST]', {
122
- method: body.method,
122
+ method: method ?? 'response',
123
123
  id: body.id,
124
124
  sessionId,
125
125
  });
@@ -129,27 +129,14 @@ class McpSessionGateway {
129
129
  await transport.handleRequest(ctx.req, ctx.res, body);
130
130
  }
131
131
  async handleGet(ctx) {
132
- const sessionId = getMcpSessionId(ctx.req);
133
- if (!sessionId) {
134
- sendError(ctx.res, -32600, 'Missing session ID');
135
- return;
136
- }
137
- const session = this.store.get(sessionId);
138
- if (!session) {
139
- sendError(ctx.res, -32600, 'Session not found', 404);
132
+ const sessionId = this.getRequiredSessionId(ctx.req, ctx.res);
133
+ if (!sessionId)
140
134
  return;
141
- }
142
- const fingerprint = buildAuthFingerprint(ctx.auth);
143
- if (!fingerprint || session.authFingerprint !== fingerprint) {
144
- sendError(ctx.res, -32600, 'Session not found', 404);
135
+ const session = this.getAuthenticatedSessionById(sessionId, ctx.auth, ctx.res);
136
+ if (!session)
145
137
  return;
146
- }
147
- if (!ensureMcpProtocolVersion(ctx.req, ctx.res, {
148
- requireHeader: true,
149
- expectedVersion: session.negotiatedProtocolVersion,
150
- })) {
138
+ if (!this.ensureSessionProtocolVersion(ctx, session))
151
139
  return;
152
- }
153
140
  const acceptHeader = getHeaderValue(ctx.req, 'accept');
154
141
  if (!acceptsEventStream(acceptHeader)) {
155
142
  sendJson(ctx.res, 406, {
@@ -161,27 +148,14 @@ class McpSessionGateway {
161
148
  await session.transport.handleRequest(ctx.req, ctx.res);
162
149
  }
163
150
  async handleDelete(ctx) {
164
- const sessionId = getMcpSessionId(ctx.req);
165
- if (!sessionId) {
166
- sendError(ctx.res, -32600, 'Missing session ID');
151
+ const sessionId = this.getRequiredSessionId(ctx.req, ctx.res);
152
+ if (!sessionId)
167
153
  return;
168
- }
169
- const session = this.store.get(sessionId);
170
- if (!session) {
171
- sendError(ctx.res, -32600, 'Session not found', 404);
172
- return;
173
- }
174
- const fingerprint = buildAuthFingerprint(ctx.auth);
175
- if (!fingerprint || session.authFingerprint !== fingerprint) {
176
- sendError(ctx.res, -32600, 'Session not found', 404);
154
+ const session = this.getAuthenticatedSessionById(sessionId, ctx.auth, ctx.res);
155
+ if (!session)
177
156
  return;
178
- }
179
- if (!ensureMcpProtocolVersion(ctx.req, ctx.res, {
180
- requireHeader: true,
181
- expectedVersion: session.negotiatedProtocolVersion,
182
- })) {
157
+ if (!this.ensureSessionProtocolVersion(ctx, session))
183
158
  return;
184
- }
185
159
  await session.transport.close();
186
160
  this.cleanupSessionRecord(sessionId, 'session-delete');
187
161
  sendJson(ctx.res, 200, { status: 'closed' });
@@ -192,7 +166,7 @@ class McpSessionGateway {
192
166
  const fingerprint = buildAuthFingerprint(ctx.auth);
193
167
  return this.getExistingTransport(sessionId, fingerprint, ctx.res, requestId);
194
168
  }
195
- if (!isInitializeRequest(ctx.body)) {
169
+ if (!isMcpRequestBody(ctx.body) || !isInitializeRequest(ctx.body)) {
196
170
  sendError(ctx.res, -32600, 'Missing session ID', 400, requestId);
197
171
  return null;
198
172
  }
@@ -201,20 +175,55 @@ class McpSessionGateway {
201
175
  sendError(ctx.res, -32602, `Unsupported protocolVersion; supported versions: ${[...SUPPORTED_MCP_PROTOCOL_VERSIONS].join(', ')}`, 400, requestId);
202
176
  return null;
203
177
  }
178
+ const headerProtocolVersion = resolveProtocolVersionHeader(ctx.req);
179
+ if (headerProtocolVersion &&
180
+ headerProtocolVersion !== negotiatedProtocolVersion) {
181
+ sendError(ctx.res, -32600, `initialize protocolVersion mismatch: header=${headerProtocolVersion}, body=${negotiatedProtocolVersion}`, 400, requestId);
182
+ return null;
183
+ }
204
184
  return this.createNewSession(ctx, requestId, negotiatedProtocolVersion);
205
185
  }
206
186
  getExistingTransport(sessionId, authFingerprint, res, requestId) {
187
+ const session = this.getAuthenticatedSessionById(sessionId, authFingerprint, res, requestId);
188
+ if (!session)
189
+ return null;
190
+ this.store.touch(sessionId);
191
+ return session.transport;
192
+ }
193
+ getRequiredSessionId(req, res, requestId = null) {
194
+ const sessionId = getMcpSessionId(req);
195
+ if (sessionId)
196
+ return sessionId;
197
+ sendError(res, -32600, 'Missing session ID', 400, requestId);
198
+ return null;
199
+ }
200
+ getAuthenticatedSessionById(sessionId, auth, res, requestId = null) {
207
201
  const session = this.store.get(sessionId);
208
202
  if (!session) {
209
203
  sendError(res, -32600, 'Session not found', 404, requestId);
210
204
  return null;
211
205
  }
212
- if (!authFingerprint || session.authFingerprint !== authFingerprint) {
206
+ const fingerprint = typeof auth === 'string' || auth === null
207
+ ? auth
208
+ : buildAuthFingerprint(auth);
209
+ if (!fingerprint || session.authFingerprint !== fingerprint) {
213
210
  sendError(res, -32600, 'Session not found', 404, requestId);
214
211
  return null;
215
212
  }
216
- this.store.touch(sessionId);
217
- return session.transport;
213
+ return session;
214
+ }
215
+ ensureSessionProtocolVersion(ctx, session) {
216
+ return ensureMcpProtocolVersion(ctx.req, ctx.res, {
217
+ requireHeader: true,
218
+ expectedVersion: session.negotiatedProtocolVersion,
219
+ });
220
+ }
221
+ markSessionInitialized(sessionId, session) {
222
+ if (!session.protocolInitialized) {
223
+ session.protocolInitialized = true;
224
+ }
225
+ if (sessionId)
226
+ this.store.touch(sessionId);
218
227
  }
219
228
  async createNewSession(ctx, requestId, negotiatedProtocolVersion) {
220
229
  const authFingerprint = buildAuthFingerprint(ctx.auth);
@@ -367,6 +376,8 @@ class HttpDispatcher {
367
376
  tryHandleProtectedResourceMetadataRoute(ctx) {
368
377
  if (ctx.method !== 'GET')
369
378
  return false;
379
+ if (!isOAuthMetadataEnabled())
380
+ return false;
370
381
  if (!isProtectedResourceMetadataPath(ctx.url.pathname))
371
382
  return false;
372
383
  const document = buildProtectedResourceMetadataDocument(ctx.req);
@@ -420,8 +431,14 @@ class HttpDispatcher {
420
431
  return await authService.authenticate(ctx.req, ctx.signal);
421
432
  }
422
433
  catch (err) {
434
+ const message = err instanceof Error ? err.message : 'Unauthorized';
435
+ if (isInsufficientScopeError(err)) {
436
+ applyInsufficientScopeAuthHeaders(ctx.req, ctx.res, err.requiredScopes, message);
437
+ sendError(ctx.res, -32000, message, 403);
438
+ return null;
439
+ }
423
440
  applyUnauthorizedAuthHeaders(ctx.req, ctx.res);
424
- sendError(ctx.res, -32000, err instanceof Error ? err.message : 'Unauthorized', 401);
441
+ sendError(ctx.res, -32000, message, 401);
425
442
  return null;
426
443
  }
427
444
  }
@@ -11,3 +11,4 @@ export interface RateLimitManagerImpl {
11
11
  }
12
12
  export declare function createRateLimitManagerImpl(options: RateLimitConfig): RateLimitManagerImpl;
13
13
  export {};
14
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/http/rate-limit.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,cAAc,CAAC;AAY7D,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;AA0FD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,eAAe,GACvB,oBAAoB,CAEtB"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
  export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
@@ -1,4 +1,6 @@
1
1
  import { type MetadataBlock } from '../transform/types.js';
2
+ export declare function serializeDocumentForMarkdown(document: Document, fallback: string): string;
3
+ export declare function prepareDocumentForMarkdown(document: Document, baseUrl?: string, signal?: AbortSignal): void;
2
4
  export declare function removeNoiseFromHtml(html: string, document?: Document, baseUrl?: string, signal?: AbortSignal): string;
3
5
  export declare function resolveLanguageFromAttributes(className: string, dataLang: string): string | undefined;
4
6
  export declare function detectLanguageFromCode(code: string): string | undefined;
@@ -12,3 +14,4 @@ export declare function addSourceToMarkdown(content: string, url: string): strin
12
14
  export declare function isRawTextContent(content: string): boolean;
13
15
  export declare function buildMetadataFooter(metadata?: MetadataBlock, fallbackUrl?: string): string;
14
16
  export {};
17
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAghB3D,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CAQR;AAWD,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,IAAI,CAYN;AACD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,MAAM,CAcR;AAkVD,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAKpB;AACD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA6BvE;AA+CD,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4QD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,cAAc,GACvB,MAAM,CA6DR;AA2GD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,SAAS,CAOpB;AACD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAmCxE;AAcD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAczD;AAaD,wBAAgB,mBAAmB,CACjC,QAAQ,CAAC,EAAE,aAAa,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAmBR"}
@@ -445,7 +445,7 @@ function resolveUrls(document, baseUrlStr) {
445
445
  processUrlElement(el, 'srcset', base, true);
446
446
  }
447
447
  }
448
- function serialize(document, fallback) {
448
+ export function serializeDocumentForMarkdown(document, fallback) {
449
449
  const bodyHtml = document.body.innerHTML;
450
450
  if (bodyHtml.trim().length > MIN_BODY_CONTENT_LENGTH)
451
451
  return bodyHtml;
@@ -463,6 +463,17 @@ function mayContainNoise(html) {
463
463
  : `${html.substring(0, NOISE_SCAN_LIMIT)}\n${html.substring(html.length - NOISE_SCAN_LIMIT)}`;
464
464
  return NOISE_PATTERNS.some((re) => re.test(sample));
465
465
  }
466
+ export function prepareDocumentForMarkdown(document, baseUrl, signal) {
467
+ const context = getContext();
468
+ if (config.noiseRemoval.debug) {
469
+ logDebug('Noise removal audit enabled', {
470
+ categories: [...(context.flags.navFooter ? ['nav-footer'] : [])],
471
+ });
472
+ }
473
+ stripNoise(document, context, signal);
474
+ if (baseUrl)
475
+ resolveUrls(document, baseUrl);
476
+ }
466
477
  export function removeNoiseFromHtml(html, document, baseUrl, signal) {
467
478
  const shouldParse = isFullDocumentHtml(html) ||
468
479
  mayContainNoise(html) ||
@@ -470,17 +481,9 @@ export function removeNoiseFromHtml(html, document, baseUrl, signal) {
470
481
  if (!shouldParse)
471
482
  return html;
472
483
  try {
473
- const context = getContext();
474
- if (config.noiseRemoval.debug) {
475
- logDebug('Noise removal audit enabled', {
476
- categories: [...(context.flags.navFooter ? ['nav-footer'] : [])],
477
- });
478
- }
479
484
  const doc = document ?? parseHTML(html).document;
480
- stripNoise(doc, context, signal);
481
- if (baseUrl)
482
- resolveUrls(doc, baseUrl);
483
- return serialize(doc, html);
485
+ prepareDocumentForMarkdown(doc, baseUrl, signal);
486
+ return serializeDocumentForMarkdown(doc, html);
484
487
  }
485
488
  catch {
486
489
  return html;
@@ -861,6 +864,7 @@ const REGEX = {
861
864
  SPACING_CODE_DASH: /(`[^`]+`)\s*\\-\s*/g,
862
865
  SPACING_ESCAPES: /\\([[\].])/g,
863
866
  SPACING_LIST_NUM_COMBINED: /^((?![-*+] |\d+\. |[ \t]).+)\n((?:[-*+]|\d+\.) )/gm,
867
+ PUNCT_ONLY_LIST_ARTIFACT: /^(?:[-*+]|\d+\.)\s*(?:\\[-*+|/]|[-*+|/])(?:\s+(?:\\[-*+|/]|[-*+|/]))*\s*$/gm,
864
868
  NESTED_LIST_INDENT: /^( +)((?:[-*+])|\d+\.)\s/gm,
865
869
  TYPEDOC_COMMENT: /(`+)(?:(?!\1)[\s\S])*?\1|\s?\/\\?\*[\s\S]*?\\?\*\//g,
866
870
  };
@@ -1093,6 +1097,7 @@ function applyGlobalRegexes(text, options) {
1093
1097
  .replace(REGEX.SPACING_CODE_DASH, '$1 - ')
1094
1098
  .replace(REGEX.SPACING_ESCAPES, '$1')
1095
1099
  .replace(REGEX.SPACING_LIST_NUM_COMBINED, '$1\n\n$2')
1100
+ .replace(REGEX.PUNCT_ONLY_LIST_ARTIFACT, '')
1096
1101
  .replace(REGEX.DOUBLE_NEWLINE_REDUCER, '\n\n');
1097
1102
  result = normalizeNestedListIndentation(result);
1098
1103
  checkAbort('markdown:cleanup:properties');
@@ -1,6 +1,6 @@
1
1
  import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { type StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
- import { z } from 'zod';
3
+ import { type CachedPayload } from '../schemas/cache.js';
4
4
  export declare const serverVersion: string;
5
5
  type LogLevel = 'debug' | 'info' | 'warn' | 'error';
6
6
  type TransformWorkerMode = 'threads' | 'process';
@@ -81,6 +81,7 @@ export declare const config: {
81
81
  maxTotal: number;
82
82
  maxPerOwner: number;
83
83
  emitStatusNotifications: boolean;
84
+ requireInterception: boolean;
84
85
  };
85
86
  cache: {
86
87
  enabled: boolean;
@@ -143,12 +144,6 @@ export declare const config: {
143
144
  runtime: RuntimeState;
144
145
  };
145
146
  export declare function enableHttpMode(): void;
146
- declare const CachedPayloadSchema: z.ZodObject<{
147
- content: z.ZodOptional<z.ZodString>;
148
- markdown: z.ZodOptional<z.ZodString>;
149
- title: z.ZodOptional<z.ZodString>;
150
- }, z.core.$strict>;
151
- type CachedPayload = z.infer<typeof CachedPayloadSchema>;
152
147
  interface CacheEntry {
153
148
  url: string;
154
149
  title?: string;
@@ -204,12 +199,14 @@ export declare function unregisterMcpSessionServerByServer(server: McpServer): v
204
199
  export declare function resolveMcpSessionIdByServer(server: McpServer): string | undefined;
205
200
  export declare function runWithRequestContext<T>(context: RequestContext, fn: () => T): T;
206
201
  export declare function getRequestId(): string | undefined;
202
+ export declare function getSessionId(): string | undefined;
207
203
  export declare function getOperationId(): string | undefined;
208
204
  export declare function logInfo(message: string, meta?: LogMetadata): void;
209
205
  export declare function logDebug(message: string, meta?: LogMetadata): void;
210
206
  export declare function logWarn(message: string, meta?: LogMetadata): void;
211
207
  export declare function logError(message: string, error?: Error | LogMetadata): void;
212
- export declare function setLogLevel(level: string): void;
208
+ export declare function getMcpLogLevel(sessionId?: string): LogLevel;
209
+ export declare function setLogLevel(level: string, sessionId?: string): void;
213
210
  export declare function redactUrl(rawUrl: string): string;
214
211
  export interface SessionEntry {
215
212
  readonly server: McpServer;
@@ -253,3 +250,4 @@ export declare function ensureSessionCapacity({ store, maxSessions, evictOldest,
253
250
  evictOldest: (store: SessionStore) => boolean;
254
251
  }): boolean;
255
252
  export {};
253
+ //# sourceMappingURL=core.d.ts.map