cloakbrowser-mcp 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +45 -18
  2. package/dist/bridge/config.d.ts +1 -1
  3. package/dist/bridge/config.d.ts.map +1 -1
  4. package/dist/bridge/config.js +4 -4
  5. package/dist/bridge/config.js.map +1 -1
  6. package/dist/bridge/env.d.ts +1 -0
  7. package/dist/bridge/env.d.ts.map +1 -1
  8. package/dist/bridge/env.js +3 -0
  9. package/dist/bridge/env.js.map +1 -1
  10. package/dist/bridge/tools.d.ts +3 -1
  11. package/dist/bridge/tools.d.ts.map +1 -1
  12. package/dist/bridge/tools.js +8 -6
  13. package/dist/bridge/tools.js.map +1 -1
  14. package/dist/cli/doctor.d.ts +1 -1
  15. package/dist/cli/doctor.d.ts.map +1 -1
  16. package/dist/cli/doctor.js +3 -3
  17. package/dist/cli/doctor.js.map +1 -1
  18. package/dist/cli/options.d.ts +6 -1
  19. package/dist/cli/options.d.ts.map +1 -1
  20. package/dist/cli/options.js +77 -3
  21. package/dist/cli/options.js.map +1 -1
  22. package/dist/cli.js +18 -7
  23. package/dist/cli.js.map +1 -1
  24. package/dist/http/nodeServer.d.ts +9 -0
  25. package/dist/http/nodeServer.d.ts.map +1 -0
  26. package/dist/http/nodeServer.js +65 -0
  27. package/dist/http/nodeServer.js.map +1 -0
  28. package/dist/http/options.d.ts +19 -2
  29. package/dist/http/options.d.ts.map +1 -1
  30. package/dist/http/options.js +20 -5
  31. package/dist/http/options.js.map +1 -1
  32. package/dist/http/requests.d.ts +11 -0
  33. package/dist/http/requests.d.ts.map +1 -0
  34. package/dist/http/requests.js +54 -0
  35. package/dist/http/requests.js.map +1 -0
  36. package/dist/http/responses.d.ts +6 -0
  37. package/dist/http/responses.d.ts.map +1 -0
  38. package/dist/http/responses.js +24 -0
  39. package/dist/http/responses.js.map +1 -0
  40. package/dist/http/server.d.ts +5 -3
  41. package/dist/http/server.d.ts.map +1 -1
  42. package/dist/http/server.js +42 -116
  43. package/dist/http/server.js.map +1 -1
  44. package/dist/http/sessionStore.d.ts +3 -1
  45. package/dist/http/sessionStore.d.ts.map +1 -1
  46. package/dist/http/sessionStore.js +8 -5
  47. package/dist/http/sessionStore.js.map +1 -1
  48. package/dist/index.d.ts +1 -1
  49. package/dist/index.js +1 -1
  50. package/dist/logging/logger.d.ts +17 -0
  51. package/dist/logging/logger.d.ts.map +1 -0
  52. package/dist/logging/logger.js +107 -0
  53. package/dist/logging/logger.js.map +1 -0
  54. package/dist/protocol/constants.d.ts +5 -0
  55. package/dist/protocol/constants.d.ts.map +1 -0
  56. package/dist/protocol/constants.js +5 -0
  57. package/dist/protocol/constants.js.map +1 -0
  58. package/dist/server.d.ts +1 -1
  59. package/dist/server.js +4 -4
  60. package/package.json +30 -16
  61. package/server.json +5 -5
@@ -0,0 +1,9 @@
1
+ import { type IncomingMessage, type Server as HttpServer, type ServerResponse } from 'node:http';
2
+ import { type Server as HttpsServer } from 'node:https';
3
+ import { type StreamableHttpOptions } from '#src/http/options';
4
+ export type StreamableNodeServer = HttpServer | HttpsServer;
5
+ export declare function createStreamableNodeServer(options: StreamableHttpOptions, requestListener: (req: IncomingMessage, res: ServerResponse) => void): StreamableNodeServer;
6
+ export declare function formatHost(host: string): string;
7
+ export declare function closeHttpServer(server: StreamableNodeServer): Promise<void>;
8
+ export declare function listenHttpServer(server: StreamableNodeServer, port: number, host: string): Promise<void>;
9
+ //# sourceMappingURL=nodeServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeServer.d.ts","sourceRoot":"","sources":["../../src/http/nodeServer.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,MAAM,IAAI,UAAU,EACzB,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAEL,KAAK,MAAM,IAAI,WAAW,EAE3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAEpF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,WAAW,CAAC;AAE5D,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,qBAAqB,EAC9B,eAAe,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,GACnE,oBAAoB,CAMtB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjF;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAkBf"}
@@ -0,0 +1,65 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createServer as createHttpServer, } from 'node:http';
3
+ import { createServer as createHttpsServer, } from 'node:https';
4
+ import { HTTP_PROTOCOL_HTTPS } from '#src/http/options';
5
+ export function createStreamableNodeServer(options, requestListener) {
6
+ if (options.protocol === HTTP_PROTOCOL_HTTPS) {
7
+ return createHttpsServer(readHttpsServerOptions(options), requestListener);
8
+ }
9
+ // HTTP is an explicit local/reverse-proxy mode; use `https` for direct TLS.
10
+ return createHttpServer(requestListener);
11
+ }
12
+ export function formatHost(host) {
13
+ if (host.startsWith('[') && host.endsWith(']'))
14
+ return host;
15
+ return host.includes(':') ? `[${host}]` : host;
16
+ }
17
+ export async function closeHttpServer(server) {
18
+ if (!server.listening)
19
+ return;
20
+ await new Promise((resolve, reject) => {
21
+ server.close((error) => {
22
+ if (error)
23
+ reject(error);
24
+ else
25
+ resolve();
26
+ });
27
+ });
28
+ }
29
+ export async function listenHttpServer(server, port, host) {
30
+ await new Promise((resolve, reject) => {
31
+ const cleanup = () => {
32
+ server.off('error', onError);
33
+ server.off('listening', onListening);
34
+ };
35
+ const onError = (error) => {
36
+ cleanup();
37
+ reject(error);
38
+ };
39
+ const onListening = () => {
40
+ cleanup();
41
+ resolve();
42
+ };
43
+ server.once('error', onError);
44
+ server.once('listening', onListening);
45
+ server.listen(port, host);
46
+ });
47
+ }
48
+ function readHttpsServerOptions(options) {
49
+ const { cert, key, pfx, passphrase } = options.tls;
50
+ if (pfx !== undefined) {
51
+ return {
52
+ pfx: readFileSync(pfx),
53
+ passphrase,
54
+ };
55
+ }
56
+ if (cert !== undefined && key !== undefined) {
57
+ return {
58
+ cert: readFileSync(cert),
59
+ key: readFileSync(key),
60
+ passphrase,
61
+ };
62
+ }
63
+ throw new Error('HTTPS requires either certificate/key files or a PFX file');
64
+ }
65
+ //# sourceMappingURL=nodeServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeServer.js","sourceRoot":"","sources":["../../src/http/nodeServer.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,30 @@
1
- export type BridgeTransportMode = 'stdio' | 'streamable-http';
2
- export type HttpSessionBackend = 'memory';
1
+ export declare const BRIDGE_TRANSPORT_STDIO: "stdio";
2
+ export declare const BRIDGE_TRANSPORT_STREAMABLE_HTTP: "streamable-http";
3
+ export declare const HTTP_PROTOCOL_HTTP: "http";
4
+ export declare const HTTP_PROTOCOL_HTTPS: "https";
5
+ export declare const HTTP_SESSION_BACKEND_MEMORY: "memory";
6
+ export declare const HEALTHZ_PATH: "/healthz";
7
+ export declare const READYZ_PATH: "/readyz";
8
+ export type BridgeTransportMode = typeof BRIDGE_TRANSPORT_STDIO | typeof BRIDGE_TRANSPORT_STREAMABLE_HTTP;
9
+ export type HttpProtocol = typeof HTTP_PROTOCOL_HTTP | typeof HTTP_PROTOCOL_HTTPS;
10
+ export type HttpSessionBackend = typeof HTTP_SESSION_BACKEND_MEMORY;
3
11
  export declare const bridgeTransportModes: readonly ["stdio", "streamable-http"];
12
+ export declare const httpProtocols: readonly ["http", "https"];
4
13
  export declare const httpSessionBackends: readonly ["memory"];
5
14
  export declare const streamableHttpProbePaths: readonly ["/healthz", "/readyz"];
15
+ export interface StreamableHttpTlsOptions {
16
+ cert?: string;
17
+ key?: string;
18
+ pfx?: string;
19
+ passphrase?: string;
20
+ }
6
21
  export interface StreamableHttpOptions {
22
+ protocol: HttpProtocol;
7
23
  host: string;
8
24
  port: number;
9
25
  endpoint: string;
10
26
  authToken?: string;
27
+ tls: StreamableHttpTlsOptions;
11
28
  sessionBackend: HttpSessionBackend;
12
29
  sessionIdleTtlMs: number;
13
30
  sessionMax: number;
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/http/options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,iBAAiB,CAAC;AAC9D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE1C,eAAO,MAAM,oBAAoB,uCAGkB,CAAC;AACpD,eAAO,MAAM,mBAAmB,qBAA8D,CAAC;AAC/F,eAAO,MAAM,wBAAwB,kCAAmC,CAAC;AAEzE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,kBAAkB,CAAC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED,eAAO,MAAM,4BAA4B,EAAE,qBAQ1C,CAAC"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/http/options.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,EAAG,OAAgB,CAAC;AACvD,eAAO,MAAM,gCAAgC,EAAG,iBAA0B,CAAC;AAC3E,eAAO,MAAM,kBAAkB,EAAG,MAAe,CAAC;AAClD,eAAO,MAAM,mBAAmB,EAAG,OAAgB,CAAC;AACpD,eAAO,MAAM,2BAA2B,EAAG,QAAiB,CAAC;AAC7D,eAAO,MAAM,YAAY,EAAG,UAAmB,CAAC;AAChD,eAAO,MAAM,WAAW,EAAG,SAAkB,CAAC;AAE9C,MAAM,MAAM,mBAAmB,GAAG,OAAO,sBAAsB,GAAG,OAAO,gCAAgC,CAAC;AAC1G,MAAM,MAAM,YAAY,GAAG,OAAO,kBAAkB,GAAG,OAAO,mBAAmB,CAAC;AAClF,MAAM,MAAM,kBAAkB,GAAG,OAAO,2BAA2B,CAAC;AAEpE,eAAO,MAAM,oBAAoB,uCAGkB,CAAC;AACpD,eAAO,MAAM,aAAa,4BAGkB,CAAC;AAC7C,eAAO,MAAM,mBAAmB,qBAEkB,CAAC;AACnD,eAAO,MAAM,wBAAwB,kCAAuC,CAAC;AAE7E,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,YAAY,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,wBAAwB,CAAC;IAC9B,cAAc,EAAE,kBAAkB,CAAC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED,eAAO,MAAM,4BAA4B,EAAE,qBAU1C,CAAC"}
@@ -1,14 +1,29 @@
1
+ export const BRIDGE_TRANSPORT_STDIO = 'stdio';
2
+ export const BRIDGE_TRANSPORT_STREAMABLE_HTTP = 'streamable-http';
3
+ export const HTTP_PROTOCOL_HTTP = 'http';
4
+ export const HTTP_PROTOCOL_HTTPS = 'https';
5
+ export const HTTP_SESSION_BACKEND_MEMORY = 'memory';
6
+ export const HEALTHZ_PATH = '/healthz';
7
+ export const READYZ_PATH = '/readyz';
1
8
  export const bridgeTransportModes = [
2
- 'stdio',
3
- 'streamable-http',
9
+ BRIDGE_TRANSPORT_STDIO,
10
+ BRIDGE_TRANSPORT_STREAMABLE_HTTP,
4
11
  ];
5
- export const httpSessionBackends = ['memory'];
6
- export const streamableHttpProbePaths = ['/healthz', '/readyz'];
12
+ export const httpProtocols = [
13
+ HTTP_PROTOCOL_HTTP,
14
+ HTTP_PROTOCOL_HTTPS,
15
+ ];
16
+ export const httpSessionBackends = [
17
+ HTTP_SESSION_BACKEND_MEMORY,
18
+ ];
19
+ export const streamableHttpProbePaths = [HEALTHZ_PATH, READYZ_PATH];
7
20
  export const defaultStreamableHttpOptions = {
21
+ protocol: HTTP_PROTOCOL_HTTP,
8
22
  host: '127.0.0.1',
9
23
  port: 3000,
10
24
  endpoint: '/mcp',
11
- sessionBackend: 'memory',
25
+ tls: {},
26
+ sessionBackend: HTTP_SESSION_BACKEND_MEMORY,
12
27
  sessionIdleTtlMs: 3_600_000,
13
28
  sessionMax: 32,
14
29
  bodyLimitBytes: 1_048_576,
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/http/options.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,OAAO;IACP,iBAAiB;CACgC,CAAC;AACpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,QAAQ,CAAkD,CAAC;AAC/F,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,UAAU,EAAE,SAAS,CAAU,CAAC;AAkBzE,MAAM,CAAC,MAAM,4BAA4B,GAA0B;IACjE,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,MAAM;IAChB,cAAc,EAAE,QAAQ;IACxB,gBAAgB,EAAE,SAAS;IAC3B,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,SAAS;CAC1B,CAAC"}
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/http/options.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAgB,CAAC;AACvD,MAAM,CAAC,MAAM,gCAAgC,GAAG,iBAA0B,CAAC;AAC3E,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAe,CAAC;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAgB,CAAC;AACpD,MAAM,CAAC,MAAM,2BAA2B,GAAG,QAAiB,CAAC;AAC7D,MAAM,CAAC,MAAM,YAAY,GAAG,UAAmB,CAAC;AAChD,MAAM,CAAC,MAAM,WAAW,GAAG,SAAkB,CAAC;AAM9C,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,sBAAsB;IACtB,gCAAgC;CACiB,CAAC;AACpD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,kBAAkB;IAClB,mBAAmB;CACuB,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,2BAA2B;CACqB,CAAC;AACnD,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,YAAY,EAAE,WAAW,CAAU,CAAC;AA2B7E,MAAM,CAAC,MAAM,4BAA4B,GAA0B;IACjE,QAAQ,EAAE,kBAAkB;IAC5B,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,MAAM;IAChB,GAAG,EAAE,EAAE;IACP,cAAc,EAAE,2BAA2B;IAC3C,gBAAgB,EAAE,SAAS;IAC3B,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,SAAS;CAC1B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ import { type StreamableHttpOptions } from '#src/http/options';
3
+ export declare function isEndpointRequest(req: IncomingMessage, endpoint: string, fallbackHost: string, protocol?: StreamableHttpOptions['protocol']): boolean;
4
+ export declare function requestPathName(req: IncomingMessage, fallbackHost: string, protocol: StreamableHttpOptions['protocol']): string;
5
+ export declare function hasJsonContentType(req: IncomingMessage): boolean;
6
+ export declare function readJsonBody(req: IncomingMessage, limitBytes: number): Promise<unknown>;
7
+ export declare function containsInitializeRequest(value: unknown): boolean;
8
+ export declare function getSingleHeader(req: IncomingMessage, name: string): string | undefined;
9
+ export declare class RequestBodyTooLargeError extends Error {
10
+ }
11
+ //# sourceMappingURL=requests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requests.d.ts","sourceRoot":"","sources":["../../src/http/requests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG/D,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,qBAAqB,CAAC,UAAU,CAAU,GACnD,OAAO,CAIT;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,eAAe,EACpB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,GAC1C,MAAM,CAOR;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAGhE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB7F;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGjE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAItF;AAED,qBAAa,wBAAyB,SAAQ,KAAK;CAAG"}
@@ -0,0 +1,54 @@
1
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
2
+ import { formatHost } from '#src/http/nodeServer';
3
+ import { JSON_CONTENT_TYPE } from '#src/protocol/constants';
4
+ export function isEndpointRequest(req, endpoint, fallbackHost, protocol = 'http') {
5
+ const host = getSingleHeader(req, 'host') ?? formatHost(fallbackHost);
6
+ const url = new URL(req.url ?? '/', `${protocol}://${host}`);
7
+ return url.pathname === endpoint;
8
+ }
9
+ export function requestPathName(req, fallbackHost, protocol) {
10
+ const host = getSingleHeader(req, 'host') ?? formatHost(fallbackHost);
11
+ try {
12
+ return new URL(req.url ?? '/', `${protocol}://${host}`).pathname;
13
+ }
14
+ catch {
15
+ return fallbackPathName(req.url);
16
+ }
17
+ }
18
+ export function hasJsonContentType(req) {
19
+ const contentType = getSingleHeader(req, 'content-type');
20
+ return contentType?.toLowerCase().includes(JSON_CONTENT_TYPE) ?? false;
21
+ }
22
+ export async function readJsonBody(req, limitBytes) {
23
+ const contentLength = getSingleHeader(req, 'content-length');
24
+ if (contentLength !== undefined && Number.parseInt(contentLength, 10) > limitBytes) {
25
+ throw new RequestBodyTooLargeError();
26
+ }
27
+ const chunks = [];
28
+ let size = 0;
29
+ for await (const chunk of req) {
30
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
31
+ size += buffer.byteLength;
32
+ if (size > limitBytes)
33
+ throw new RequestBodyTooLargeError();
34
+ chunks.push(buffer);
35
+ }
36
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
37
+ }
38
+ export function containsInitializeRequest(value) {
39
+ const messages = Array.isArray(value) ? value : [value];
40
+ return messages.some((message) => isInitializeRequest(message));
41
+ }
42
+ export function getSingleHeader(req, name) {
43
+ const value = req.headers[name];
44
+ if (Array.isArray(value))
45
+ return value[0];
46
+ return value;
47
+ }
48
+ export class RequestBodyTooLargeError extends Error {
49
+ }
50
+ function fallbackPathName(url) {
51
+ const pathName = (url ?? '/').split(/[?#]/u, 1)[0];
52
+ return pathName.length > 0 ? pathName : '/';
53
+ }
54
+ //# sourceMappingURL=requests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requests.js","sourceRoot":"","sources":["../../src/http/requests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ import { HttpStatus, JsonRpcErrorCode } from '#src/http/status';
3
+ export declare function writeJsonRpcError(res: ServerResponse, status: HttpStatus, code: JsonRpcErrorCode, message: string, headers?: Record<string, string>): void;
4
+ export declare function writeJsonResponse(res: ServerResponse, status: HttpStatus, body: Record<string, unknown>, headers?: Record<string, string>): void;
5
+ export declare function endResponse(res: ServerResponse, body?: string): void;
6
+ //# sourceMappingURL=responses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/http/responses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGhE,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,IAAI,CAWN;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,IAAI,CAMN;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,SAAK,GAAG,IAAI,CAEhE"}
@@ -0,0 +1,24 @@
1
+ import { JSON_CONTENT_TYPE, JSON_RPC_VERSION } from '#src/protocol/constants';
2
+ export function writeJsonRpcError(res, status, code, message, headers = {}) {
3
+ const body = JSON.stringify({
4
+ jsonrpc: JSON_RPC_VERSION,
5
+ error: { code, message },
6
+ id: null,
7
+ });
8
+ res.writeHead(status, {
9
+ 'Content-Type': JSON_CONTENT_TYPE,
10
+ ...headers,
11
+ });
12
+ endResponse(res, body);
13
+ }
14
+ export function writeJsonResponse(res, status, body, headers = {}) {
15
+ res.writeHead(status, {
16
+ 'Content-Type': JSON_CONTENT_TYPE,
17
+ ...headers,
18
+ });
19
+ endResponse(res, JSON.stringify(body));
20
+ }
21
+ export function endResponse(res, body = '') {
22
+ res.end(body);
23
+ }
24
+ //# sourceMappingURL=responses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responses.js","sourceRoot":"","sources":["../../src/http/responses.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,UAAU,iBAAiB,CAC/B,GAAmB,EACnB,MAAkB,EAClB,IAAsB,EACtB,OAAe,EACf,UAAkC,EAAE;IAEpC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,OAAO,EAAE,gBAAgB;QACzB,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;QACxB,EAAE,EAAE,IAAI;KACT,CAAC,CAAC;IACH,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE;QACpB,cAAc,EAAE,iBAAiB;QACjC,GAAG,OAAO;KACX,CAAC,CAAC;IACH,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,GAAmB,EACnB,MAAkB,EAClB,IAA6B,EAC7B,UAAkC,EAAE;IAEpC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE;QACpB,cAAc,EAAE,iBAAiB;QACjC,GAAG,OAAO;KACX,CAAC,CAAC;IACH,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAmB,EAAE,IAAI,GAAG,EAAE;IACxD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -1,11 +1,13 @@
1
1
  import { type IncomingMessage } from 'node:http';
2
2
  import type { AddressInfo } from 'node:net';
3
3
  import { type Implementation } from '@modelcontextprotocol/sdk/types.js';
4
- import { type SessionStore } from './sessionStore.js';
5
- import { type HttpSessionBackend, type StreamableHttpOptions } from './options.js';
4
+ import { type HttpSessionBackend, type StreamableHttpOptions } from '#src/http/options';
5
+ import { type SessionStore } from '#src/http/sessionStore';
6
+ import type { BridgeLogger } from '#src/logging/logger';
6
7
  export interface StartStreamableHttpBridgeOptions extends StreamableHttpOptions {
7
8
  serverInfo?: Partial<Implementation>;
8
9
  sessionStore?: SessionStore;
10
+ logger?: BridgeLogger;
9
11
  }
10
12
  export interface StreamableHttpBridgeServer {
11
13
  url: string;
@@ -14,6 +16,6 @@ export interface StreamableHttpBridgeServer {
14
16
  }
15
17
  export declare function startStreamableHttpBridge(options: StartStreamableHttpBridgeOptions): Promise<StreamableHttpBridgeServer>;
16
18
  export declare function isAuthorizedRequest(req: IncomingMessage, authToken: string | undefined): boolean;
17
- export declare function isEndpointRequest(req: IncomingMessage, endpoint: string, fallbackHost: string): boolean;
19
+ export { isEndpointRequest } from '#src/http/requests';
18
20
  export type { HttpSessionBackend };
19
21
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAoC,MAAM,WAAW,CAAC;AACjG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAE9F,OAAO,EAA8C,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAClG,OAAO,EAA4B,KAAK,kBAAkB,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAQ7G,MAAM,WAAW,gCAAiC,SAAQ,qBAAqB;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACrC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAQD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,gCAAgC,GACxC,OAAO,CAAC,0BAA0B,CAAC,CAGrC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUhG;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAIvG;AAgcD,YAAY,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,eAAe,EAAuB,MAAM,WAAW,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAIL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAuBxD,MAAM,WAAW,gCAAiC,SAAQ,qBAAqB;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACrC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAQD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,gCAAgC,GACxC,OAAO,CAAC,0BAA0B,CAAC,CAGrC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUhG;AAED,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA6WvD,YAAY,EAAE,kBAAkB,EAAE,CAAC"}
@@ -1,14 +1,13 @@
1
1
  import { randomUUID, timingSafeEqual } from 'node:crypto';
2
- import { createServer } from 'node:http';
3
2
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4
- import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
5
- import { createBridgeServer } from '../server.js';
6
- import { createSessionStore } from './sessionStore.js';
7
- import { streamableHttpProbePaths } from './options.js';
8
- import { HttpStatus, JsonRpcErrorCode } from './status.js';
9
- const mcpSessionIdHeader = 'mcp-session-id';
10
- const jsonRpcContentType = 'application/json';
11
- const [healthzPath, readyzPath] = streamableHttpProbePaths;
3
+ import { BRIDGE_TRANSPORT_STREAMABLE_HTTP, HEALTHZ_PATH, READYZ_PATH, } from '#src/http/options';
4
+ import { HTTP_SESSION_STATUS_ACTIVE, createSessionStore, } from '#src/http/sessionStore';
5
+ import { HttpStatus, JsonRpcErrorCode } from '#src/http/status';
6
+ import { MCP_SESSION_ID_HEADER } from '#src/protocol/constants';
7
+ import { createBridgeServer } from '#src/server';
8
+ import { closeHttpServer, createStreamableNodeServer, formatHost, listenHttpServer, } from '#src/http/nodeServer';
9
+ import { RequestBodyTooLargeError, containsInitializeRequest, getSingleHeader, hasJsonContentType, isEndpointRequest, readJsonBody, requestPathName, } from '#src/http/requests';
10
+ import { endResponse, writeJsonResponse, writeJsonRpcError } from '#src/http/responses';
12
11
  const allowedMethods = 'GET, POST, DELETE';
13
12
  export async function startStreamableHttpBridge(options) {
14
13
  const controller = new StreamableHttpBridgeController(options);
@@ -26,11 +25,7 @@ export function isAuthorizedRequest(req, authToken) {
26
25
  const [scheme, token] = parts;
27
26
  return scheme.toLowerCase() === 'bearer' && timingSafeStringEqual(token, authToken);
28
27
  }
29
- export function isEndpointRequest(req, endpoint, fallbackHost) {
30
- const host = getSingleHeader(req, 'host') ?? formatHost(fallbackHost);
31
- const url = new URL(req.url ?? '/', `http://${host}`);
32
- return url.pathname === endpoint;
33
- }
28
+ export { isEndpointRequest } from '#src/http/requests';
34
29
  class StreamableHttpBridgeController {
35
30
  #options;
36
31
  #store;
@@ -43,9 +38,10 @@ class StreamableHttpBridgeController {
43
38
  constructor(options) {
44
39
  this.#options = options;
45
40
  this.#store = options.sessionStore ?? createSessionStore(options.sessionBackend);
46
- this.#httpServer = createServer((req, res) => {
41
+ const requestListener = (req, res) => {
47
42
  void this.#handleRequest(req, res);
48
- });
43
+ };
44
+ this.#httpServer = createStreamableNodeServer(options, requestListener);
49
45
  this.#cleanupTimer = setInterval(() => {
50
46
  void this.#closeExpiredSessions();
51
47
  }, Math.min(options.sessionIdleTtlMs, 60_000));
@@ -59,7 +55,7 @@ class StreamableHttpBridgeController {
59
55
  }
60
56
  return {
61
57
  address,
62
- url: `http://${formatHost(address.address)}:${address.port}${this.#options.endpoint}`,
58
+ url: `${this.#options.protocol}://${formatHost(address.address)}:${address.port}${this.#options.endpoint}`,
63
59
  close: async () => {
64
60
  clearInterval(this.#cleanupTimer);
65
61
  await Promise.allSettled([...this.#sessions.keys()].map((id) => this.#closeSession(id)));
@@ -69,16 +65,18 @@ class StreamableHttpBridgeController {
69
65
  };
70
66
  }
71
67
  async #handleRequest(req, res) {
68
+ const startedAt = Date.now();
69
+ this.#logRequestOnFinish(req, res, startedAt);
72
70
  try {
73
- if (isEndpointRequest(req, healthzPath, this.#options.host)) {
71
+ if (isEndpointRequest(req, HEALTHZ_PATH, this.#options.host, this.#options.protocol)) {
74
72
  this.#handleHealthProbe(req, res);
75
73
  return;
76
74
  }
77
- if (isEndpointRequest(req, readyzPath, this.#options.host)) {
75
+ if (isEndpointRequest(req, READYZ_PATH, this.#options.host, this.#options.protocol)) {
78
76
  await this.#handleReadinessProbe(req, res);
79
77
  return;
80
78
  }
81
- if (!isEndpointRequest(req, this.#options.endpoint, this.#options.host)) {
79
+ if (!isEndpointRequest(req, this.#options.endpoint, this.#options.host, this.#options.protocol)) {
82
80
  writeJsonRpcError(res, HttpStatus.NotFound, JsonRpcErrorCode.ServerError, 'Not found');
83
81
  return;
84
82
  }
@@ -107,7 +105,7 @@ class StreamableHttpBridgeController {
107
105
  writeJsonRpcError(res, HttpStatus.InternalServerError, JsonRpcErrorCode.InternalError, 'Internal server error');
108
106
  }
109
107
  else {
110
- res.end();
108
+ endResponse(res);
111
109
  }
112
110
  }
113
111
  }
@@ -129,7 +127,7 @@ class StreamableHttpBridgeController {
129
127
  }
130
128
  return;
131
129
  }
132
- const sessionId = getSingleHeader(req, mcpSessionIdHeader);
130
+ const sessionId = getSingleHeader(req, MCP_SESSION_ID_HEADER);
133
131
  if (sessionId) {
134
132
  await this.#handleSessionRequest(req, res, parsedBody);
135
133
  return;
@@ -153,7 +151,7 @@ class StreamableHttpBridgeController {
153
151
  createdAt: now,
154
152
  lastSeenAt: now,
155
153
  expiresAt: now + this.#options.sessionIdleTtlMs,
156
- status: 'active',
154
+ status: HTTP_SESSION_STATUS_ACTIVE,
157
155
  };
158
156
  const transport = new StreamableHTTPServerTransport({
159
157
  sessionIdGenerator: () => sessionId,
@@ -186,7 +184,7 @@ class StreamableHttpBridgeController {
186
184
  }
187
185
  }
188
186
  async #handleSessionRequest(req, res, parsedBody) {
189
- const sessionId = getSingleHeader(req, mcpSessionIdHeader);
187
+ const sessionId = getSingleHeader(req, MCP_SESSION_ID_HEADER);
190
188
  if (!sessionId) {
191
189
  writeJsonRpcError(res, HttpStatus.BadRequest, JsonRpcErrorCode.ServerError, 'Bad Request: Mcp-Session-Id header is required');
192
190
  return;
@@ -206,9 +204,10 @@ class StreamableHttpBridgeController {
206
204
  const record = await this.#store.get(sessionId);
207
205
  const session = this.#sessions.get(sessionId);
208
206
  const now = Date.now();
209
- if (!record || record.status !== 'active' || record.expiresAt <= now || !session) {
210
- if (record?.status === 'active' && record.expiresAt <= now)
207
+ if (!record || record.status !== HTTP_SESSION_STATUS_ACTIVE || record.expiresAt <= now || !session) {
208
+ if (record?.status === HTTP_SESSION_STATUS_ACTIVE && record.expiresAt <= now) {
211
209
  await this.#closeSession(sessionId);
210
+ }
212
211
  return undefined;
213
212
  }
214
213
  return session;
@@ -245,7 +244,7 @@ class StreamableHttpBridgeController {
245
244
  writeJsonResponse(res, HttpStatus.Ok, {
246
245
  status: 'ok',
247
246
  version: this.#options.serverInfo?.version ?? 'unknown',
248
- transport: 'streamable-http',
247
+ transport: BRIDGE_TRANSPORT_STREAMABLE_HTTP,
249
248
  uptimeMs: this.#uptimeMs(),
250
249
  });
251
250
  }
@@ -265,7 +264,7 @@ class StreamableHttpBridgeController {
265
264
  writeJsonResponse(res, ready ? HttpStatus.Ok : HttpStatus.ServiceUnavailable, {
266
265
  status: ready ? 'ready' : 'not_ready',
267
266
  version: this.#options.serverInfo?.version ?? 'unknown',
268
- transport: 'streamable-http',
267
+ transport: BRIDGE_TRANSPORT_STREAMABLE_HTTP,
269
268
  uptimeMs: this.#uptimeMs(),
270
269
  sessions: {
271
270
  active,
@@ -284,99 +283,26 @@ class StreamableHttpBridgeController {
284
283
  #uptimeMs() {
285
284
  return Math.max(Date.now() - this.#startedAt, 0);
286
285
  }
287
- }
288
- function hasJsonContentType(req) {
289
- const contentType = getSingleHeader(req, 'content-type');
290
- return contentType?.toLowerCase().includes(jsonRpcContentType) ?? false;
291
- }
292
- async function readJsonBody(req, limitBytes) {
293
- const contentLength = getSingleHeader(req, 'content-length');
294
- if (contentLength !== undefined && Number.parseInt(contentLength, 10) > limitBytes) {
295
- throw new RequestBodyTooLargeError();
296
- }
297
- const chunks = [];
298
- let size = 0;
299
- for await (const chunk of req) {
300
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
301
- size += buffer.byteLength;
302
- if (size > limitBytes)
303
- throw new RequestBodyTooLargeError();
304
- chunks.push(buffer);
286
+ #logRequestOnFinish(req, res, startedAt) {
287
+ const logger = this.#options.logger;
288
+ if (!logger)
289
+ return;
290
+ res.once('finish', () => {
291
+ const method = req.method ?? 'UNKNOWN';
292
+ const pathName = requestPathName(req, this.#options.host, this.#options.protocol);
293
+ const durationMs = Math.max(Date.now() - startedAt, 0);
294
+ logger.info({
295
+ duration_ms: durationMs,
296
+ method,
297
+ path: pathName,
298
+ status: res.statusCode,
299
+ }, 'http request');
300
+ });
305
301
  }
306
- return JSON.parse(Buffer.concat(chunks).toString('utf8'));
307
- }
308
- function containsInitializeRequest(value) {
309
- const messages = Array.isArray(value) ? value : [value];
310
- return messages.some((message) => isInitializeRequest(message));
311
- }
312
- function getSingleHeader(req, name) {
313
- const value = req.headers[name];
314
- if (Array.isArray(value))
315
- return value[0];
316
- return value;
317
- }
318
- function writeJsonRpcError(res, status, code, message, headers = {}, data) {
319
- const error = { code, message };
320
- if (data !== undefined)
321
- error.data = data;
322
- res.writeHead(status, {
323
- 'Content-Type': jsonRpcContentType,
324
- ...headers,
325
- });
326
- res.end(JSON.stringify({
327
- jsonrpc: '2.0',
328
- error,
329
- id: null,
330
- }));
331
- }
332
- function writeJsonResponse(res, status, body, headers = {}) {
333
- res.writeHead(status, {
334
- 'Content-Type': jsonRpcContentType,
335
- ...headers,
336
- });
337
- res.end(JSON.stringify(body));
338
- }
339
- function formatHost(host) {
340
- if (host.startsWith('[') && host.endsWith(']'))
341
- return host;
342
- return host.includes(':') ? `[${host}]` : host;
343
302
  }
344
303
  function timingSafeStringEqual(actual, expected) {
345
304
  const actualBuffer = Buffer.from(actual);
346
305
  const expectedBuffer = Buffer.from(expected);
347
306
  return actualBuffer.length === expectedBuffer.length && timingSafeEqual(actualBuffer, expectedBuffer);
348
307
  }
349
- async function closeHttpServer(server) {
350
- if (!server.listening)
351
- return;
352
- await new Promise((resolve, reject) => {
353
- server.close((error) => {
354
- if (error)
355
- reject(error);
356
- else
357
- resolve();
358
- });
359
- });
360
- }
361
- async function listenHttpServer(server, port, host) {
362
- await new Promise((resolve, reject) => {
363
- const cleanup = () => {
364
- server.off('error', onError);
365
- server.off('listening', onListening);
366
- };
367
- const onError = (error) => {
368
- cleanup();
369
- reject(error);
370
- };
371
- const onListening = () => {
372
- cleanup();
373
- resolve();
374
- };
375
- server.once('error', onError);
376
- server.once('listening', onListening);
377
- server.listen(port, host);
378
- });
379
- }
380
- class RequestBodyTooLargeError extends Error {
381
- }
382
308
  //# sourceMappingURL=server.js.map