@j0hanz/fetch-url-mcp 1.12.7 → 1.12.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/http/auth.d.ts +2 -2
  2. package/dist/http/auth.d.ts.map +1 -1
  3. package/dist/http/auth.js +4 -5
  4. package/dist/http/index.d.ts +6 -0
  5. package/dist/http/index.d.ts.map +1 -0
  6. package/dist/http/index.js +5 -0
  7. package/dist/http/native.d.ts +73 -0
  8. package/dist/http/native.d.ts.map +1 -1
  9. package/dist/http/native.js +554 -10
  10. package/dist/http/rate-limit.d.ts +1 -1
  11. package/dist/http/rate-limit.d.ts.map +1 -1
  12. package/dist/http/rate-limit.js +3 -4
  13. package/dist/index.d.ts +17 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +67 -6
  16. package/dist/lib/config.js +2 -2
  17. package/dist/lib/core.d.ts +56 -4
  18. package/dist/lib/core.d.ts.map +1 -1
  19. package/dist/lib/core.js +155 -4
  20. package/dist/lib/error/classes.d.ts +19 -0
  21. package/dist/lib/error/classes.d.ts.map +1 -0
  22. package/dist/lib/error/classes.js +107 -0
  23. package/dist/lib/error/classify.d.ts +4 -0
  24. package/dist/lib/error/classify.d.ts.map +1 -0
  25. package/dist/lib/error/classify.js +154 -0
  26. package/dist/lib/error/codes.d.ts +23 -0
  27. package/dist/lib/error/codes.d.ts.map +1 -0
  28. package/dist/lib/error/codes.js +22 -0
  29. package/dist/lib/error/index.d.ts +6 -0
  30. package/dist/lib/error/index.d.ts.map +1 -0
  31. package/dist/lib/error/index.js +5 -0
  32. package/dist/lib/{error-messages.d.ts → error/messages.d.ts} +2 -2
  33. package/dist/lib/error/messages.d.ts.map +1 -0
  34. package/dist/lib/{error-messages.js → error/messages.js} +2 -2
  35. package/dist/lib/{tool-errors.d.ts → error/payload.d.ts} +7 -13
  36. package/dist/lib/error/payload.d.ts.map +1 -0
  37. package/dist/lib/error/payload.js +108 -0
  38. package/dist/lib/mcp-interop.d.ts.map +1 -1
  39. package/dist/lib/mcp-interop.js +4 -6
  40. package/dist/lib/net/http.d.ts.map +1 -0
  41. package/dist/lib/{http.js → net/http.js} +4 -7
  42. package/dist/lib/net/index.d.ts +4 -0
  43. package/dist/lib/net/index.d.ts.map +1 -0
  44. package/dist/lib/net/index.js +3 -0
  45. package/dist/lib/{fetch-pipeline.d.ts → net/pipeline.d.ts} +3 -3
  46. package/dist/lib/net/pipeline.d.ts.map +1 -0
  47. package/dist/lib/{fetch-pipeline.js → net/pipeline.js} +3 -5
  48. package/dist/lib/{url.d.ts → net/url.d.ts} +1 -1
  49. package/dist/lib/net/url.d.ts.map +1 -0
  50. package/dist/lib/{url.js → net/url.js} +3 -5
  51. package/dist/lib/utils.d.ts +2 -18
  52. package/dist/lib/utils.d.ts.map +1 -1
  53. package/dist/lib/utils.js +29 -104
  54. package/dist/resources/index.d.ts.map +1 -1
  55. package/dist/resources/index.js +8 -5
  56. package/dist/schemas.d.ts +1 -1
  57. package/dist/server.d.ts.map +1 -1
  58. package/dist/server.js +7 -9
  59. package/dist/tasks/index.d.ts +2 -0
  60. package/dist/tasks/index.d.ts.map +1 -0
  61. package/dist/tasks/index.js +1 -0
  62. package/dist/tasks/manager.d.ts +123 -1
  63. package/dist/tasks/manager.d.ts.map +1 -1
  64. package/dist/tasks/manager.js +745 -10
  65. package/dist/tools/{fetch-url.d.ts → index.d.ts} +4 -5
  66. package/dist/tools/index.d.ts.map +1 -0
  67. package/dist/tools/{fetch-url.js → index.js} +6 -8
  68. package/dist/transform/index.d.ts +279 -0
  69. package/dist/transform/index.d.ts.map +1 -0
  70. package/dist/transform/index.js +5234 -0
  71. package/package.json +2 -2
  72. package/dist/cli.d.ts +0 -19
  73. package/dist/cli.d.ts.map +0 -1
  74. package/dist/cli.js +0 -65
  75. package/dist/http/health.d.ts +0 -8
  76. package/dist/http/health.d.ts.map +0 -1
  77. package/dist/http/health.js +0 -152
  78. package/dist/http/helpers.d.ts +0 -68
  79. package/dist/http/helpers.d.ts.map +0 -1
  80. package/dist/http/helpers.js +0 -402
  81. package/dist/lib/error-codes.d.ts +0 -13
  82. package/dist/lib/error-codes.d.ts.map +0 -1
  83. package/dist/lib/error-codes.js +0 -12
  84. package/dist/lib/error-messages.d.ts.map +0 -1
  85. package/dist/lib/fetch-pipeline.d.ts.map +0 -1
  86. package/dist/lib/http.d.ts.map +0 -1
  87. package/dist/lib/logger-names.d.ts +0 -16
  88. package/dist/lib/logger-names.d.ts.map +0 -1
  89. package/dist/lib/logger-names.js +0 -15
  90. package/dist/lib/session.d.ts +0 -44
  91. package/dist/lib/session.d.ts.map +0 -1
  92. package/dist/lib/session.js +0 -137
  93. package/dist/lib/tool-errors.d.ts.map +0 -1
  94. package/dist/lib/tool-errors.js +0 -253
  95. package/dist/lib/url.d.ts.map +0 -1
  96. package/dist/lib/zod.d.ts +0 -3
  97. package/dist/lib/zod.d.ts.map +0 -1
  98. package/dist/lib/zod.js +0 -27
  99. package/dist/tasks/call-contract.d.ts +0 -25
  100. package/dist/tasks/call-contract.d.ts.map +0 -1
  101. package/dist/tasks/call-contract.js +0 -59
  102. package/dist/tasks/execution.d.ts +0 -16
  103. package/dist/tasks/execution.d.ts.map +0 -1
  104. package/dist/tasks/execution.js +0 -241
  105. package/dist/tasks/handlers.d.ts +0 -11
  106. package/dist/tasks/handlers.d.ts.map +0 -1
  107. package/dist/tasks/handlers.js +0 -157
  108. package/dist/tasks/owner.d.ts +0 -43
  109. package/dist/tasks/owner.d.ts.map +0 -1
  110. package/dist/tasks/owner.js +0 -144
  111. package/dist/tasks/registry.d.ts +0 -20
  112. package/dist/tasks/registry.d.ts.map +0 -1
  113. package/dist/tasks/registry.js +0 -40
  114. package/dist/tasks/waiters.d.ts +0 -27
  115. package/dist/tasks/waiters.d.ts.map +0 -1
  116. package/dist/tasks/waiters.js +0 -114
  117. package/dist/tools/fetch-url.d.ts.map +0 -1
  118. package/dist/transform/dom-prep.d.ts +0 -16
  119. package/dist/transform/dom-prep.d.ts.map +0 -1
  120. package/dist/transform/dom-prep.js +0 -1287
  121. package/dist/transform/html-translators.d.ts +0 -5
  122. package/dist/transform/html-translators.d.ts.map +0 -1
  123. package/dist/transform/html-translators.js +0 -697
  124. package/dist/transform/markdown-cleanup.d.ts +0 -10
  125. package/dist/transform/markdown-cleanup.d.ts.map +0 -1
  126. package/dist/transform/markdown-cleanup.js +0 -542
  127. package/dist/transform/metadata.d.ts +0 -18
  128. package/dist/transform/metadata.d.ts.map +0 -1
  129. package/dist/transform/metadata.js +0 -462
  130. package/dist/transform/next-flight.d.ts +0 -2
  131. package/dist/transform/next-flight.d.ts.map +0 -1
  132. package/dist/transform/next-flight.js +0 -374
  133. package/dist/transform/shared.d.ts +0 -8
  134. package/dist/transform/shared.d.ts.map +0 -1
  135. package/dist/transform/shared.js +0 -137
  136. package/dist/transform/transform.d.ts +0 -38
  137. package/dist/transform/transform.d.ts.map +0 -1
  138. package/dist/transform/transform.js +0 -1042
  139. package/dist/transform/types.d.ts +0 -124
  140. package/dist/transform/types.d.ts.map +0 -1
  141. package/dist/transform/types.js +0 -5
  142. package/dist/transform/worker-pool.d.ts +0 -76
  143. package/dist/transform/worker-pool.d.ts.map +0 -1
  144. package/dist/transform/worker-pool.js +0 -725
  145. /package/dist/lib/{http.d.ts → net/http.d.ts} +0 -0
@@ -1,23 +1,567 @@
1
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
1
3
  import { randomUUID } from 'node:crypto';
2
4
  import { once } from 'node:events';
3
5
  import { readFileSync } from 'node:fs';
4
6
  import { createServer, } from 'node:http';
5
7
  import { createServer as createHttpsServer, } from 'node:https';
6
- import { hostname } from 'node:os';
8
+ import { freemem, hostname, totalmem } from 'node:os';
9
+ import { monitorEventLoopDelay, performance } from 'node:perf_hooks';
7
10
  import process from 'node:process';
8
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
9
- import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
10
- import { composeCloseHandlers, config, createSessionStore, createSlotTracker, enableHttpMode, ensureSessionCapacity, logDebug, logError, logInfo, logWarn, registerMcpSessionOwnerKey, registerMcpSessionServer, reserveSessionSlot, runWithRequestContext, startSessionCleanupLoop, } from '../lib/core.js';
11
- import { Loggers } from '../lib/logger-names.js';
11
+ import { Writable } from 'node:stream';
12
+ import { pipeline } from 'node:stream/promises';
13
+ import { composeCloseHandlers, config, createSessionStore, createSlotTracker, enableHttpMode, ensureSessionCapacity, logDebug, logError, Loggers, logInfo, logWarn, registerMcpSessionOwnerKey, registerMcpSessionServer, reserveSessionSlot, resolveMcpSessionIdByServer, runWithRequestContext, serverVersion, startSessionCleanupLoop, unregisterMcpSessionServer, unregisterMcpSessionServerByServer, } from '../lib/core.js';
14
+ import { getErrorMessage, toError } from '../lib/error/index.js';
12
15
  import { acceptsEventStream, acceptsJsonAndEventStream, isMcpRequestBody, } from '../lib/mcp-interop.js';
13
- import { applyHttpServerTuning, drainConnectionsOnShutdown, isObject, toError, } from '../lib/utils.js';
16
+ import { createDefaultBlockList, normalizeIpForBlockList, } from '../lib/net/index.js';
17
+ import { applyHttpServerTuning, drainConnectionsOnShutdown, isObject, } from '../lib/utils.js';
14
18
  import { createMcpServerForHttpSession } from '../server.js';
15
- import { buildAuthenticatedOwnerKey } from '../tasks/owner.js';
19
+ import { buildAuthenticatedOwnerKey } from '../tasks/index.js';
20
+ import { getTransformPoolStats } from '../transform/index.js';
16
21
  import { applyInsufficientScopeAuthHeaders, applyUnauthorizedAuthHeaders, assertHttpModeConfiguration, authService, buildAuthFingerprint, buildProtectedResourceMetadataDocument, corsPolicy, DEFAULT_MCP_PROTOCOL_VERSION, ensureMcpProtocolVersion, hostOriginPolicy, isInsufficientScopeError, isOAuthMetadataEnabled, isProtectedResourceMetadataPath, SUPPORTED_MCP_PROTOCOL_VERSIONS, } from './auth.js';
17
- import { disableEventLoopMonitoring, isVerboseHealthRequest, resetEventLoopMonitoring, sendHealthRouteResponse, shouldHandleHealthRoute, } from './health.js';
18
- import { buildRequestContext, createRequestAbortSignal, createTransportAdapter, DEFAULT_BODY_LIMIT_BYTES, drainRequest, findDuplicateSingleValueHeader, getHeaderValue, getMcpSessionId, isJsonBodyError, jsonBodyReader, registerInboundBlockList, sendEmpty, sendError, sendJson, } from './helpers.js';
19
- import { teardownSessionRegistration, teardownSessionResources, teardownUnregisteredSessionResources, } from './helpers.js';
20
22
  import { createRateLimitManagerImpl, } from './rate-limit.js';
23
+ function abortControllerBestEffort(controller) {
24
+ if (!controller.signal.aborted)
25
+ controller.abort();
26
+ }
27
+ function destroyRequestBestEffort(req) {
28
+ try {
29
+ req.destroy();
30
+ }
31
+ catch {
32
+ // Best-effort only.
33
+ }
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Response helpers
37
+ // ---------------------------------------------------------------------------
38
+ function setNoStoreHeaders(res) {
39
+ res.setHeader('X-Content-Type-Options', 'nosniff');
40
+ res.setHeader('Cache-Control', 'no-store');
41
+ }
42
+ export function sendJson(res, status, body) {
43
+ res.statusCode = status;
44
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
45
+ setNoStoreHeaders(res);
46
+ res.end(JSON.stringify(body));
47
+ }
48
+ export function sendEmpty(res, status) {
49
+ res.statusCode = status;
50
+ res.setHeader('Content-Length', '0');
51
+ res.end();
52
+ }
53
+ export function sendError(res, _code, message, status = 400,
54
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- kept for call-site compat
55
+ _id) {
56
+ sendJson(res, status, { error: message });
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // Request helpers
60
+ // ---------------------------------------------------------------------------
61
+ export function getHeaderValue(req, name) {
62
+ const val = req.headers[name];
63
+ if (!val)
64
+ return null;
65
+ return Array.isArray(val) ? (val[0] ?? null) : val;
66
+ }
67
+ export function getMcpSessionId(req) {
68
+ return (getHeaderValue(req, 'mcp-session-id') ??
69
+ getHeaderValue(req, 'x-mcp-session-id'));
70
+ }
71
+ const SINGLE_VALUE_HEADER_NAMES = [
72
+ 'authorization',
73
+ 'x-api-key',
74
+ 'host',
75
+ 'origin',
76
+ 'content-length',
77
+ 'mcp-protocol-version',
78
+ 'mcp-session-id',
79
+ 'x-mcp-session-id',
80
+ ];
81
+ function hasDuplicateHeader(req, name) {
82
+ const values = req.headersDistinct[name];
83
+ return Array.isArray(values) && values.length > 1;
84
+ }
85
+ export function findDuplicateSingleValueHeader(req) {
86
+ for (const name of SINGLE_VALUE_HEADER_NAMES) {
87
+ if (hasDuplicateHeader(req, name))
88
+ return name;
89
+ }
90
+ return null;
91
+ }
92
+ export function drainRequest(req) {
93
+ if (req.readableEnded)
94
+ return;
95
+ try {
96
+ req.resume();
97
+ }
98
+ catch {
99
+ // Best-effort only.
100
+ }
101
+ }
102
+ // ---------------------------------------------------------------------------
103
+ // Request abort signal
104
+ // ---------------------------------------------------------------------------
105
+ export function createRequestAbortSignal(req) {
106
+ const controller = new AbortController();
107
+ let cleanedUp = false;
108
+ const abortRequest = () => {
109
+ if (cleanedUp)
110
+ return;
111
+ abortControllerBestEffort(controller);
112
+ };
113
+ if (req.destroyed) {
114
+ abortRequest();
115
+ return {
116
+ signal: controller.signal,
117
+ cleanup: () => {
118
+ cleanedUp = true;
119
+ },
120
+ };
121
+ }
122
+ const onClose = () => {
123
+ // A normal close after a complete body should not be treated as cancellation.
124
+ if (req.complete)
125
+ return;
126
+ abortRequest();
127
+ };
128
+ const onError = () => {
129
+ abortRequest();
130
+ };
131
+ req.once('close', onClose);
132
+ req.once('error', onError);
133
+ return {
134
+ signal: controller.signal,
135
+ cleanup: () => {
136
+ cleanedUp = true;
137
+ req.removeListener('close', onClose);
138
+ req.removeListener('error', onError);
139
+ },
140
+ };
141
+ }
142
+ // ---------------------------------------------------------------------------
143
+ // IP & connection helpers
144
+ // ---------------------------------------------------------------------------
145
+ function normalizeRemoteAddress(address) {
146
+ if (!address)
147
+ return null;
148
+ const trimmed = address.trim();
149
+ if (!trimmed)
150
+ return null;
151
+ const normalized = normalizeIpForBlockList(trimmed);
152
+ if (normalized)
153
+ return normalized.ip;
154
+ return trimmed;
155
+ }
156
+ export function registerInboundBlockList(server) {
157
+ if (!config.server.http.blockPrivateConnections)
158
+ return;
159
+ const blockList = createDefaultBlockList();
160
+ server.on('connection', (socket) => {
161
+ const raw = socket.remoteAddress?.trim();
162
+ if (!raw)
163
+ return;
164
+ const normalized = normalizeIpForBlockList(raw);
165
+ if (!normalized)
166
+ return;
167
+ if (blockList.check(normalized.ip, normalized.family)) {
168
+ logWarn('Blocked inbound connection', {
169
+ remoteAddress: normalized.ip,
170
+ family: normalized.family,
171
+ }, Loggers.LOG_HTTP);
172
+ socket.destroy();
173
+ }
174
+ });
175
+ }
176
+ // ---------------------------------------------------------------------------
177
+ // Request context builder
178
+ // ---------------------------------------------------------------------------
179
+ export function buildRequestContext(req, res, signal) {
180
+ const url = URL.parse(req.url ?? '', 'http://localhost');
181
+ if (!url) {
182
+ sendJson(res, 400, { error: 'Invalid request URL' });
183
+ return null;
184
+ }
185
+ return {
186
+ req,
187
+ res,
188
+ url,
189
+ method: req.method,
190
+ ip: normalizeRemoteAddress(req.socket.remoteAddress),
191
+ body: undefined,
192
+ ...(signal ? { signal } : {}),
193
+ };
194
+ }
195
+ // ---------------------------------------------------------------------------
196
+ // Transport / MCP helpers
197
+ // ---------------------------------------------------------------------------
198
+ export async function closeTransportBestEffort(transport, context) {
199
+ try {
200
+ await transport.close();
201
+ }
202
+ catch (error) {
203
+ logWarn('Transport close failed', { context, error }, Loggers.LOG_HTTP);
204
+ }
205
+ }
206
+ export async function closeMcpServerBestEffort(server, context) {
207
+ try {
208
+ await server.close();
209
+ }
210
+ catch (error) {
211
+ logWarn('MCP server close failed', { context, error }, Loggers.LOG_HTTP);
212
+ }
213
+ }
214
+ export function createTransportAdapter(transportImpl) {
215
+ const noopOnClose = () => { };
216
+ const noopOnError = () => { };
217
+ const noopOnMessage = () => { };
218
+ const baseOnClose = transportImpl.onclose;
219
+ let oncloseHandler = noopOnClose;
220
+ let onerrorHandler = noopOnError;
221
+ let onmessageHandler = noopOnMessage;
222
+ return {
223
+ start: () => transportImpl.start(),
224
+ send: (message, options) => transportImpl.send(message, options),
225
+ close: () => transportImpl.close(),
226
+ get onclose() {
227
+ return oncloseHandler;
228
+ },
229
+ set onclose(handler) {
230
+ oncloseHandler = handler;
231
+ transportImpl.onclose = composeCloseHandlers(baseOnClose, handler);
232
+ },
233
+ get onerror() {
234
+ return onerrorHandler;
235
+ },
236
+ set onerror(handler) {
237
+ onerrorHandler = handler;
238
+ transportImpl.onerror = handler;
239
+ },
240
+ get onmessage() {
241
+ return onmessageHandler;
242
+ },
243
+ set onmessage(handler) {
244
+ onmessageHandler = handler;
245
+ transportImpl.onmessage = handler;
246
+ },
247
+ };
248
+ }
249
+ export class JsonBodyError extends Error {
250
+ kind;
251
+ constructor(kind, message) {
252
+ super(message);
253
+ this.name = 'JsonBodyError';
254
+ this.kind = kind;
255
+ }
256
+ }
257
+ export function isJsonBodyError(error) {
258
+ return error instanceof JsonBodyError;
259
+ }
260
+ export const DEFAULT_BODY_LIMIT_BYTES = 1024 * 1024;
261
+ function isRequestReadAborted(req) {
262
+ return req.destroyed && !req.complete;
263
+ }
264
+ class JsonBodyReader {
265
+ async read(req, limit = DEFAULT_BODY_LIMIT_BYTES, signal) {
266
+ const contentType = getHeaderValue(req, 'content-type');
267
+ if (!contentType?.includes('application/json'))
268
+ return undefined;
269
+ const contentLengthHeader = getHeaderValue(req, 'content-length');
270
+ if (contentLengthHeader) {
271
+ const contentLength = Number.parseInt(contentLengthHeader, 10);
272
+ if (Number.isFinite(contentLength) && contentLength > limit) {
273
+ const error = new JsonBodyError('payload-too-large', 'Payload too large');
274
+ throw error;
275
+ }
276
+ }
277
+ if (signal?.aborted || isRequestReadAborted(req)) {
278
+ const error = new JsonBodyError('read-failed', 'Request aborted');
279
+ throw error;
280
+ }
281
+ const body = await this.readBody(req, limit, signal);
282
+ if (!body)
283
+ return undefined;
284
+ try {
285
+ return JSON.parse(body);
286
+ }
287
+ catch (err) {
288
+ const error = new JsonBodyError('invalid-json', getErrorMessage(err));
289
+ throw error;
290
+ }
291
+ }
292
+ async readBody(req, limit, signal) {
293
+ const abortListener = signal != null
294
+ ? () => {
295
+ destroyRequestBestEffort(req);
296
+ }
297
+ : null;
298
+ if (signal != null && abortListener) {
299
+ if (signal.aborted) {
300
+ abortListener();
301
+ }
302
+ else {
303
+ signal.addEventListener('abort', abortListener, { once: true });
304
+ }
305
+ }
306
+ try {
307
+ const { chunks, size } = await this.collectChunks(req, limit, signal);
308
+ if (chunks.length === 0)
309
+ return undefined;
310
+ const combined = new Uint8Array(size);
311
+ let offset = 0;
312
+ for (const chunk of chunks) {
313
+ combined.set(chunk, offset);
314
+ offset += chunk.byteLength;
315
+ }
316
+ const text = new TextDecoder().decode(combined);
317
+ return text;
318
+ }
319
+ finally {
320
+ if (signal && abortListener) {
321
+ try {
322
+ signal.removeEventListener('abort', abortListener);
323
+ }
324
+ catch {
325
+ // Best-effort cleanup.
326
+ }
327
+ }
328
+ }
329
+ }
330
+ async collectChunks(req, limit, signal) {
331
+ let size = 0;
332
+ const chunks = [];
333
+ const sink = new Writable({
334
+ write: (chunk, _encoding, callback) => {
335
+ try {
336
+ if (signal?.aborted || isRequestReadAborted(req)) {
337
+ callback(new JsonBodyError('read-failed', 'Request aborted'));
338
+ return;
339
+ }
340
+ const buf = this.normalizeChunk(chunk);
341
+ size += buf.byteLength;
342
+ if (size > limit) {
343
+ callback(new JsonBodyError('payload-too-large', 'Payload too large'));
344
+ return;
345
+ }
346
+ chunks.push(buf);
347
+ callback();
348
+ }
349
+ catch (err) {
350
+ callback(toError(err));
351
+ }
352
+ },
353
+ });
354
+ try {
355
+ if (signal?.aborted || isRequestReadAborted(req)) {
356
+ const error = new JsonBodyError('read-failed', 'Request aborted');
357
+ throw error;
358
+ }
359
+ await pipeline(req, sink, signal ? { signal } : undefined);
360
+ return { chunks, size };
361
+ }
362
+ catch (err) {
363
+ if (err instanceof JsonBodyError)
364
+ throw err;
365
+ if (signal?.aborted || isRequestReadAborted(req)) {
366
+ const error = new JsonBodyError('read-failed', 'Request aborted');
367
+ throw error;
368
+ }
369
+ const error = new JsonBodyError('read-failed', getErrorMessage(err));
370
+ throw error;
371
+ }
372
+ }
373
+ normalizeChunk(chunk) {
374
+ if (typeof chunk === 'string') {
375
+ const encoded = new TextEncoder().encode(chunk);
376
+ return encoded;
377
+ }
378
+ return chunk;
379
+ }
380
+ }
381
+ export const jsonBodyReader = new JsonBodyReader();
382
+ function unregisterSessionTaskScope(server) {
383
+ const sessionId = resolveMcpSessionIdByServer(server);
384
+ if (!sessionId)
385
+ return null;
386
+ unregisterMcpSessionServer(sessionId);
387
+ return sessionId;
388
+ }
389
+ async function closeSessionResources(session, options) {
390
+ const closeTasks = [];
391
+ if (options.closeTransportReason) {
392
+ closeTasks.push(closeTransportBestEffort(session.transport, options.closeTransportReason));
393
+ }
394
+ if (options.closeServerReason) {
395
+ closeTasks.push(closeMcpServerBestEffort(session.server, options.closeServerReason));
396
+ }
397
+ if (options.awaitClose && closeTasks.length > 0) {
398
+ await Promise.all(closeTasks);
399
+ }
400
+ }
401
+ export async function teardownSessionResources(session, options) {
402
+ unregisterSessionTaskScope(session.server);
403
+ if (options.unregisterByServer) {
404
+ unregisterMcpSessionServerByServer(session.server);
405
+ }
406
+ await closeSessionResources(session, options);
407
+ }
408
+ export async function teardownUnregisteredSessionResources(session, context) {
409
+ await closeSessionResources(session, {
410
+ closeTransportReason: context,
411
+ closeServerReason: context,
412
+ awaitClose: true,
413
+ });
414
+ }
415
+ export function teardownSessionRegistration(server) {
416
+ unregisterSessionTaskScope(server);
417
+ }
418
+ // --- health.ts ---
419
+ // ---------------------------------------------------------------------------
420
+ // Event-loop monitoring
421
+ // ---------------------------------------------------------------------------
422
+ const EVENT_LOOP_DELAY_RESOLUTION_MS = 20;
423
+ const eventLoopDelay = monitorEventLoopDelay({
424
+ resolution: EVENT_LOOP_DELAY_RESOLUTION_MS,
425
+ });
426
+ let lastEventLoopUtilization = performance.eventLoopUtilization();
427
+ export function resetEventLoopMonitoring() {
428
+ lastEventLoopUtilization = performance.eventLoopUtilization();
429
+ eventLoopDelay.reset();
430
+ eventLoopDelay.enable();
431
+ }
432
+ export function disableEventLoopMonitoring() {
433
+ eventLoopDelay.disable();
434
+ }
435
+ // ---------------------------------------------------------------------------
436
+ // Stats helpers
437
+ // ---------------------------------------------------------------------------
438
+ function roundTo(value, precision) {
439
+ const factor = 10 ** precision;
440
+ return Math.round(value * factor) / factor;
441
+ }
442
+ function formatEventLoopUtilization(snapshot) {
443
+ return {
444
+ utilization: roundTo(snapshot.utilization, 4),
445
+ activeMs: Math.round(snapshot.active),
446
+ idleMs: Math.round(snapshot.idle),
447
+ };
448
+ }
449
+ function toMs(valueNs) {
450
+ return roundTo(valueNs / 1_000_000, 3);
451
+ }
452
+ function getEventLoopStats() {
453
+ const current = performance.eventLoopUtilization();
454
+ const delta = performance.eventLoopUtilization(current, lastEventLoopUtilization);
455
+ lastEventLoopUtilization = current;
456
+ return {
457
+ utilization: {
458
+ total: formatEventLoopUtilization(current),
459
+ sinceLast: formatEventLoopUtilization(delta),
460
+ },
461
+ delay: {
462
+ minMs: toMs(eventLoopDelay.min),
463
+ maxMs: toMs(eventLoopDelay.max),
464
+ meanMs: toMs(eventLoopDelay.mean),
465
+ stddevMs: toMs(eventLoopDelay.stddev),
466
+ p50Ms: toMs(eventLoopDelay.percentile(50)),
467
+ p95Ms: toMs(eventLoopDelay.percentile(95)),
468
+ p99Ms: toMs(eventLoopDelay.percentile(99)),
469
+ },
470
+ };
471
+ }
472
+ function buildHealthResponse(store, includeDiagnostics) {
473
+ const base = {
474
+ status: 'ok',
475
+ version: serverVersion,
476
+ uptime: Math.floor(process.uptime()),
477
+ timestamp: new Date().toISOString(),
478
+ };
479
+ if (!includeDiagnostics)
480
+ return base;
481
+ const poolStats = getTransformPoolStats();
482
+ return {
483
+ ...base,
484
+ os: {
485
+ hostname: hostname(),
486
+ platform: process.platform,
487
+ arch: process.arch,
488
+ memoryFree: freemem(),
489
+ memoryTotal: totalmem(),
490
+ },
491
+ process: {
492
+ pid: process.pid,
493
+ ppid: process.ppid,
494
+ memory: process.memoryUsage(),
495
+ cpu: process.cpuUsage(),
496
+ resource: process.resourceUsage(),
497
+ ...(typeof process.availableMemory === 'function'
498
+ ? { availableMemory: process.availableMemory() }
499
+ : {}),
500
+ ...(typeof process.constrainedMemory === 'function'
501
+ ? { constrainedMemory: process.constrainedMemory() }
502
+ : {}),
503
+ },
504
+ perf: getEventLoopStats(),
505
+ ...(typeof process.getActiveResourcesInfo === 'function'
506
+ ? { activeResources: process.getActiveResourcesInfo() }
507
+ : {}),
508
+ stats: {
509
+ activeSessions: store.size(),
510
+ workerPool: poolStats ?? {
511
+ queueDepth: 0,
512
+ activeWorkers: 0,
513
+ capacity: 0,
514
+ },
515
+ },
516
+ };
517
+ }
518
+ function sendHealth(store, res, includeDiagnostics) {
519
+ res.setHeader('Cache-Control', 'no-store');
520
+ sendJson(res, 200, buildHealthResponse(store, includeDiagnostics));
521
+ }
522
+ // ---------------------------------------------------------------------------
523
+ // Health route helpers
524
+ // ---------------------------------------------------------------------------
525
+ export function isVerboseHealthRequest(ctx) {
526
+ const value = ctx.url.searchParams.get('verbose');
527
+ if (!value)
528
+ return false;
529
+ const normalized = value.trim().toLowerCase();
530
+ return normalized === '1' || normalized === 'true';
531
+ }
532
+ function isHealthRoute(ctx) {
533
+ return ctx.method === 'GET' && ctx.url.pathname === '/health';
534
+ }
535
+ function isVerboseHealthRoute(ctx) {
536
+ return isHealthRoute(ctx) && isVerboseHealthRequest(ctx);
537
+ }
538
+ function ensureHealthAuthIfNeeded(ctx, authPresent) {
539
+ if (!isVerboseHealthRoute(ctx))
540
+ return true;
541
+ if (!config.security.allowRemote)
542
+ return true;
543
+ if (authPresent)
544
+ return true;
545
+ sendJson(ctx.res, 401, {
546
+ error: 'Authentication required for verbose health metrics',
547
+ });
548
+ return false;
549
+ }
550
+ function resolveHealthDiagnosticsMode(ctx, authPresent) {
551
+ return (isVerboseHealthRoute(ctx) && (authPresent || !config.security.allowRemote));
552
+ }
553
+ export function shouldHandleHealthRoute(ctx) {
554
+ return isHealthRoute(ctx);
555
+ }
556
+ export function sendHealthRouteResponse(store, ctx, authPresent) {
557
+ if (!shouldHandleHealthRoute(ctx))
558
+ return false;
559
+ if (!ensureHealthAuthIfNeeded(ctx, authPresent))
560
+ return true;
561
+ const includeDiagnostics = resolveHealthDiagnosticsMode(ctx, authPresent);
562
+ sendHealth(store, ctx.res, includeDiagnostics);
563
+ return true;
564
+ }
21
565
  function resolveRequestedProtocolVersion(body) {
22
566
  if (!isObject(body))
23
567
  return DEFAULT_MCP_PROTOCOL_VERSION;
@@ -1,4 +1,4 @@
1
- import { type RequestContext } from './helpers.js';
1
+ import { type RequestContext } from './native.js';
2
2
  interface RateLimitConfig {
3
3
  maxRequests: number;
4
4
  windowMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/http/rate-limit.ts"],"names":[],"mappings":"AAKA,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;AA+FD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,eAAe,GACvB,oBAAoB,CAGtB"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/http/rate-limit.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,aAAa,CAAC;AAY5D,UAAU,eAAe;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC;IACpC,IAAI,IAAI,IAAI,CAAC;CACd;AA+FD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,eAAe,GACvB,oBAAoB,CAGtB"}
@@ -1,8 +1,7 @@
1
- import { logWarn } from '../lib/core.js';
2
- import { Loggers } from '../lib/logger-names.js';
3
- import { isAbortError } from '../lib/utils.js';
1
+ import { Loggers, logWarn } from '../lib/core.js';
2
+ import { isAbortError } from '../lib/error/index.js';
4
3
  import { startAbortableIntervalLoop } from '../lib/utils.js';
5
- import { sendJson } from './helpers.js';
4
+ import { sendJson } from './native.js';
6
5
  // ---------------------------------------------------------------------------
7
6
  // Rate limiter
8
7
  // ---------------------------------------------------------------------------
package/dist/index.d.ts CHANGED
@@ -1,3 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ interface CliValues {
3
+ readonly stdio: boolean;
4
+ readonly http: boolean;
5
+ readonly help: boolean;
6
+ readonly version: boolean;
7
+ }
8
+ interface CliParseSuccess {
9
+ readonly ok: true;
10
+ readonly values: CliValues;
11
+ }
12
+ interface CliParseFailure {
13
+ readonly ok: false;
14
+ readonly message: string;
15
+ }
16
+ type CliParseResult = CliParseSuccess | CliParseFailure;
17
+ export declare function renderCliUsage(): string;
18
+ export declare function parseCliArgs(args: readonly string[]): CliParseResult;
2
19
  export {};
3
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAUA,UAAU,SAAS;IACjB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;CAC5B;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,KAAK,cAAc,GAAG,eAAe,GAAG,eAAe,CAAC;AA2CxD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,CA2BpE"}