@iworkforces/tracelattice 2.0.6 → 2.0.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 (67) hide show
  1. package/README.md +28 -1
  2. package/dist/cli.js +296 -196
  3. package/dist/contracts/summary.d.ts +0 -1
  4. package/dist/contracts/summary.d.ts.map +1 -1
  5. package/dist/core/PersistenceRestore.d.ts +1 -1
  6. package/dist/core/PersistenceRestore.d.ts.map +1 -1
  7. package/dist/core/PersistenceRestore.js.map +1 -1
  8. package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
  9. package/dist/core/ThoughtEvaluator.js.map +1 -1
  10. package/dist/core/ThoughtFormatter.js.map +1 -1
  11. package/dist/core/ThoughtProcessor.d.ts.map +1 -1
  12. package/dist/core/ThoughtProcessor.js.map +1 -1
  13. package/dist/core/compression/CompressionService.d.ts +6 -4
  14. package/dist/core/compression/CompressionService.d.ts.map +1 -1
  15. package/dist/core/compression/CompressionService.js +19 -10
  16. package/dist/core/compression/CompressionService.js.map +1 -1
  17. package/dist/core/compression/DehydrationPolicy.d.ts +4 -4
  18. package/dist/core/compression/DehydrationPolicy.d.ts.map +1 -1
  19. package/dist/core/compression/DehydrationPolicy.js +3 -6
  20. package/dist/core/compression/DehydrationPolicy.js.map +1 -1
  21. package/dist/core/compression/InMemorySummaryStore.d.ts +2 -1
  22. package/dist/core/compression/InMemorySummaryStore.d.ts.map +1 -1
  23. package/dist/core/compression/InMemorySummaryStore.js.map +1 -1
  24. package/dist/core/evaluator/Calibrator.d.ts +1 -1
  25. package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
  26. package/dist/core/evaluator/Calibrator.js.map +1 -1
  27. package/dist/core/evaluator/SignalComputer.d.ts +2 -1
  28. package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
  29. package/dist/core/evaluator/SignalComputer.js.map +1 -1
  30. package/dist/core/evaluator/internals.d.ts +1 -1
  31. package/dist/core/evaluator/internals.d.ts.map +1 -1
  32. package/dist/core/evaluator/internals.js.map +1 -1
  33. package/dist/core/graph/GraphView.d.ts +13 -1
  34. package/dist/core/graph/GraphView.d.ts.map +1 -1
  35. package/dist/core/graph/GraphView.js +34 -0
  36. package/dist/core/graph/GraphView.js.map +1 -1
  37. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +6 -6
  38. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -1
  39. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +16 -2
  40. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -1
  41. package/dist/core/reasoning/strategies/totScoring.d.ts.map +1 -1
  42. package/dist/core/reasoning/strategies/totScoring.js.map +1 -1
  43. package/dist/core/reasoning.d.ts +1 -3
  44. package/dist/core/reasoning.d.ts.map +1 -1
  45. package/dist/core/thought.d.ts +1 -1
  46. package/dist/core/thought.d.ts.map +1 -1
  47. package/dist/lib.d.ts +20 -6
  48. package/dist/lib.d.ts.map +1 -1
  49. package/dist/lib.js +52 -3
  50. package/dist/lib.js.map +1 -1
  51. package/dist/transport/HttpHelpers.d.ts +4 -1
  52. package/dist/transport/HttpHelpers.d.ts.map +1 -1
  53. package/dist/transport/HttpHelpers.js +61 -11
  54. package/dist/transport/HttpHelpers.js.map +1 -1
  55. package/dist/transport/HttpRequestLifecycle.d.ts +15 -1
  56. package/dist/transport/HttpRequestLifecycle.d.ts.map +1 -1
  57. package/dist/transport/HttpRequestLifecycle.js +66 -4
  58. package/dist/transport/HttpRequestLifecycle.js.map +1 -1
  59. package/dist/transport/HttpTransport.d.ts +2 -0
  60. package/dist/transport/HttpTransport.d.ts.map +1 -1
  61. package/dist/transport/HttpTransport.js +75 -38
  62. package/dist/transport/HttpTransport.js.map +1 -1
  63. package/dist/transport/StreamableHttpTransport.d.ts +17 -29
  64. package/dist/transport/StreamableHttpTransport.d.ts.map +1 -1
  65. package/dist/transport/StreamableHttpTransport.js +153 -124
  66. package/dist/transport/StreamableHttpTransport.js.map +1 -1
  67. package/package.json +4 -4
@@ -6,7 +6,7 @@ import { JsonRpcRequestSchema } from "../schema.js";
6
6
  import { BaseTransport } from "./BaseTransport.js";
7
7
  import { readRequestBody, sendCorsPreflight, sendJsonRpcError, sendJsonRpcResponse } from "./HttpHelpers.js";
8
8
  import { runWithContext } from "../context/RequestContext.js";
9
- import { AcceptedWorkTracker, LifecycleFailureReporter, ResponseFinalizer } from "./HttpRequestLifecycle.js";
9
+ import { AcceptedWorkTracker, LifecycleFailureReporter, PreDispatchTracker, ResponseFinalizer } from "./HttpRequestLifecycle.js";
10
10
  class HttpTransport extends BaseTransport {
11
11
  get kind() {
12
12
  return 'http';
@@ -23,6 +23,7 @@ class HttpTransport extends BaseTransport {
23
23
  _metricsProvider;
24
24
  _lifecycleFailureReporter;
25
25
  _acceptedWork;
26
+ _preDispatch = new PreDispatchTracker();
26
27
  _stopPromise = null;
27
28
  constructor(options = {}){
28
29
  super(options);
@@ -88,6 +89,28 @@ class HttpTransport extends BaseTransport {
88
89
  error_type: errorType
89
90
  }, 'Total HTTP request errors');
90
91
  }
92
+ _parseJsonRpcRequest(body, responseFinalizer) {
93
+ let rawBody;
94
+ try {
95
+ rawBody = JSON.parse(body);
96
+ } catch {
97
+ this._trackError('parse_error');
98
+ responseFinalizer.finalize((response)=>{
99
+ sendJsonRpcError(response, 200, -32700, 'Parse error');
100
+ });
101
+ return null;
102
+ }
103
+ const parseResult = safeParse(JsonRpcRequestSchema, rawBody);
104
+ const rawId = rawBody && 'object' == typeof rawBody && 'id' in rawBody ? rawBody.id ?? null : null;
105
+ if (!parseResult.success) {
106
+ this._trackError('validation');
107
+ responseFinalizer.finalize((response)=>{
108
+ sendJsonRpcError(response, 200, -32600, 'Invalid Request', rawId, parseResult.issues);
109
+ });
110
+ return null;
111
+ }
112
+ return parseResult.output;
113
+ }
91
114
  async _handleRequest(req, res) {
92
115
  const startTime = Date.now();
93
116
  const requestPath = req.url || '/';
@@ -135,23 +158,37 @@ class HttpTransport extends BaseTransport {
135
158
  requests: this._requestCount
136
159
  });
137
160
  if ('GET' === req.method && '/ready' === req.url) return this.handleReadinessEndpoint(res);
138
- if ('POST' === req.method && req.url === this._path) return void this._acceptedWork.track(this._handlePostRequest(req, res));
161
+ if ('POST' === req.method && req.url === this._path) {
162
+ const lease = this._preDispatch.acquire();
163
+ if (!lease) {
164
+ const responseFinalizer = new ResponseFinalizer(res, this._lifecycleFailureReporter);
165
+ responseFinalizer.finalize((response)=>{
166
+ sendJsonRpcError(response, 503, -32603, 'Server is shutting down');
167
+ });
168
+ return;
169
+ }
170
+ await this._handlePostRequest(req, res, lease);
171
+ return;
172
+ }
139
173
  this._trackError('not_found');
140
174
  sendJsonRpcError(res, 404, -32601, 'Not Found');
141
175
  }
142
- async _handlePostRequest(req, res) {
176
+ async _handlePostRequest(req, res, lease) {
143
177
  this._requestCount++;
144
178
  this._activeRequests++;
145
179
  const responseFinalizer = new ResponseFinalizer(res, this._lifecycleFailureReporter);
180
+ const onAborted = ()=>lease.cancel('peer');
181
+ req.once('aborted', onAborted);
146
182
  const timeout = setTimeout(()=>{
147
183
  this._trackError('timeout');
148
184
  responseFinalizer.finalize((response)=>{
149
185
  sendJsonRpcError(response, 500, -32603, 'Request timeout');
150
186
  });
187
+ lease.cancel('timeout');
151
188
  }, this._requestTimeout);
152
189
  try {
153
190
  const maxBodySize = this._bodySizeLimitEnabled ? this._maxBodySize : 0;
154
- const body = await readRequestBody(req, maxBodySize);
191
+ const body = await readRequestBody(req, maxBodySize, lease.signal);
155
192
  if (null === body) {
156
193
  this._trackError('payload_too_large');
157
194
  responseFinalizer.finalize((response)=>{
@@ -159,53 +196,52 @@ class HttpTransport extends BaseTransport {
159
196
  });
160
197
  return;
161
198
  }
162
- let rawBody;
163
- try {
164
- rawBody = JSON.parse(body);
165
- } catch {
166
- this._trackError('parse_error');
167
- responseFinalizer.finalize((response)=>{
168
- sendJsonRpcError(response, 200, -32700, 'Parse error');
169
- });
170
- return;
171
- }
172
- const parseResult = safeParse(JsonRpcRequestSchema, rawBody);
173
- const rawId = rawBody && 'object' == typeof rawBody && 'id' in rawBody ? rawBody.id ?? null : null;
174
- if (!parseResult.success) {
175
- this._trackError('validation');
176
- responseFinalizer.finalize((response)=>{
177
- sendJsonRpcError(response, 200, -32600, 'Invalid Request', rawId, parseResult.issues);
178
- });
179
- return;
180
- }
181
- const jsonRpcRequest = parseResult.output;
199
+ const jsonRpcRequest = this._parseJsonRpcRequest(body, responseFinalizer);
200
+ if (null === jsonRpcRequest) return;
182
201
  if (!this._mcpServer) {
183
202
  this._trackError('server_not_ready');
203
+ const requestId = 'id' in jsonRpcRequest ? jsonRpcRequest.id ?? null : null;
184
204
  responseFinalizer.finalize((response)=>{
185
- sendJsonRpcError(response, 200, -32603, 'Server not ready', jsonRpcRequest.id ?? null);
205
+ sendJsonRpcError(response, 200, -32603, 'Server not ready', requestId);
186
206
  });
187
207
  return;
188
208
  }
209
+ req.off('aborted', onAborted);
189
210
  const owner = randomUUID();
190
- const response = await runWithContext({
191
- requestId: randomUUID(),
192
- owner
193
- }, ()=>this._requireMcpServer().receive(jsonRpcRequest, {
194
- sessionInfo: {}
211
+ const acceptedWork = this._acceptedWork.transfer(lease, ()=>runWithContext({
212
+ requestId: randomUUID(),
213
+ owner
214
+ }, ()=>this._requireMcpServer().receive(jsonRpcRequest, {
215
+ sessionInfo: {}
216
+ })).then((response)=>{
217
+ responseFinalizer.finalize((httpResponse)=>{
218
+ if (response) sendJsonRpcResponse(httpResponse, response);
219
+ else {
220
+ httpResponse.writeHead(204);
221
+ httpResponse.end();
222
+ }
223
+ });
224
+ }).catch((error)=>{
225
+ this._trackError('internal_error');
226
+ responseFinalizer.finalize((response)=>{
227
+ sendJsonRpcError(response, 200, -32603, 'Internal error', null, getErrorMessage(error));
228
+ });
195
229
  }));
196
- responseFinalizer.finalize((httpResponse)=>{
197
- if (response) sendJsonRpcResponse(httpResponse, response);
198
- else {
199
- httpResponse.writeHead(204);
200
- httpResponse.end();
201
- }
202
- });
230
+ await acceptedWork;
203
231
  } catch (error) {
232
+ if (lease.signal.aborted) {
233
+ if ('shutdown' === lease.cancellationReason) responseFinalizer.finalize((response)=>{
234
+ sendJsonRpcError(response, 503, -32603, 'Server is shutting down');
235
+ });
236
+ return;
237
+ }
204
238
  this._trackError('internal_error');
205
239
  responseFinalizer.finalize((response)=>{
206
240
  sendJsonRpcError(response, 200, -32603, 'Internal error', null, getErrorMessage(error));
207
241
  });
208
242
  } finally{
243
+ req.off('aborted', onAborted);
244
+ lease.release();
209
245
  clearTimeout(timeout);
210
246
  this._activeRequests--;
211
247
  }
@@ -216,6 +252,7 @@ class HttpTransport extends BaseTransport {
216
252
  stop() {
217
253
  if (this._stopPromise) return this._stopPromise;
218
254
  this._isShuttingDown = true;
255
+ this._preDispatch.closeAdmission('shutdown');
219
256
  this._stopRateLimitCleanup();
220
257
  const server = this._server;
221
258
  const serverClosed = server.listening ? new Promise((resolve, reject)=>{
@@ -226,7 +263,7 @@ class HttpTransport extends BaseTransport {
226
263
  }) : Promise.resolve();
227
264
  const stopPromise = Promise.all([
228
265
  serverClosed,
229
- this._acceptedWork.join()
266
+ this._preDispatch.join().then(()=>this._acceptedWork.join())
230
267
  ]).then(()=>{
231
268
  this.log('info', 'HTTP transport stopped');
232
269
  });
@@ -1 +1 @@
1
- {"version":3,"file":"transport/HttpTransport.js","sources":["../../src/transport/HttpTransport.ts"],"sourcesContent":["/**\n * HTTP Transport implementation.\n *\n * This transport provides a stateless, REST-like API interface for MCP tool invocations\n * using standard HTTP request-response patterns.\n *\n * @example\n * ```typescript\n * const transport = new HttpTransport({\n * port: 3000,\n * host: 'localhost'\n * });\n * await transport.connect(server);\n * ```\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { createServer } from 'node:http';\nimport { randomUUID } from 'node:crypto';\nimport type { McpServer } from 'tmcp';\nimport { safeParse } from 'valibot';\nimport type { IMetrics } from '../contracts/interfaces.js';\nimport { getErrorMessage } from '../errors.js';\nimport { JsonRpcRequestSchema } from '../schema.js';\nimport { BaseTransport, type TransportOptions } from './BaseTransport.js';\nimport type { ITransport, TransportKind } from '../contracts/transport.js';\nimport {\n\treadRequestBody,\n\tsendCorsPreflight,\n\tsendJsonRpcError,\n\tsendJsonRpcResponse,\n} from './HttpHelpers.js';\nimport { runWithContext } from '../context/RequestContext.js';\nimport {\n\tAcceptedWorkTracker,\n\tLifecycleFailureReporter,\n\tResponseFinalizer,\n} from './HttpRequestLifecycle.js';\n\nexport interface HttpTransportOptions extends TransportOptions {\n\t/**\n\t * Path for messages endpoint\n\t * @default '/messages'\n\t */\n\tpath?: string;\n\tmetrics?: IMetrics;\n\tmetricsProvider?: () => string;\n\n\t/**\n\t * Enable request body size limit\n\t * @default true\n\t */\n\tenableBodySizeLimit?: boolean;\n\n\t/**\n\t * Maximum request body size in bytes\n\t * @default 10485760 (10MB)\n\t */\n\tmaxBodySize?: number;\n\n\t/**\n\t * Request timeout in milliseconds\n\t * @default 30000 (30 seconds)\n\t */\n\trequestTimeout?: number;\n}\n\n/**\n * HTTP Transport for MCP server.\n *\n * This transport provides a stateless, REST-like API interface for MCP tool invocations\n * using standard HTTP request-response patterns.\n *\n * @remarks\n * **Security Features:**\n * - Session ID validation (alphanumeric, max 64 chars)\n * - Query parameter sanitization (whitelist allowed keys)\n * - Rate limiting per IP (configurable, default 100 req/min)\n * - CORS origin validation\n * - Request body size limits (configurable, default 10MB)\n * - Request timeout (configurable, default 30s)\n *\n * **Rate Limiting:**\n * - Tracks requests per IP address within a time window\n * - Returns 429 Too Many Requests when limit exceeded\n * - Can be disabled via `enableRateLimit: false`\n *\n * **HTTP Status Code Mapping:**\n * - 200: Success (JSON-RPC response)\n * - 204: CORS Preflight (empty body)\n * - 400: Bad Request\n * - 403: Forbidden (invalid CORS)\n * - 404: Not Found\n * - 413: Payload Too Large\n * - 429: Too Many Requests\n * - 500: Internal Server Error\n * - 503: Server Not Ready\n */\nexport class HttpTransport extends BaseTransport implements ITransport {\n\tget kind(): TransportKind {\n\t\treturn 'http';\n\t}\n\tprivate _server: ReturnType<typeof createServer>;\n\tprivate _mcpServer: McpServer | null = null;\n\tprivate _requestTimeout: number;\n\tprivate _bodySizeLimitEnabled: boolean;\n\tprivate _maxBodySize: number;\n\tprivate _requestCount: number = 0;\n\tprivate _activeRequests: number = 0;\n\tprivate _path: string;\n\tprivate _metrics?: IMetrics;\n\tprivate _metricsProvider: (() => string) | null;\n\tprivate readonly _lifecycleFailureReporter: LifecycleFailureReporter;\n\tprivate readonly _acceptedWork: AcceptedWorkTracker;\n\tprivate _stopPromise: Promise<void> | null = null;\n\n\tconstructor(options: HttpTransportOptions = {}) {\n\t\tsuper(options);\n\n\t\tthis._requestTimeout = options.requestTimeout ?? 30000;\n\t\tthis._bodySizeLimitEnabled = options.enableBodySizeLimit ?? true;\n\t\tthis._maxBodySize = options.maxBodySize ?? 10 * 1024 * 1024;\n\t\tthis._path = options.path ?? '/messages';\n\t\tthis._metrics = options.metrics;\n\t\tthis._metricsProvider = options.metricsProvider ?? null;\n\t\tthis._lifecycleFailureReporter = new LifecycleFailureReporter((error) => {\n\t\t\tthis.log('error', 'HTTP request lifecycle failed', { error: getErrorMessage(error) });\n\t\t});\n\t\tthis._acceptedWork = new AcceptedWorkTracker(this._lifecycleFailureReporter);\n\t\tthis._server = createServer((req, res) => {\n\t\t\tconst responseFinalizer = new ResponseFinalizer(res, this._lifecycleFailureReporter);\n\t\t\tvoid this._handleRequest(req, res).catch((error: unknown) => {\n\t\t\t\tthis._lifecycleFailureReporter.report(error);\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 500, -32603, 'Internal error', null, getErrorMessage(error));\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _requireMcpServer(): McpServer {\n\t\tif (!this._mcpServer) {\n\t\t\tthrow new Error('MCP server not initialized. Did you call connect()?');\n\t\t}\n\t\treturn this._mcpServer;\n\t}\n\n\t/**\n\t * Get number of active HTTP connections.\n\t */\n\tget clientCount(): number {\n\t\treturn this._activeRequests;\n\t}\n\n\t/**\n\t * Connects MCP server to this transport.\n\t */\n\tasync connect(mcpServer: McpServer): Promise<void> {\n\t\tthis._mcpServer = mcpServer;\n\t\tconst server = this._server;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst cleanup = (): void => {\n\t\t\t\tserver.off('error', onError);\n\t\t\t\tserver.off('listening', onListening);\n\t\t\t};\n\t\t\tconst onError = (error: Error): void => {\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\tconst onListening = (): void => {\n\t\t\t\tcleanup();\n\t\t\t\tthis.log('info', `HTTP transport listening on http://${this._host}:${this._port}`);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tserver.once('error', onError);\n\t\t\tserver.once('listening', onListening);\n\t\t\ttry {\n\t\t\t\tserver.listen(this._port, this._host);\n\t\t\t} catch (error) {\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Track an error in metrics.\n\t */\n\tprivate _trackError(errorType: string): void {\n\t\tthis._metrics?.counter(\n\t\t\t'http_request_errors_total',\n\t\t\t1,\n\t\t\t{ transport: 'http', error_type: errorType },\n\t\t\t'Total HTTP request errors'\n\t\t);\n\t}\n\n\t/**\n\t * Route and handle incoming HTTP requests.\n\t *\n\t * Performs security checks (host, shutdown, rate limit, CORS) then\n\t * dispatches to the appropriate endpoint handler.\n\t */\n\tprivate async _handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\tconst requestPath = req.url || '/';\n\t\tconst requestMethod = req.method || 'GET';\n\t\tthis._metrics?.counter('http_requests_total', 1, {}, 'Total HTTP transport requests');\n\t\tthis._metrics?.counter(\n\t\t\t'http_transport_requests_total',\n\t\t\t1,\n\t\t\t{ transport: 'http', method: requestMethod, path: requestPath },\n\t\t\t'Total HTTP requests by transport'\n\t\t);\n\t\tres.once('finish', () => {\n\t\t\tconst durationSeconds = (Date.now() - startTime) / 1000;\n\t\t\tthis._metrics?.histogram('http_request_duration_seconds', durationSeconds, {});\n\t\t\tthis._metrics?.histogram('http_transport_request_duration_seconds', durationSeconds, {\n\t\t\t\ttransport: 'http',\n\t\t\t\tpath: requestPath,\n\t\t\t});\n\t\t});\n\n\t\t// Security middleware chain\n\t\tif (!this.validateHostHeader(req)) {\n\t\t\tthis._trackError('forbidden');\n\t\t\tsendJsonRpcError(res, 403, -32000, 'Forbidden - invalid host header');\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.isShuttingDown) {\n\t\t\tthis._trackError('shutting_down');\n\t\t\tsendJsonRpcError(res, 503, -32603, 'Server is shutting down');\n\t\t\treturn;\n\t\t}\n\n\t\tconst clientIp = this.getClientIp(req);\n\t\tif (this.checkRateLimit(clientIp)) {\n\t\t\tthis._trackError('rate_limit');\n\t\t\tres.setHeader('Retry-After', '60');\n\t\t\tsendJsonRpcError(res, 429, -32000, 'Too many requests');\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.validateCorsOrigin(req)) {\n\t\t\tthis._trackError('forbidden');\n\t\t\tsendJsonRpcError(res, 403, -32000, 'Forbidden - invalid origin');\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setCorsHeaders(res);\n\n\t\t// Static endpoints\n\t\tif (req.method === 'GET' && req.url === '/metrics')\n\t\t\treturn this.handleMetricsEndpoint(res, this._metricsProvider);\n\t\tif (req.method === 'OPTIONS') return sendCorsPreflight(res);\n\t\tif (req.method === 'GET' && req.url === '/health')\n\t\t\treturn this.handleHealthEndpoint(res, { requests: this._requestCount });\n\t\tif (req.method === 'GET' && req.url === '/ready') return this.handleReadinessEndpoint(res);\n\n\t\t// MCP endpoint\n\t\tif (req.method === 'POST' && req.url === this._path) {\n\t\t\tthis._acceptedWork.track(this._handlePostRequest(req, res));\n\t\t\treturn;\n\t\t}\n\n\t\t// 404\n\t\tthis._trackError('not_found');\n\t\tsendJsonRpcError(res, 404, -32601, 'Not Found');\n\t}\n\n\t/**\n\t * Handle POST to the MCP messages endpoint.\n\t *\n\t * Reads the request body, validates JSON-RPC format, and delegates\n\t * processing to the MCP server.\n\t */\n\tprivate async _handlePostRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n\t\tthis._requestCount++;\n\t\tthis._activeRequests++;\n\t\tconst responseFinalizer = new ResponseFinalizer(res, this._lifecycleFailureReporter);\n\n\t\tconst timeout = setTimeout(() => {\n\t\t\tthis._trackError('timeout');\n\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\tsendJsonRpcError(response, 500, -32603, 'Request timeout');\n\t\t\t});\n\t\t}, this._requestTimeout);\n\n\t\ttry {\n\t\t\tconst maxBodySize = this._bodySizeLimitEnabled ? this._maxBodySize : 0;\n\t\t\tconst body = await readRequestBody(req, maxBodySize);\n\n\t\t\tif (body === null) {\n\t\t\t\tthis._trackError('payload_too_large');\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 413, -32000, 'Request body too large');\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet rawBody: unknown;\n\t\t\ttry {\n\t\t\t\trawBody = JSON.parse(body) as unknown;\n\t\t\t} catch {\n\t\t\t\tthis._trackError('parse_error');\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 200, -32700, 'Parse error');\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst parseResult = safeParse(JsonRpcRequestSchema, rawBody);\n\t\t\tconst rawId =\n\t\t\t\trawBody && typeof rawBody === 'object' && 'id' in rawBody\n\t\t\t\t\t? ((rawBody as { id?: unknown }).id ?? null)\n\t\t\t\t\t: null;\n\t\t\tif (!parseResult.success) {\n\t\t\t\tthis._trackError('validation');\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t200,\n\t\t\t\t\t\t-32600,\n\t\t\t\t\t\t'Invalid Request',\n\t\t\t\t\t\trawId as string | number | null,\n\t\t\t\t\t\tparseResult.issues\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst jsonRpcRequest = parseResult.output;\n\n\t\t\tif (!this._mcpServer) {\n\t\t\t\tthis._trackError('server_not_ready');\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 200, -32603, 'Server not ready', jsonRpcRequest.id ?? null);\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst owner = randomUUID();\n\t\t\tconst response = await runWithContext({ requestId: randomUUID(), owner }, () =>\n\t\t\t\tthis._requireMcpServer().receive(jsonRpcRequest as Parameters<McpServer['receive']>[0], {\n\t\t\t\t\tsessionInfo: {},\n\t\t\t\t})\n\t\t\t);\n\n\t\t\tresponseFinalizer.finalize((httpResponse) => {\n\t\t\t\tif (response) {\n\t\t\t\t\tsendJsonRpcResponse(httpResponse, response);\n\t\t\t\t} else {\n\t\t\t\t\thttpResponse.writeHead(204);\n\t\t\t\t\thttpResponse.end();\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis._trackError('internal_error');\n\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\tsendJsonRpcError(response, 200, -32603, 'Internal error', null, getErrorMessage(error));\n\t\t\t});\n\t\t} finally {\n\t\t\tclearTimeout(timeout);\n\t\t\tthis._activeRequests--;\n\t\t}\n\t}\n\n\t/**\n\t * Returns number of requests handled.\n\t */\n\tget requestCount(): number {\n\t\treturn this._requestCount;\n\t}\n\n\t/**\n\t * Stops transport server.\n\t */\n\tstop(): Promise<void> {\n\t\tif (this._stopPromise) return this._stopPromise;\n\t\tthis._isShuttingDown = true;\n\t\tthis._stopRateLimitCleanup();\n\n\t\tconst server = this._server;\n\t\tconst serverClosed = server.listening\n\t\t\t? new Promise<void>((resolve, reject) => {\n\t\t\t\t\tserver.close((error) => {\n\t\t\t\t\t\tif (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t});\n\t\t\t\t})\n\t\t\t: Promise.resolve();\n\t\tconst stopPromise = Promise.all([serverClosed, this._acceptedWork.join()]).then(() => {\n\t\t\tthis.log('info', 'HTTP transport stopped');\n\t\t});\n\t\tthis._stopPromise = stopPromise;\n\t\tvoid stopPromise.catch(() => {\n\t\t\tif (this._stopPromise === stopPromise) this._stopPromise = null;\n\t\t});\n\t\treturn stopPromise;\n\t}\n}\n\n/**\n * Create an HTTP transport with given options.\n *\n * @param options - Transport configuration\n * @returns A configured HTTP transport\n *\n * @example\n * ```typescript\n * const transport = new HttpTransport({ port: 3000 });\n * await transport.connect(server);\n * ```\n */\nexport function createHttpTransport(options: HttpTransportOptions = {}): HttpTransport {\n\treturn new HttpTransport(options);\n}\n"],"names":["HttpTransport","BaseTransport","options","LifecycleFailureReporter","error","getErrorMessage","AcceptedWorkTracker","createServer","req","res","responseFinalizer","ResponseFinalizer","response","sendJsonRpcError","Error","mcpServer","server","Promise","resolve","reject","cleanup","onError","onListening","errorType","startTime","Date","requestPath","requestMethod","durationSeconds","clientIp","sendCorsPreflight","timeout","setTimeout","maxBodySize","body","readRequestBody","rawBody","JSON","parseResult","safeParse","JsonRpcRequestSchema","rawId","jsonRpcRequest","owner","randomUUID","runWithContext","httpResponse","sendJsonRpcResponse","clearTimeout","serverClosed","stopPromise","createHttpTransport"],"mappings":"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"}
1
+ {"version":3,"file":"transport/HttpTransport.js","sources":["../../src/transport/HttpTransport.ts"],"sourcesContent":["/**\n * HTTP Transport implementation.\n *\n * This transport provides a stateless, REST-like API interface for MCP tool invocations\n * using standard HTTP request-response patterns.\n *\n * @example\n * ```typescript\n * const transport = new HttpTransport({\n * port: 3000,\n * host: 'localhost'\n * });\n * await transport.connect(server);\n * ```\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { createServer } from 'node:http';\nimport { randomUUID } from 'node:crypto';\nimport type { McpServer } from 'tmcp';\nimport { safeParse } from 'valibot';\nimport type { IMetrics } from '../contracts/interfaces.js';\nimport { getErrorMessage } from '../errors.js';\nimport { JsonRpcRequestSchema } from '../schema.js';\nimport { BaseTransport, type TransportOptions } from './BaseTransport.js';\nimport type { ITransport, TransportKind } from '../contracts/transport.js';\nimport {\n\treadRequestBody,\n\tsendCorsPreflight,\n\tsendJsonRpcError,\n\tsendJsonRpcResponse,\n} from './HttpHelpers.js';\nimport { runWithContext } from '../context/RequestContext.js';\nimport {\n\tAcceptedWorkTracker,\n\tLifecycleFailureReporter,\n\tPreDispatchTracker,\n\tResponseFinalizer,\n\ttype PreDispatchLease,\n} from './HttpRequestLifecycle.js';\n\nexport interface HttpTransportOptions extends TransportOptions {\n\t/**\n\t * Path for messages endpoint\n\t * @default '/messages'\n\t */\n\tpath?: string;\n\tmetrics?: IMetrics;\n\tmetricsProvider?: () => string;\n\n\t/**\n\t * Enable request body size limit\n\t * @default true\n\t */\n\tenableBodySizeLimit?: boolean;\n\n\t/**\n\t * Maximum request body size in bytes\n\t * @default 10485760 (10MB)\n\t */\n\tmaxBodySize?: number;\n\n\t/**\n\t * Request timeout in milliseconds\n\t * @default 30000 (30 seconds)\n\t */\n\trequestTimeout?: number;\n}\n\n/**\n * HTTP Transport for MCP server.\n *\n * This transport provides a stateless, REST-like API interface for MCP tool invocations\n * using standard HTTP request-response patterns.\n *\n * @remarks\n * **Security Features:**\n * - Session ID validation (alphanumeric, max 64 chars)\n * - Query parameter sanitization (whitelist allowed keys)\n * - Rate limiting per IP (configurable, default 100 req/min)\n * - CORS origin validation\n * - Request body size limits (configurable, default 10MB)\n * - Request timeout (configurable, default 30s)\n *\n * **Rate Limiting:**\n * - Tracks requests per IP address within a time window\n * - Returns 429 Too Many Requests when limit exceeded\n * - Can be disabled via `enableRateLimit: false`\n *\n * **HTTP Status Code Mapping:**\n * - 200: Success (JSON-RPC response)\n * - 204: CORS Preflight (empty body)\n * - 400: Bad Request\n * - 403: Forbidden (invalid CORS)\n * - 404: Not Found\n * - 413: Payload Too Large\n * - 429: Too Many Requests\n * - 500: Internal Server Error\n * - 503: Server Not Ready\n */\nexport class HttpTransport extends BaseTransport implements ITransport {\n\tget kind(): TransportKind {\n\t\treturn 'http';\n\t}\n\tprivate _server: ReturnType<typeof createServer>;\n\tprivate _mcpServer: McpServer | null = null;\n\tprivate _requestTimeout: number;\n\tprivate _bodySizeLimitEnabled: boolean;\n\tprivate _maxBodySize: number;\n\tprivate _requestCount: number = 0;\n\tprivate _activeRequests: number = 0;\n\tprivate _path: string;\n\tprivate _metrics?: IMetrics;\n\tprivate _metricsProvider: (() => string) | null;\n\tprivate readonly _lifecycleFailureReporter: LifecycleFailureReporter;\n\tprivate readonly _acceptedWork: AcceptedWorkTracker;\n\tprivate readonly _preDispatch = new PreDispatchTracker();\n\tprivate _stopPromise: Promise<void> | null = null;\n\n\tconstructor(options: HttpTransportOptions = {}) {\n\t\tsuper(options);\n\n\t\tthis._requestTimeout = options.requestTimeout ?? 30000;\n\t\tthis._bodySizeLimitEnabled = options.enableBodySizeLimit ?? true;\n\t\tthis._maxBodySize = options.maxBodySize ?? 10 * 1024 * 1024;\n\t\tthis._path = options.path ?? '/messages';\n\t\tthis._metrics = options.metrics;\n\t\tthis._metricsProvider = options.metricsProvider ?? null;\n\t\tthis._lifecycleFailureReporter = new LifecycleFailureReporter((error) => {\n\t\t\tthis.log('error', 'HTTP request lifecycle failed', { error: getErrorMessage(error) });\n\t\t});\n\t\tthis._acceptedWork = new AcceptedWorkTracker(this._lifecycleFailureReporter);\n\t\tthis._server = createServer((req, res) => {\n\t\t\tconst responseFinalizer = new ResponseFinalizer(res, this._lifecycleFailureReporter);\n\t\t\tvoid this._handleRequest(req, res).catch((error: unknown) => {\n\t\t\t\tthis._lifecycleFailureReporter.report(error);\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 500, -32603, 'Internal error', null, getErrorMessage(error));\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _requireMcpServer(): McpServer {\n\t\tif (!this._mcpServer) {\n\t\t\tthrow new Error('MCP server not initialized. Did you call connect()?');\n\t\t}\n\t\treturn this._mcpServer;\n\t}\n\n\t/**\n\t * Get number of active HTTP connections.\n\t */\n\tget clientCount(): number {\n\t\treturn this._activeRequests;\n\t}\n\n\t/**\n\t * Connects MCP server to this transport.\n\t */\n\tasync connect(mcpServer: McpServer): Promise<void> {\n\t\tthis._mcpServer = mcpServer;\n\t\tconst server = this._server;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst cleanup = (): void => {\n\t\t\t\tserver.off('error', onError);\n\t\t\t\tserver.off('listening', onListening);\n\t\t\t};\n\t\t\tconst onError = (error: Error): void => {\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\tconst onListening = (): void => {\n\t\t\t\tcleanup();\n\t\t\t\tthis.log('info', `HTTP transport listening on http://${this._host}:${this._port}`);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tserver.once('error', onError);\n\t\t\tserver.once('listening', onListening);\n\t\t\ttry {\n\t\t\t\tserver.listen(this._port, this._host);\n\t\t\t} catch (error) {\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Track an error in metrics.\n\t */\n\tprivate _trackError(errorType: string): void {\n\t\tthis._metrics?.counter(\n\t\t\t'http_request_errors_total',\n\t\t\t1,\n\t\t\t{ transport: 'http', error_type: errorType },\n\t\t\t'Total HTTP request errors'\n\t\t);\n\t}\n\n\tprivate _parseJsonRpcRequest(\n\t\tbody: string,\n\t\tresponseFinalizer: ResponseFinalizer\n\t): Parameters<McpServer['receive']>[0] | null {\n\t\tlet rawBody: unknown;\n\t\ttry {\n\t\t\trawBody = JSON.parse(body) as unknown;\n\t\t} catch {\n\t\t\tthis._trackError('parse_error');\n\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\tsendJsonRpcError(response, 200, -32700, 'Parse error');\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\n\t\tconst parseResult = safeParse(JsonRpcRequestSchema, rawBody);\n\t\tconst rawId =\n\t\t\trawBody && typeof rawBody === 'object' && 'id' in rawBody\n\t\t\t\t? ((rawBody as { id?: unknown }).id ?? null)\n\t\t\t\t: null;\n\t\tif (!parseResult.success) {\n\t\t\tthis._trackError('validation');\n\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\tsendJsonRpcError(\n\t\t\t\t\tresponse,\n\t\t\t\t\t200,\n\t\t\t\t\t-32600,\n\t\t\t\t\t'Invalid Request',\n\t\t\t\t\trawId as string | number | null,\n\t\t\t\t\tparseResult.issues\n\t\t\t\t);\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\t\treturn parseResult.output as Parameters<McpServer['receive']>[0];\n\t}\n\n\t/**\n\t * Route and handle incoming HTTP requests.\n\t *\n\t * Performs security checks (host, shutdown, rate limit, CORS) then\n\t * dispatches to the appropriate endpoint handler.\n\t */\n\tprivate async _handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\tconst requestPath = req.url || '/';\n\t\tconst requestMethod = req.method || 'GET';\n\t\tthis._metrics?.counter('http_requests_total', 1, {}, 'Total HTTP transport requests');\n\t\tthis._metrics?.counter(\n\t\t\t'http_transport_requests_total',\n\t\t\t1,\n\t\t\t{ transport: 'http', method: requestMethod, path: requestPath },\n\t\t\t'Total HTTP requests by transport'\n\t\t);\n\t\tres.once('finish', () => {\n\t\t\tconst durationSeconds = (Date.now() - startTime) / 1000;\n\t\t\tthis._metrics?.histogram('http_request_duration_seconds', durationSeconds, {});\n\t\t\tthis._metrics?.histogram('http_transport_request_duration_seconds', durationSeconds, {\n\t\t\t\ttransport: 'http',\n\t\t\t\tpath: requestPath,\n\t\t\t});\n\t\t});\n\n\t\t// Security middleware chain\n\t\tif (!this.validateHostHeader(req)) {\n\t\t\tthis._trackError('forbidden');\n\t\t\tsendJsonRpcError(res, 403, -32000, 'Forbidden - invalid host header');\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.isShuttingDown) {\n\t\t\tthis._trackError('shutting_down');\n\t\t\tsendJsonRpcError(res, 503, -32603, 'Server is shutting down');\n\t\t\treturn;\n\t\t}\n\n\t\tconst clientIp = this.getClientIp(req);\n\t\tif (this.checkRateLimit(clientIp)) {\n\t\t\tthis._trackError('rate_limit');\n\t\t\tres.setHeader('Retry-After', '60');\n\t\t\tsendJsonRpcError(res, 429, -32000, 'Too many requests');\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.validateCorsOrigin(req)) {\n\t\t\tthis._trackError('forbidden');\n\t\t\tsendJsonRpcError(res, 403, -32000, 'Forbidden - invalid origin');\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setCorsHeaders(res);\n\n\t\t// Static endpoints\n\t\tif (req.method === 'GET' && req.url === '/metrics')\n\t\t\treturn this.handleMetricsEndpoint(res, this._metricsProvider);\n\t\tif (req.method === 'OPTIONS') return sendCorsPreflight(res);\n\t\tif (req.method === 'GET' && req.url === '/health')\n\t\t\treturn this.handleHealthEndpoint(res, { requests: this._requestCount });\n\t\tif (req.method === 'GET' && req.url === '/ready') return this.handleReadinessEndpoint(res);\n\n\t\t// MCP endpoint\n\t\tif (req.method === 'POST' && req.url === this._path) {\n\t\t\tconst lease = this._preDispatch.acquire();\n\t\t\tif (!lease) {\n\t\t\t\tconst responseFinalizer = new ResponseFinalizer(res, this._lifecycleFailureReporter);\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 503, -32603, 'Server is shutting down');\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this._handlePostRequest(req, res, lease);\n\t\t\treturn;\n\t\t}\n\n\t\t// 404\n\t\tthis._trackError('not_found');\n\t\tsendJsonRpcError(res, 404, -32601, 'Not Found');\n\t}\n\n\t/**\n\t * Handle POST to the MCP messages endpoint.\n\t *\n\t * Reads the request body, validates JSON-RPC format, and delegates\n\t * processing to the MCP server.\n\t */\n\tprivate async _handlePostRequest(\n\t\treq: IncomingMessage,\n\t\tres: ServerResponse,\n\t\tlease: PreDispatchLease\n\t): Promise<void> {\n\t\tthis._requestCount++;\n\t\tthis._activeRequests++;\n\t\tconst responseFinalizer = new ResponseFinalizer(res, this._lifecycleFailureReporter);\n\t\tconst onAborted = (): void => lease.cancel('peer');\n\t\treq.once('aborted', onAborted);\n\n\t\tconst timeout = setTimeout(() => {\n\t\t\tthis._trackError('timeout');\n\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\tsendJsonRpcError(response, 500, -32603, 'Request timeout');\n\t\t\t});\n\t\t\tlease.cancel('timeout');\n\t\t}, this._requestTimeout);\n\n\t\ttry {\n\t\t\tconst maxBodySize = this._bodySizeLimitEnabled ? this._maxBodySize : 0;\n\t\t\tconst body = await readRequestBody(req, maxBodySize, lease.signal);\n\n\t\t\tif (body === null) {\n\t\t\t\tthis._trackError('payload_too_large');\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 413, -32000, 'Request body too large');\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst jsonRpcRequest = this._parseJsonRpcRequest(body, responseFinalizer);\n\t\t\tif (jsonRpcRequest === null) return;\n\n\t\t\tif (!this._mcpServer) {\n\t\t\t\tthis._trackError('server_not_ready');\n\t\t\t\tconst requestId = 'id' in jsonRpcRequest ? (jsonRpcRequest.id ?? null) : null;\n\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\tsendJsonRpcError(response, 200, -32603, 'Server not ready', requestId);\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treq.off('aborted', onAborted);\n\t\t\tconst owner = randomUUID();\n\t\t\tconst acceptedWork = this._acceptedWork.transfer(lease, () =>\n\t\t\t\trunWithContext({ requestId: randomUUID(), owner }, () =>\n\t\t\t\t\tthis._requireMcpServer().receive(jsonRpcRequest, {\n\t\t\t\t\t\tsessionInfo: {},\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t\t\t.then((response: Awaited<ReturnType<McpServer['receive']>>) => {\n\t\t\t\t\t\tresponseFinalizer.finalize((httpResponse) => {\n\t\t\t\t\t\t\tif (response) {\n\t\t\t\t\t\t\t\tsendJsonRpcResponse(httpResponse, response);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\thttpResponse.writeHead(204);\n\t\t\t\t\t\t\t\thttpResponse.end();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\t\tthis._trackError('internal_error');\n\t\t\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\t\t\tsendJsonRpcError(\n\t\t\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t\t\t200,\n\t\t\t\t\t\t\t\t-32603,\n\t\t\t\t\t\t\t\t'Internal error',\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tgetErrorMessage(error)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t\t})\n\t\t\t);\n\t\t\tawait acceptedWork;\n\t\t} catch (error) {\n\t\t\tif (lease.signal.aborted) {\n\t\t\t\tif (lease.cancellationReason === 'shutdown') {\n\t\t\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\t\t\tsendJsonRpcError(response, 503, -32603, 'Server is shutting down');\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis._trackError('internal_error');\n\t\t\tresponseFinalizer.finalize((response) => {\n\t\t\t\tsendJsonRpcError(response, 200, -32603, 'Internal error', null, getErrorMessage(error));\n\t\t\t});\n\t\t} finally {\n\t\t\treq.off('aborted', onAborted);\n\t\t\tlease.release();\n\t\t\tclearTimeout(timeout);\n\t\t\tthis._activeRequests--;\n\t\t}\n\t}\n\n\t/**\n\t * Returns number of requests handled.\n\t */\n\tget requestCount(): number {\n\t\treturn this._requestCount;\n\t}\n\n\t/**\n\t * Stops transport server.\n\t */\n\tstop(): Promise<void> {\n\t\tif (this._stopPromise) return this._stopPromise;\n\t\tthis._isShuttingDown = true;\n\t\tthis._preDispatch.closeAdmission('shutdown');\n\t\tthis._stopRateLimitCleanup();\n\n\t\tconst server = this._server;\n\t\tconst serverClosed = server.listening\n\t\t\t? new Promise<void>((resolve, reject) => {\n\t\t\t\t\tserver.close((error) => {\n\t\t\t\t\t\tif (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t});\n\t\t\t\t})\n\t\t\t: Promise.resolve();\n\t\tconst stopPromise = Promise.all([\n\t\t\tserverClosed,\n\t\t\tthis._preDispatch.join().then(() => this._acceptedWork.join()),\n\t\t]).then(() => {\n\t\t\tthis.log('info', 'HTTP transport stopped');\n\t\t});\n\t\tthis._stopPromise = stopPromise;\n\t\tvoid stopPromise.catch(() => {\n\t\t\tif (this._stopPromise === stopPromise) this._stopPromise = null;\n\t\t});\n\t\treturn stopPromise;\n\t}\n}\n\n/**\n * Create an HTTP transport with given options.\n *\n * @param options - Transport configuration\n * @returns A configured HTTP transport\n *\n * @example\n * ```typescript\n * const transport = new HttpTransport({ port: 3000 });\n * await transport.connect(server);\n * ```\n */\nexport function createHttpTransport(options: HttpTransportOptions = {}): HttpTransport {\n\treturn new HttpTransport(options);\n}\n"],"names":["HttpTransport","BaseTransport","PreDispatchTracker","options","LifecycleFailureReporter","error","getErrorMessage","AcceptedWorkTracker","createServer","req","res","responseFinalizer","ResponseFinalizer","response","sendJsonRpcError","Error","mcpServer","server","Promise","resolve","reject","cleanup","onError","onListening","errorType","body","rawBody","JSON","parseResult","safeParse","JsonRpcRequestSchema","rawId","startTime","Date","requestPath","requestMethod","durationSeconds","clientIp","sendCorsPreflight","lease","onAborted","timeout","setTimeout","maxBodySize","readRequestBody","jsonRpcRequest","requestId","owner","randomUUID","acceptedWork","runWithContext","httpResponse","sendJsonRpcResponse","clearTimeout","serverClosed","stopPromise","createHttpTransport"],"mappings":"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"}
@@ -1,12 +1,8 @@
1
1
  /**
2
2
  * Streamable HTTP Transport implementation (MCP spec recommended transport).
3
3
  *
4
- * This transport implements the MCP Streamable HTTP specification, which replaces
5
- * the deprecated SSE transport as the recommended HTTP-based transport since March 2025.
6
- *
7
4
  * Key features:
8
5
  * - POST /mcp for JSON-RPC requests (main MCP endpoint)
9
- * - GET /mcp for optional SSE server-to-client notifications
10
6
  * - Session management via Mcp-Session-Id header
11
7
  * - Supports both stateful (session-based) and stateless (per-request) modes
12
8
  * - Health endpoints (/health, /ready)
@@ -69,13 +65,18 @@ export interface StreamableHttpTransportOptions extends TransportOptions {
69
65
  * @default 30000 (30 seconds)
70
66
  */
71
67
  requestTimeout?: number;
68
+ /** Maximum retained sessions when idle expiry is enabled. */
69
+ maxSessions?: number;
70
+ /** Idle duration before an unpinned session expires. */
71
+ sessionIdleTimeoutMs?: number;
72
+ /** Interval between idle-session sweeps. */
73
+ sessionSweepIntervalMs?: number;
72
74
  }
73
75
  /**
74
76
  * Streamable HTTP Transport for MCP server.
75
77
  *
76
78
  * This transport implements the MCP Streamable HTTP specification,
77
- * providing JSON-RPC over HTTP with optional session management
78
- * and server-to-client SSE notification streams.
79
+ * providing JSON-RPC over HTTP with optional session management.
79
80
  *
80
81
  * @remarks
81
82
  * **Security Features (inherited from BaseTransport):**
@@ -87,13 +88,11 @@ export interface StreamableHttpTransportOptions extends TransportOptions {
87
88
  *
88
89
  * **MCP Streamable HTTP Spec Compliance:**
89
90
  * - POST /mcp — JSON-RPC method calls
90
- * - GET /mcp — SSE notification stream (server-to-client)
91
91
  * - Mcp-Session-Id header for session management
92
92
  * - Content-Type: application/json for JSON-RPC responses
93
- * - Content-Type: text/event-stream for SSE notification streams
94
93
  *
95
94
  * **HTTP Status Code Mapping:**
96
- * - 200: Success (JSON-RPC response or SSE stream)
95
+ * - 200: Success (JSON-RPC response)
97
96
  * - 202: Accepted (JSON-RPC notification, no response body)
98
97
  * - 204: CORS Preflight (empty body)
99
98
  * - 400: Bad Request (invalid JSON, invalid session ID)
@@ -122,6 +121,9 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
122
121
  private _metricsProvider;
123
122
  private readonly _lifecycleFailureReporter;
124
123
  private readonly _acceptedWork;
124
+ private readonly _preDispatch;
125
+ private readonly _sessionRetention;
126
+ private _sessionSweepIntervalId;
125
127
  private _stopPromise;
126
128
  constructor(options?: StreamableHttpTransportOptions);
127
129
  private _requireMcpServer;
@@ -137,6 +139,7 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
137
139
  * Connects MCP server to this transport and starts listening.
138
140
  */
139
141
  connect(mcpServer: McpServer): Promise<void>;
142
+ private _recordRequestMetrics;
140
143
  /**
141
144
  * Route and handle incoming HTTP requests.
142
145
  */
@@ -145,6 +148,7 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
145
148
  * Send a JSON-RPC error response.
146
149
  */
147
150
  private _sendJsonRpcError;
151
+ private _parseJsonRpcRequest;
148
152
  /**
149
153
  * Handle POST /mcp — JSON-RPC method calls.
150
154
  *
@@ -155,37 +159,21 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
155
159
  * - Content-Type: application/json for responses
156
160
  */
157
161
  private _handleMcpPost;
162
+ private _acceptPostSession;
158
163
  private _receiveAndFinalize;
159
- /**
160
- * Handle GET /mcp — Optional SSE notification stream.
161
- *
162
- * Per the MCP Streamable HTTP spec, clients may open a GET request
163
- * to receive server-initiated notifications as SSE events.
164
- * Requires a valid Mcp-Session-Id in stateful mode.
165
- */
166
- private _handleMcpGet;
167
164
  /**
168
165
  * Resolve or create a session for a stateful request.
169
166
  *
170
167
  * @returns Session ID string on success, or the response error to finalize.
171
168
  */
172
169
  private _resolveSession;
170
+ private _sweepExpiredSessions;
171
+ private _disposeSession;
172
+ private _stopSessionSweep;
173
173
  /**
174
174
  * Extract Mcp-Session-Id from request headers.
175
175
  */
176
176
  private _getSessionIdFromHeader;
177
- /**
178
- * Send an SSE event to a specific client response stream.
179
- */
180
- private _sendSseEvent;
181
- /**
182
- * Broadcast a notification to all SSE streams in a given session.
183
- *
184
- * @param sessionId - Target session ID
185
- * @param event - SSE event name
186
- * @param data - Event payload
187
- */
188
- broadcastToSession(sessionId: string, event: string, data: unknown): void;
189
177
  /**
190
178
  * Handle GET /metrics endpoint.
191
179
  */
@@ -1 +1 @@
1
- {"version":3,"file":"StreamableHttpTransport.d.ts","sourceRoot":"","sources":["../../src/transport/StreamableHttpTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAU3E;;GAEG;AACH,MAAM,WAAW,8BAA+B,SAAQ,gBAAgB;IACvE;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;IAEnB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;IAE/B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;IAElC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,uBAAwB,SAAQ,aAAc,YAAW,UAAU;IAC/E,IAAI,IAAI,IAAI,aAAa,CAExB;IACD,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,mBAAmB,CAAe;IAC1C,OAAO,CAAC,SAAS,CAA2C;IAC5D,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,qBAAqB,CAAU;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,CAAW;IAC5B,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAA2B;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,YAAY,CAA8B;gBAEtC,OAAO,GAAE,8BAAmC;IAmBxD,OAAO,CAAC,iBAAiB;IAOzB;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;OAEG;IACG,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA2ClD;;OAEG;YACW,cAAc;IA+F5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;;;;;;;OAQG;YACW,cAAc;YAqGd,mBAAmB;IAwBjC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAsErB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAuCvB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAQ/B;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;;;;;OAMG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAWzE;;OAEG;IACH,OAAO,CAAC,cAAc;IAWtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;YACW,qBAAqB;IAkBnC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoB7B;;;;OAIG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAyDrC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAC5C,OAAO,GAAE,8BAAmC,GAC1C,uBAAuB,CAEzB"}
1
+ {"version":3,"file":"StreamableHttpTransport.d.ts","sourceRoot":"","sources":["../../src/transport/StreamableHttpTransport.ts"],"names":[],"mappings":"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"}