@iworkforces/tracelattice 2.0.7 → 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.
- package/README.md +1 -1
- package/dist/cli.js +205 -181
- package/dist/contracts/summary.d.ts +0 -1
- package/dist/contracts/summary.d.ts.map +1 -1
- package/dist/core/PersistenceRestore.d.ts +1 -1
- package/dist/core/PersistenceRestore.d.ts.map +1 -1
- package/dist/core/PersistenceRestore.js.map +1 -1
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
- package/dist/core/ThoughtEvaluator.js.map +1 -1
- package/dist/core/ThoughtFormatter.js.map +1 -1
- package/dist/core/ThoughtProcessor.d.ts.map +1 -1
- package/dist/core/ThoughtProcessor.js.map +1 -1
- package/dist/core/compression/CompressionService.d.ts +2 -1
- package/dist/core/compression/CompressionService.d.ts.map +1 -1
- package/dist/core/compression/CompressionService.js.map +1 -1
- package/dist/core/compression/DehydrationPolicy.d.ts.map +1 -1
- package/dist/core/compression/DehydrationPolicy.js.map +1 -1
- package/dist/core/compression/InMemorySummaryStore.d.ts +2 -1
- package/dist/core/compression/InMemorySummaryStore.d.ts.map +1 -1
- package/dist/core/compression/InMemorySummaryStore.js.map +1 -1
- package/dist/core/evaluator/Calibrator.d.ts +1 -1
- package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
- package/dist/core/evaluator/Calibrator.js.map +1 -1
- package/dist/core/evaluator/SignalComputer.d.ts +2 -1
- package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
- package/dist/core/evaluator/SignalComputer.js.map +1 -1
- package/dist/core/evaluator/internals.d.ts +1 -1
- package/dist/core/evaluator/internals.d.ts.map +1 -1
- package/dist/core/evaluator/internals.js.map +1 -1
- package/dist/core/graph/GraphView.d.ts +13 -1
- package/dist/core/graph/GraphView.d.ts.map +1 -1
- package/dist/core/graph/GraphView.js +34 -0
- package/dist/core/graph/GraphView.js.map +1 -1
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +6 -6
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -1
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +16 -2
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -1
- package/dist/core/reasoning/strategies/totScoring.d.ts.map +1 -1
- package/dist/core/reasoning/strategies/totScoring.js.map +1 -1
- package/dist/core/reasoning.d.ts +1 -3
- package/dist/core/reasoning.d.ts.map +1 -1
- package/dist/core/thought.d.ts +1 -1
- package/dist/core/thought.d.ts.map +1 -1
- package/dist/lib.d.ts +18 -4
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +51 -3
- package/dist/lib.js.map +1 -1
- package/dist/transport/HttpHelpers.d.ts +4 -1
- package/dist/transport/HttpHelpers.d.ts.map +1 -1
- package/dist/transport/HttpHelpers.js +61 -11
- package/dist/transport/HttpHelpers.js.map +1 -1
- package/dist/transport/HttpRequestLifecycle.d.ts +15 -1
- package/dist/transport/HttpRequestLifecycle.d.ts.map +1 -1
- package/dist/transport/HttpRequestLifecycle.js +66 -4
- package/dist/transport/HttpRequestLifecycle.js.map +1 -1
- package/dist/transport/HttpTransport.d.ts +2 -0
- package/dist/transport/HttpTransport.d.ts.map +1 -1
- package/dist/transport/HttpTransport.js +75 -38
- package/dist/transport/HttpTransport.js.map +1 -1
- package/dist/transport/StreamableHttpTransport.d.ts +5 -29
- package/dist/transport/StreamableHttpTransport.d.ts.map +1 -1
- package/dist/transport/StreamableHttpTransport.js +74 -116
- package/dist/transport/StreamableHttpTransport.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport/HttpHelpers.js","sources":["../../src/transport/HttpHelpers.ts"],"sourcesContent":["/**\n * Shared HTTP helper utilities for MCP transport implementations.\n *\n * Centralizes JSON-RPC response formatting, request body reading,\n * and common HTTP response patterns to eliminate duplication across\n * HttpTransport and StreamableHttpTransport.\n *\n * @module transport/HttpHelpers\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\n/**\n * Send a JSON-RPC 2.0 error response.\n *\n * Standardizes error response formatting across all transport implementations.\n * All JSON-RPC errors use the standard `{ jsonrpc, id, error }` shape.\n *\n * @param res - The server response to write to\n * @param statusCode - HTTP status code (e.g. 400, 403, 429, 500)\n * @param code - JSON-RPC error code (e.g. -32700, -32600, -32603)\n * @param message - Human-readable error message\n * @param id - Optional JSON-RPC request ID (defaults to null)\n * @param data - Optional additional error data\n */\nexport function sendJsonRpcError(\n\tres: ServerResponse,\n\tstatusCode: number,\n\tcode: number,\n\tmessage: string,\n\tid: string | number | null = null,\n\tdata?: unknown\n): void {\n\tconst body: Record<string, unknown> = {\n\t\tjsonrpc: '2.0',\n\t\tid,\n\t\terror: { code, message },\n\t};\n\tif (data !== undefined) {\n\t\t(body.error as Record<string, unknown>).data = data;\n\t}\n\tres.writeHead(statusCode, { 'Content-Type': 'application/json' });\n\tres.end(JSON.stringify(body));\n}\n\n/**\n * Send a JSON-RPC 2.0 success response.\n *\n * @param res - The server response to write to\n * @param response - The JSON-RPC response object to send\n * @param statusCode - HTTP status code (default: 200)\n * @param headers - Optional additional response headers\n */\nexport function sendJsonRpcResponse(\n\tres: ServerResponse,\n\tresponse: unknown,\n\tstatusCode: number = 200,\n\theaders: Record<string, string> = {}\n): void {\n\tconst defaultHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n\tres.writeHead(statusCode, { ...defaultHeaders, ...headers });\n\tres.end(JSON.stringify(response));\n}\n\n/**\n * Send a CORS preflight (OPTIONS) response.\n *\n * @param res - The server response to write to\n * @param extraAllowHeaders - Optional extra Access-Control-Allow-Headers values\n */\nexport function sendCorsPreflight(res: ServerResponse, extraAllowHeaders?: string[]): void {\n\tif (extraAllowHeaders && extraAllowHeaders.length > 0) {\n\t\tres.setHeader('Access-Control-Allow-Headers', `Content-Type, ${extraAllowHeaders.join(', ')}`);\n\t}\n\tres.writeHead(204);\n\tres.end();\n}\n\n/**\n * Read the full request body with optional size limit enforcement.\n *\n * Streams the request body chunks, tracking total size.\n * If the body exceeds `maxBodySize`, reading stops and `null` is returned\n * to indicate the payload is too large.\n *\n * @param req - The incoming HTTP request\n * @param maxBodySize - Maximum allowed body size in bytes (0 = unlimited)\n * @returns The body string, or `null` if the body exceeded the size limit\n */\nexport async function readRequestBody(\n\treq: IncomingMessage,\n\tmaxBodySize: number\n): Promise<string | null> {\n\tlet body = '';\n\tlet bodySize = 0;\n\n\
|
|
1
|
+
{"version":3,"file":"transport/HttpHelpers.js","sources":["../../src/transport/HttpHelpers.ts"],"sourcesContent":["/**\n * Shared HTTP helper utilities for MCP transport implementations.\n *\n * Centralizes JSON-RPC response formatting, request body reading,\n * and common HTTP response patterns to eliminate duplication across\n * HttpTransport and StreamableHttpTransport.\n *\n * @module transport/HttpHelpers\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { StringDecoder } from 'node:string_decoder';\n\nexport class RequestBodyAbortedError extends Error {\n\toverride readonly name = 'RequestBodyAbortedError';\n}\n\n/**\n * Send a JSON-RPC 2.0 error response.\n *\n * Standardizes error response formatting across all transport implementations.\n * All JSON-RPC errors use the standard `{ jsonrpc, id, error }` shape.\n *\n * @param res - The server response to write to\n * @param statusCode - HTTP status code (e.g. 400, 403, 429, 500)\n * @param code - JSON-RPC error code (e.g. -32700, -32600, -32603)\n * @param message - Human-readable error message\n * @param id - Optional JSON-RPC request ID (defaults to null)\n * @param data - Optional additional error data\n */\nexport function sendJsonRpcError(\n\tres: ServerResponse,\n\tstatusCode: number,\n\tcode: number,\n\tmessage: string,\n\tid: string | number | null = null,\n\tdata?: unknown\n): void {\n\tconst body: Record<string, unknown> = {\n\t\tjsonrpc: '2.0',\n\t\tid,\n\t\terror: { code, message },\n\t};\n\tif (data !== undefined) {\n\t\t(body.error as Record<string, unknown>).data = data;\n\t}\n\tres.writeHead(statusCode, { 'Content-Type': 'application/json' });\n\tres.end(JSON.stringify(body));\n}\n\n/**\n * Send a JSON-RPC 2.0 success response.\n *\n * @param res - The server response to write to\n * @param response - The JSON-RPC response object to send\n * @param statusCode - HTTP status code (default: 200)\n * @param headers - Optional additional response headers\n */\nexport function sendJsonRpcResponse(\n\tres: ServerResponse,\n\tresponse: unknown,\n\tstatusCode: number = 200,\n\theaders: Record<string, string> = {}\n): void {\n\tconst defaultHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n\tres.writeHead(statusCode, { ...defaultHeaders, ...headers });\n\tres.end(JSON.stringify(response));\n}\n\n/**\n * Send a CORS preflight (OPTIONS) response.\n *\n * @param res - The server response to write to\n * @param extraAllowHeaders - Optional extra Access-Control-Allow-Headers values\n */\nexport function sendCorsPreflight(res: ServerResponse, extraAllowHeaders?: string[]): void {\n\tif (extraAllowHeaders && extraAllowHeaders.length > 0) {\n\t\tres.setHeader('Access-Control-Allow-Headers', `Content-Type, ${extraAllowHeaders.join(', ')}`);\n\t}\n\tres.writeHead(204);\n\tres.end();\n}\n\n/**\n * Read the full request body with optional size limit enforcement.\n *\n * Streams the request body chunks, tracking total size.\n * If the body exceeds `maxBodySize`, reading stops and `null` is returned\n * to indicate the payload is too large.\n *\n * @param req - The incoming HTTP request\n * @param maxBodySize - Maximum allowed body size in bytes (0 = unlimited)\n * @returns The body string, or `null` if the body exceeded the size limit\n */\nexport async function readRequestBody(\n\treq: IncomingMessage,\n\tmaxBodySize: number,\n\tsignal?: AbortSignal\n): Promise<string | null> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet body = '';\n\t\tlet bodySize = 0;\n\t\tlet settled = false;\n\t\tconst decoder = new StringDecoder('utf8');\n\n\t\tconst cleanup = (): void => {\n\t\t\treq.off('data', onData);\n\t\t\treq.off('end', onEnd);\n\t\t\treq.off('aborted', onAborted);\n\t\t\treq.off('close', onAborted);\n\t\t\treq.off('error', onError);\n\t\t\tsignal?.removeEventListener('abort', onAborted);\n\t\t};\n\t\tconst onData = (chunk: Buffer | string): void => {\n\t\t\tif (settled) return;\n\t\t\tconst chunkSize = typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.byteLength;\n\t\t\tbodySize += chunkSize;\n\t\t\tif (maxBodySize > 0 && bodySize > maxBodySize) {\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treq.resume();\n\t\t\t\tresolve(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbody += typeof chunk === 'string' ? chunk : decoder.write(chunk);\n\t\t};\n\t\tconst onEnd = (): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(body + decoder.end());\n\t\t};\n\t\tconst onAborted = (): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treq.resume();\n\t\t\treject(new RequestBodyAbortedError());\n\t\t};\n\t\tconst onError = (error: Error): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(error);\n\t\t};\n\n\t\treq.on('data', onData);\n\t\treq.once('end', onEnd);\n\t\treq.once('aborted', onAborted);\n\t\treq.once('close', onAborted);\n\t\treq.once('error', onError);\n\t\tsignal?.addEventListener('abort', onAborted, { once: true });\n\t\tif (signal?.aborted) onAborted();\n\t});\n}\n"],"names":["RequestBodyAbortedError","Error","sendJsonRpcError","res","statusCode","code","message","id","data","body","undefined","JSON","sendJsonRpcResponse","response","headers","defaultHeaders","sendCorsPreflight","extraAllowHeaders","readRequestBody","req","maxBodySize","signal","Promise","resolve","reject","bodySize","settled","decoder","StringDecoder","cleanup","onData","onEnd","onAborted","onError","chunk","chunkSize","Buffer","error"],"mappings":";AAaO,MAAMA,gCAAgCC;IAC1B,OAAO,0BAA0B;AACpD;AAeO,SAASC,iBACfC,GAAmB,EACnBC,UAAkB,EAClBC,IAAY,EACZC,OAAe,EACfC,KAA6B,IAAI,EACjCC,IAAc;IAEd,MAAMC,OAAgC;QACrC,SAAS;QACTF;QACA,OAAO;YAAEF;YAAMC;QAAQ;IACxB;IACA,IAAIE,AAASE,WAATF,MACFC,KAAK,KAAK,CAA6B,IAAI,GAAGD;IAEhDL,IAAI,SAAS,CAACC,YAAY;QAAE,gBAAgB;IAAmB;IAC/DD,IAAI,GAAG,CAACQ,KAAK,SAAS,CAACF;AACxB;AAUO,SAASG,oBACfT,GAAmB,EACnBU,QAAiB,EACjBT,aAAqB,GAAG,EACxBU,UAAkC,CAAC,CAAC;IAEpC,MAAMC,iBAAyC;QAAE,gBAAgB;IAAmB;IACpFZ,IAAI,SAAS,CAACC,YAAY;QAAE,GAAGW,cAAc;QAAE,GAAGD,OAAO;IAAC;IAC1DX,IAAI,GAAG,CAACQ,KAAK,SAAS,CAACE;AACxB;AAQO,SAASG,kBAAkBb,GAAmB,EAAEc,iBAA4B;IAClF,IAAIA,qBAAqBA,kBAAkB,MAAM,GAAG,GACnDd,IAAI,SAAS,CAAC,gCAAgC,CAAC,cAAc,EAAEc,kBAAkB,IAAI,CAAC,OAAO;IAE9Fd,IAAI,SAAS,CAAC;IACdA,IAAI,GAAG;AACR;AAaO,eAAee,gBACrBC,GAAoB,EACpBC,WAAmB,EACnBC,MAAoB;IAEpB,OAAO,IAAIC,QAAQ,CAACC,SAASC;QAC5B,IAAIf,OAAO;QACX,IAAIgB,WAAW;QACf,IAAIC,UAAU;QACd,MAAMC,UAAU,IAAIC,cAAc;QAElC,MAAMC,UAAU;YACfV,IAAI,GAAG,CAAC,QAAQW;YAChBX,IAAI,GAAG,CAAC,OAAOY;YACfZ,IAAI,GAAG,CAAC,WAAWa;YACnBb,IAAI,GAAG,CAAC,SAASa;YACjBb,IAAI,GAAG,CAAC,SAASc;YACjBZ,QAAQ,oBAAoB,SAASW;QACtC;QACA,MAAMF,SAAS,CAACI;YACf,IAAIR,SAAS;YACb,MAAMS,YAAY,AAAiB,YAAjB,OAAOD,QAAqBE,OAAO,UAAU,CAACF,SAASA,MAAM,UAAU;YACzFT,YAAYU;YACZ,IAAIf,cAAc,KAAKK,WAAWL,aAAa;gBAC9CM,UAAU;gBACVG;gBACAV,IAAI,MAAM;gBACVI,QAAQ;gBACR;YACD;YACAd,QAAQ,AAAiB,YAAjB,OAAOyB,QAAqBA,QAAQP,QAAQ,KAAK,CAACO;QAC3D;QACA,MAAMH,QAAQ;YACb,IAAIL,SAAS;YACbA,UAAU;YACVG;YACAN,QAAQd,OAAOkB,QAAQ,GAAG;QAC3B;QACA,MAAMK,YAAY;YACjB,IAAIN,SAAS;YACbA,UAAU;YACVG;YACAV,IAAI,MAAM;YACVK,OAAO,IAAIxB;QACZ;QACA,MAAMiC,UAAU,CAACI;YAChB,IAAIX,SAAS;YACbA,UAAU;YACVG;YACAL,OAAOa;QACR;QAEAlB,IAAI,EAAE,CAAC,QAAQW;QACfX,IAAI,IAAI,CAAC,OAAOY;QAChBZ,IAAI,IAAI,CAAC,WAAWa;QACpBb,IAAI,IAAI,CAAC,SAASa;QAClBb,IAAI,IAAI,CAAC,SAASc;QAClBZ,QAAQ,iBAAiB,SAASW,WAAW;YAAE,MAAM;QAAK;QAC1D,IAAIX,QAAQ,SAASW;IACtB;AACD"}
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import type { ServerResponse } from 'node:http';
|
|
2
2
|
type ResponseWriter = (response: ServerResponse) => void;
|
|
3
3
|
type WorkFailureHandler = (error: unknown) => void;
|
|
4
|
+
export type PreDispatchCancellationReason = 'peer' | 'shutdown' | 'timeout';
|
|
5
|
+
export interface PreDispatchLease {
|
|
6
|
+
readonly signal: AbortSignal;
|
|
7
|
+
readonly cancellationReason: PreDispatchCancellationReason | null;
|
|
8
|
+
cancel(reason: PreDispatchCancellationReason): void;
|
|
9
|
+
release(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare class PreDispatchTracker {
|
|
12
|
+
private readonly _leases;
|
|
13
|
+
private _accepting;
|
|
14
|
+
acquire(): PreDispatchLease | null;
|
|
15
|
+
closeAdmission(reason: PreDispatchCancellationReason): void;
|
|
16
|
+
join(): Promise<void>;
|
|
17
|
+
}
|
|
4
18
|
export type LifecycleReportingFailure = {
|
|
5
19
|
readonly lifecycleError: unknown;
|
|
6
20
|
readonly reportingError: unknown;
|
|
@@ -30,7 +44,7 @@ export declare class AcceptedWorkTracker {
|
|
|
30
44
|
private readonly _work;
|
|
31
45
|
constructor(_failureReporter: LifecycleFailureReporter);
|
|
32
46
|
get size(): number;
|
|
33
|
-
|
|
47
|
+
transfer(lease: PreDispatchLease, start: () => Promise<void>): Promise<void>;
|
|
34
48
|
join(): Promise<void>;
|
|
35
49
|
}
|
|
36
50
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpRequestLifecycle.d.ts","sourceRoot":"","sources":["../../src/transport/HttpRequestLifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;AACzD,KAAK,kBAAkB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;AAEnD,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,qBAAa,wBAAwB;IAKxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAmC;IACtE,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,6BAA6B,CAAK;gBAEb,UAAU,EAAE,kBAAkB;IAE3D,IAAI,iBAAiB,IAAI,SAAS,yBAAyB,EAAE,CAE5D;IAED,IAAI,qBAAqB,IAAI,MAAM,CAElC;IAED,IAAI,4BAA4B,IAAI,MAAM,CAEzC;IAED,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;CAY5B;AAED,qBAAa,iBAAiB;IAI5B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAJlC,OAAO,CAAC,YAAY,CAAS;gBAGX,SAAS,EAAE,cAAc,EACzB,gBAAgB,EAAE,wBAAwB;IAO5D,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO;CAezC;AAED,qBAAa,mBAAmB;IAGnB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAF7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;gBAErB,gBAAgB,EAAE,wBAAwB;IAEvE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,
|
|
1
|
+
{"version":3,"file":"HttpRequestLifecycle.d.ts","sourceRoot":"","sources":["../../src/transport/HttpRequestLifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;AACzD,KAAK,kBAAkB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;AAEnD,MAAM,MAAM,6BAA6B,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAE5E,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,6BAA6B,GAAG,IAAI,CAAC;IAClE,MAAM,CAAC,MAAM,EAAE,6BAA6B,GAAG,IAAI,CAAC;IACpD,OAAO,IAAI,IAAI,CAAC;CAChB;AAoCD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,UAAU,CAAQ;IAE1B,OAAO,IAAI,gBAAgB,GAAG,IAAI;IAOlC,cAAc,CAAC,MAAM,EAAE,6BAA6B,GAAG,IAAI;IAKrD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAK3B;AAED,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,qBAAa,wBAAwB;IAKxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAmC;IACtE,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,6BAA6B,CAAK;gBAEb,UAAU,EAAE,kBAAkB;IAE3D,IAAI,iBAAiB,IAAI,SAAS,yBAAyB,EAAE,CAE5D;IAED,IAAI,qBAAqB,IAAI,MAAM,CAElC;IAED,IAAI,4BAA4B,IAAI,MAAM,CAEzC;IAED,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;CAY5B;AAED,qBAAa,iBAAiB;IAI5B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAJlC,OAAO,CAAC,YAAY,CAAS;gBAGX,SAAS,EAAE,cAAc,EACzB,gBAAgB,EAAE,wBAAwB;IAO5D,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO;CAezC;AAED,qBAAa,mBAAmB;IAGnB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAF7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;gBAErB,gBAAgB,EAAE,wBAAwB;IAEvE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAK3B"}
|
|
@@ -1,3 +1,52 @@
|
|
|
1
|
+
class TrackedPreDispatchLease {
|
|
2
|
+
_onRelease;
|
|
3
|
+
_controller = new AbortController();
|
|
4
|
+
_settled = Promise.withResolvers();
|
|
5
|
+
_active = true;
|
|
6
|
+
_cancellationReason = null;
|
|
7
|
+
constructor(_onRelease){
|
|
8
|
+
this._onRelease = _onRelease;
|
|
9
|
+
}
|
|
10
|
+
get signal() {
|
|
11
|
+
return this._controller.signal;
|
|
12
|
+
}
|
|
13
|
+
get cancellationReason() {
|
|
14
|
+
return this._cancellationReason;
|
|
15
|
+
}
|
|
16
|
+
get settled() {
|
|
17
|
+
return this._settled.promise;
|
|
18
|
+
}
|
|
19
|
+
cancel(reason) {
|
|
20
|
+
if (!this._active || this._controller.signal.aborted) return;
|
|
21
|
+
this._cancellationReason = reason;
|
|
22
|
+
this._controller.abort();
|
|
23
|
+
}
|
|
24
|
+
release() {
|
|
25
|
+
if (!this._active) return;
|
|
26
|
+
this._active = false;
|
|
27
|
+
this._onRelease(this);
|
|
28
|
+
this._settled.resolve();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
class PreDispatchTracker {
|
|
32
|
+
_leases = new Set();
|
|
33
|
+
_accepting = true;
|
|
34
|
+
acquire() {
|
|
35
|
+
if (!this._accepting) return null;
|
|
36
|
+
const lease = new TrackedPreDispatchLease((released)=>this._leases.delete(released));
|
|
37
|
+
this._leases.add(lease);
|
|
38
|
+
return lease;
|
|
39
|
+
}
|
|
40
|
+
closeAdmission(reason) {
|
|
41
|
+
this._accepting = false;
|
|
42
|
+
for (const lease of this._leases)lease.cancel(reason);
|
|
43
|
+
}
|
|
44
|
+
async join() {
|
|
45
|
+
while(this._leases.size > 0)await Promise.all([
|
|
46
|
+
...this._leases
|
|
47
|
+
].map((lease)=>lease.settled));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
1
50
|
const LIFECYCLE_REPORTING_FAILURE_CAPACITY = 32;
|
|
2
51
|
class LifecycleFailureReporter {
|
|
3
52
|
_onFailure;
|
|
@@ -69,19 +118,32 @@ class AcceptedWorkTracker {
|
|
|
69
118
|
get size() {
|
|
70
119
|
return this._work.size;
|
|
71
120
|
}
|
|
72
|
-
|
|
121
|
+
transfer(lease, start) {
|
|
122
|
+
lease.signal.throwIfAborted();
|
|
123
|
+
const reservation = Promise.withResolvers();
|
|
124
|
+
this._work.add(reservation.promise);
|
|
125
|
+
lease.release();
|
|
126
|
+
let work;
|
|
127
|
+
try {
|
|
128
|
+
work = start();
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this._work.delete(reservation.promise);
|
|
131
|
+
reservation.resolve();
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
73
134
|
const observed = work.catch((error)=>{
|
|
74
135
|
this._failureReporter.report(error);
|
|
75
136
|
});
|
|
76
|
-
this._work.add(observed);
|
|
77
137
|
observed.then(()=>{
|
|
78
|
-
this._work.delete(
|
|
138
|
+
this._work.delete(reservation.promise);
|
|
139
|
+
reservation.resolve();
|
|
79
140
|
});
|
|
141
|
+
return work;
|
|
80
142
|
}
|
|
81
143
|
async join() {
|
|
82
144
|
while(this._work.size > 0)await Promise.all(this._work);
|
|
83
145
|
}
|
|
84
146
|
}
|
|
85
|
-
export { AcceptedWorkTracker, LIFECYCLE_REPORTING_FAILURE_CAPACITY, LifecycleFailureReporter, ResponseFinalizer };
|
|
147
|
+
export { AcceptedWorkTracker, LIFECYCLE_REPORTING_FAILURE_CAPACITY, LifecycleFailureReporter, PreDispatchTracker, ResponseFinalizer };
|
|
86
148
|
|
|
87
149
|
//# sourceMappingURL=HttpRequestLifecycle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport/HttpRequestLifecycle.js","sources":["../../src/transport/HttpRequestLifecycle.ts"],"sourcesContent":["import type { ServerResponse } from 'node:http';\n\ntype ResponseWriter = (response: ServerResponse) => void;\ntype WorkFailureHandler = (error: unknown) => void;\n\nexport type LifecycleReportingFailure = {\n\treadonly lifecycleError: unknown;\n\treadonly reportingError: unknown;\n};\n\n/** Maximum number of reporting failures retained for diagnostics. */\nexport const LIFECYCLE_REPORTING_FAILURE_CAPACITY = 32;\n\nexport class LifecycleFailureReporter {\n\tprivate readonly _reportingFailures: LifecycleReportingFailure[] = [];\n\tprivate _reportingFailureCount = 0;\n\tprivate _droppedReportingFailureCount = 0;\n\n\tconstructor(private readonly _onFailure: WorkFailureHandler) {}\n\n\tget reportingFailures(): readonly LifecycleReportingFailure[] {\n\t\treturn [...this._reportingFailures];\n\t}\n\n\tget reportingFailureCount(): number {\n\t\treturn this._reportingFailureCount;\n\t}\n\n\tget droppedReportingFailureCount(): number {\n\t\treturn this._droppedReportingFailureCount;\n\t}\n\n\treport(error: unknown): void {\n\t\ttry {\n\t\t\tthis._onFailure(error);\n\t\t} catch (reportingError) {\n\t\t\tthis._reportingFailureCount++;\n\t\t\tif (this._reportingFailures.length === LIFECYCLE_REPORTING_FAILURE_CAPACITY) {\n\t\t\t\tthis._reportingFailures.shift();\n\t\t\t\tthis._droppedReportingFailureCount++;\n\t\t\t}\n\t\t\tthis._reportingFailures.push({ lifecycleError: error, reportingError });\n\t\t}\n\t}\n}\n\nexport class ResponseFinalizer {\n\tprivate _isFinalized = false;\n\n\tconstructor(\n\t\tprivate readonly _response: ServerResponse,\n\t\tprivate readonly _failureReporter: LifecycleFailureReporter\n\t) {\n\t\tthis._response.once('close', () => {\n\t\t\tthis._isFinalized = true;\n\t\t});\n\t}\n\n\tfinalize(writer: ResponseWriter): boolean {\n\t\tif (this._isFinalized || this._response.destroyed || this._response.writableEnded) {\n\t\t\tthis._isFinalized = true;\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._isFinalized = true;\n\t\ttry {\n\t\t\twriter(this._response);\n\t\t} catch (error) {\n\t\t\tthis._failureReporter.report(error);\n\t\t\tthis._response.destroy();\n\t\t}\n\t\treturn true;\n\t}\n}\n\nexport class AcceptedWorkTracker {\n\tprivate readonly _work = new Set<Promise<void>>();\n\n\tconstructor(private readonly _failureReporter: LifecycleFailureReporter) {}\n\n\tget size(): number {\n\t\treturn this._work.size;\n\t}\n\n\
|
|
1
|
+
{"version":3,"file":"transport/HttpRequestLifecycle.js","sources":["../../src/transport/HttpRequestLifecycle.ts"],"sourcesContent":["import type { ServerResponse } from 'node:http';\n\ntype ResponseWriter = (response: ServerResponse) => void;\ntype WorkFailureHandler = (error: unknown) => void;\n\nexport type PreDispatchCancellationReason = 'peer' | 'shutdown' | 'timeout';\n\nexport interface PreDispatchLease {\n\treadonly signal: AbortSignal;\n\treadonly cancellationReason: PreDispatchCancellationReason | null;\n\tcancel(reason: PreDispatchCancellationReason): void;\n\trelease(): void;\n}\n\nclass TrackedPreDispatchLease implements PreDispatchLease {\n\tprivate readonly _controller = new AbortController();\n\tprivate readonly _settled = Promise.withResolvers<void>();\n\tprivate _active = true;\n\tprivate _cancellationReason: PreDispatchCancellationReason | null = null;\n\n\tconstructor(private readonly _onRelease: (lease: TrackedPreDispatchLease) => void) {}\n\n\tget signal(): AbortSignal {\n\t\treturn this._controller.signal;\n\t}\n\n\tget cancellationReason(): PreDispatchCancellationReason | null {\n\t\treturn this._cancellationReason;\n\t}\n\n\tget settled(): Promise<void> {\n\t\treturn this._settled.promise;\n\t}\n\n\tcancel(reason: PreDispatchCancellationReason): void {\n\t\tif (!this._active || this._controller.signal.aborted) return;\n\t\tthis._cancellationReason = reason;\n\t\tthis._controller.abort();\n\t}\n\n\trelease(): void {\n\t\tif (!this._active) return;\n\t\tthis._active = false;\n\t\tthis._onRelease(this);\n\t\tthis._settled.resolve();\n\t}\n}\n\nexport class PreDispatchTracker {\n\tprivate readonly _leases = new Set<TrackedPreDispatchLease>();\n\tprivate _accepting = true;\n\n\tacquire(): PreDispatchLease | null {\n\t\tif (!this._accepting) return null;\n\t\tconst lease = new TrackedPreDispatchLease((released) => this._leases.delete(released));\n\t\tthis._leases.add(lease);\n\t\treturn lease;\n\t}\n\n\tcloseAdmission(reason: PreDispatchCancellationReason): void {\n\t\tthis._accepting = false;\n\t\tfor (const lease of this._leases) lease.cancel(reason);\n\t}\n\n\tasync join(): Promise<void> {\n\t\twhile (this._leases.size > 0) {\n\t\t\tawait Promise.all([...this._leases].map((lease) => lease.settled));\n\t\t}\n\t}\n}\n\nexport type LifecycleReportingFailure = {\n\treadonly lifecycleError: unknown;\n\treadonly reportingError: unknown;\n};\n\n/** Maximum number of reporting failures retained for diagnostics. */\nexport const LIFECYCLE_REPORTING_FAILURE_CAPACITY = 32;\n\nexport class LifecycleFailureReporter {\n\tprivate readonly _reportingFailures: LifecycleReportingFailure[] = [];\n\tprivate _reportingFailureCount = 0;\n\tprivate _droppedReportingFailureCount = 0;\n\n\tconstructor(private readonly _onFailure: WorkFailureHandler) {}\n\n\tget reportingFailures(): readonly LifecycleReportingFailure[] {\n\t\treturn [...this._reportingFailures];\n\t}\n\n\tget reportingFailureCount(): number {\n\t\treturn this._reportingFailureCount;\n\t}\n\n\tget droppedReportingFailureCount(): number {\n\t\treturn this._droppedReportingFailureCount;\n\t}\n\n\treport(error: unknown): void {\n\t\ttry {\n\t\t\tthis._onFailure(error);\n\t\t} catch (reportingError) {\n\t\t\tthis._reportingFailureCount++;\n\t\t\tif (this._reportingFailures.length === LIFECYCLE_REPORTING_FAILURE_CAPACITY) {\n\t\t\t\tthis._reportingFailures.shift();\n\t\t\t\tthis._droppedReportingFailureCount++;\n\t\t\t}\n\t\t\tthis._reportingFailures.push({ lifecycleError: error, reportingError });\n\t\t}\n\t}\n}\n\nexport class ResponseFinalizer {\n\tprivate _isFinalized = false;\n\n\tconstructor(\n\t\tprivate readonly _response: ServerResponse,\n\t\tprivate readonly _failureReporter: LifecycleFailureReporter\n\t) {\n\t\tthis._response.once('close', () => {\n\t\t\tthis._isFinalized = true;\n\t\t});\n\t}\n\n\tfinalize(writer: ResponseWriter): boolean {\n\t\tif (this._isFinalized || this._response.destroyed || this._response.writableEnded) {\n\t\t\tthis._isFinalized = true;\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._isFinalized = true;\n\t\ttry {\n\t\t\twriter(this._response);\n\t\t} catch (error) {\n\t\t\tthis._failureReporter.report(error);\n\t\t\tthis._response.destroy();\n\t\t}\n\t\treturn true;\n\t}\n}\n\nexport class AcceptedWorkTracker {\n\tprivate readonly _work = new Set<Promise<void>>();\n\n\tconstructor(private readonly _failureReporter: LifecycleFailureReporter) {}\n\n\tget size(): number {\n\t\treturn this._work.size;\n\t}\n\n\ttransfer(lease: PreDispatchLease, start: () => Promise<void>): Promise<void> {\n\t\tlease.signal.throwIfAborted();\n\t\tconst reservation = Promise.withResolvers<void>();\n\t\tthis._work.add(reservation.promise);\n\t\tlease.release();\n\n\t\tlet work: Promise<void>;\n\t\ttry {\n\t\t\twork = start();\n\t\t} catch (error) {\n\t\t\tthis._work.delete(reservation.promise);\n\t\t\treservation.resolve();\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst observed = work.catch((error: unknown) => {\n\t\t\tthis._failureReporter.report(error);\n\t\t});\n\t\tvoid observed.then(() => {\n\t\t\tthis._work.delete(reservation.promise);\n\t\t\treservation.resolve();\n\t\t});\n\t\treturn work;\n\t}\n\n\tasync join(): Promise<void> {\n\t\twhile (this._work.size > 0) {\n\t\t\tawait Promise.all(this._work);\n\t\t}\n\t}\n}\n"],"names":["TrackedPreDispatchLease","AbortController","Promise","_onRelease","reason","PreDispatchTracker","Set","lease","released","LIFECYCLE_REPORTING_FAILURE_CAPACITY","LifecycleFailureReporter","_onFailure","error","reportingError","ResponseFinalizer","_response","_failureReporter","writer","AcceptedWorkTracker","start","reservation","work","observed"],"mappings":"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"}
|
|
@@ -86,6 +86,7 @@ export declare class HttpTransport extends BaseTransport implements ITransport {
|
|
|
86
86
|
private _metricsProvider;
|
|
87
87
|
private readonly _lifecycleFailureReporter;
|
|
88
88
|
private readonly _acceptedWork;
|
|
89
|
+
private readonly _preDispatch;
|
|
89
90
|
private _stopPromise;
|
|
90
91
|
constructor(options?: HttpTransportOptions);
|
|
91
92
|
private _requireMcpServer;
|
|
@@ -101,6 +102,7 @@ export declare class HttpTransport extends BaseTransport implements ITransport {
|
|
|
101
102
|
* Track an error in metrics.
|
|
102
103
|
*/
|
|
103
104
|
private _trackError;
|
|
105
|
+
private _parseJsonRpcRequest;
|
|
104
106
|
/**
|
|
105
107
|
* Route and handle incoming HTTP requests.
|
|
106
108
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpTransport.d.ts","sourceRoot":"","sources":["../../src/transport/HttpTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,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;
|
|
1
|
+
{"version":3,"file":"HttpTransport.d.ts","sourceRoot":"","sources":["../../src/transport/HttpTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,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;AAgB3E,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC7D;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,aAAc,SAAQ,aAAc,YAAW,UAAU;IACrE,IAAI,IAAI,IAAI,aAAa,CAExB;IACD,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,qBAAqB,CAAU;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,KAAK,CAAS;IACtB,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,QAAQ,CAAC,YAAY,CAA4B;IACzD,OAAO,CAAC,YAAY,CAA8B;gBAEtC,OAAO,GAAE,oBAAyB;IAwB9C,OAAO,CAAC,iBAAiB;IAOzB;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;OAEG;IACG,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BlD;;OAEG;IACH,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,oBAAoB;IAqC5B;;;;;OAKG;YACW,cAAc;IA4E5B;;;;;OAKG;YACW,kBAAkB;IAiGhC;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CA8BrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAErF"}
|
|
@@ -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)
|
|
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
|
-
|
|
163
|
-
|
|
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',
|
|
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
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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)
|
|
@@ -80,8 +76,7 @@ export interface StreamableHttpTransportOptions extends TransportOptions {
|
|
|
80
76
|
* Streamable HTTP Transport for MCP server.
|
|
81
77
|
*
|
|
82
78
|
* This transport implements the MCP Streamable HTTP specification,
|
|
83
|
-
* providing JSON-RPC over HTTP with optional session management
|
|
84
|
-
* and server-to-client SSE notification streams.
|
|
79
|
+
* providing JSON-RPC over HTTP with optional session management.
|
|
85
80
|
*
|
|
86
81
|
* @remarks
|
|
87
82
|
* **Security Features (inherited from BaseTransport):**
|
|
@@ -93,13 +88,11 @@ export interface StreamableHttpTransportOptions extends TransportOptions {
|
|
|
93
88
|
*
|
|
94
89
|
* **MCP Streamable HTTP Spec Compliance:**
|
|
95
90
|
* - POST /mcp — JSON-RPC method calls
|
|
96
|
-
* - GET /mcp — SSE notification stream (server-to-client)
|
|
97
91
|
* - Mcp-Session-Id header for session management
|
|
98
92
|
* - Content-Type: application/json for JSON-RPC responses
|
|
99
|
-
* - Content-Type: text/event-stream for SSE notification streams
|
|
100
93
|
*
|
|
101
94
|
* **HTTP Status Code Mapping:**
|
|
102
|
-
* - 200: Success (JSON-RPC response
|
|
95
|
+
* - 200: Success (JSON-RPC response)
|
|
103
96
|
* - 202: Accepted (JSON-RPC notification, no response body)
|
|
104
97
|
* - 204: CORS Preflight (empty body)
|
|
105
98
|
* - 400: Bad Request (invalid JSON, invalid session ID)
|
|
@@ -128,6 +121,7 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
|
|
|
128
121
|
private _metricsProvider;
|
|
129
122
|
private readonly _lifecycleFailureReporter;
|
|
130
123
|
private readonly _acceptedWork;
|
|
124
|
+
private readonly _preDispatch;
|
|
131
125
|
private readonly _sessionRetention;
|
|
132
126
|
private _sessionSweepIntervalId;
|
|
133
127
|
private _stopPromise;
|
|
@@ -145,6 +139,7 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
|
|
|
145
139
|
* Connects MCP server to this transport and starts listening.
|
|
146
140
|
*/
|
|
147
141
|
connect(mcpServer: McpServer): Promise<void>;
|
|
142
|
+
private _recordRequestMetrics;
|
|
148
143
|
/**
|
|
149
144
|
* Route and handle incoming HTTP requests.
|
|
150
145
|
*/
|
|
@@ -153,6 +148,7 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
|
|
|
153
148
|
* Send a JSON-RPC error response.
|
|
154
149
|
*/
|
|
155
150
|
private _sendJsonRpcError;
|
|
151
|
+
private _parseJsonRpcRequest;
|
|
156
152
|
/**
|
|
157
153
|
* Handle POST /mcp — JSON-RPC method calls.
|
|
158
154
|
*
|
|
@@ -165,14 +161,6 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
|
|
|
165
161
|
private _handleMcpPost;
|
|
166
162
|
private _acceptPostSession;
|
|
167
163
|
private _receiveAndFinalize;
|
|
168
|
-
/**
|
|
169
|
-
* Handle GET /mcp — Optional SSE notification stream.
|
|
170
|
-
*
|
|
171
|
-
* Per the MCP Streamable HTTP spec, clients may open a GET request
|
|
172
|
-
* to receive server-initiated notifications as SSE events.
|
|
173
|
-
* Requires a valid Mcp-Session-Id in stateful mode.
|
|
174
|
-
*/
|
|
175
|
-
private _handleMcpGet;
|
|
176
164
|
/**
|
|
177
165
|
* Resolve or create a session for a stateful request.
|
|
178
166
|
*
|
|
@@ -186,18 +174,6 @@ export declare class StreamableHttpTransport extends BaseTransport implements IT
|
|
|
186
174
|
* Extract Mcp-Session-Id from request headers.
|
|
187
175
|
*/
|
|
188
176
|
private _getSessionIdFromHeader;
|
|
189
|
-
/**
|
|
190
|
-
* Send an SSE event to a specific client response stream.
|
|
191
|
-
*/
|
|
192
|
-
private _sendSseEvent;
|
|
193
|
-
/**
|
|
194
|
-
* Broadcast a notification to all SSE streams in a given session.
|
|
195
|
-
*
|
|
196
|
-
* @param sessionId - Target session ID
|
|
197
|
-
* @param event - SSE event name
|
|
198
|
-
* @param data - Event payload
|
|
199
|
-
*/
|
|
200
|
-
broadcastToSession(sessionId: string, event: string, data: unknown): void;
|
|
201
177
|
/**
|
|
202
178
|
* Handle GET /metrics endpoint.
|
|
203
179
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamableHttpTransport.d.ts","sourceRoot":"","sources":["../../src/transport/StreamableHttpTransport.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"StreamableHttpTransport.d.ts","sourceRoot":"","sources":["../../src/transport/StreamableHttpTransport.ts"],"names":[],"mappings":"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"}
|