@mongodb-js/mcp-http-runners 3.0.0-prerelease.1

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.
@@ -0,0 +1,28 @@
1
+ import express from "express";
2
+ import type http from "http";
3
+ import type { ILogger, HttpServerOptions } from "@mongodb-js/mcp-types";
4
+ export type ExpressBasedHttpServerOptions = {
5
+ logContext: string;
6
+ http: HttpServerOptions;
7
+ };
8
+ /**
9
+ * Base class for Express-based HTTP servers.
10
+ * Provides common functionality for starting and stopping HTTP servers.
11
+ */
12
+ export declare abstract class ExpressBasedHttpServer {
13
+ protected httpServer: http.Server | undefined;
14
+ protected app: express.Express;
15
+ protected readonly logger: ILogger;
16
+ protected readonly logContext: string;
17
+ protected readonly httpOptions: HttpServerOptions;
18
+ constructor({ options, logger }: {
19
+ options: ExpressBasedHttpServerOptions;
20
+ logger: ILogger;
21
+ });
22
+ private checkHttpHost;
23
+ get serverAddress(): string;
24
+ protected abstract setupRoutes(): Promise<void>;
25
+ start(): Promise<void>;
26
+ stop(): Promise<void>;
27
+ }
28
+ //# sourceMappingURL=expressBasedHttpServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressBasedHttpServer.d.ts","sourceRoot":"","sources":["../src/expressBasedHttpServer.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGxE,MAAM,MAAM,6BAA6B,GAAG;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,8BAAsB,sBAAsB;IACxC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;IAC9C,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC;IAE/B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACnC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;gBAEtC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QAAE,OAAO,EAAE,6BAA6B,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE;IAQ5F,OAAO,CAAC,aAAa;IAerB,IAAW,aAAa,IAAI,MAAM,CASjC;IAED,SAAS,CAAC,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAElC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAgCrC"}
@@ -0,0 +1,92 @@
1
+ import express from "express";
2
+ import { LogId } from "@mongodb-js/mcp-core";
3
+ /**
4
+ * Base class for Express-based HTTP servers.
5
+ * Provides common functionality for starting and stopping HTTP servers.
6
+ */
7
+ export class ExpressBasedHttpServer {
8
+ constructor({ options, logger }) {
9
+ this.app = express();
10
+ this.app.enable("trust proxy"); // needed for reverse proxy support
11
+ this.httpOptions = options.http;
12
+ this.logger = logger;
13
+ this.logContext = options.logContext;
14
+ }
15
+ checkHttpHost() {
16
+ const host = this.httpOptions.host.trim();
17
+ const safeHosts = new Set(["127.0.0.1", "localhost", "::1"]);
18
+ const shouldWarn = !safeHosts.has(host) && host !== "";
19
+ if (shouldWarn) {
20
+ this.logger.warning({
21
+ id: LogId.streamableHttpTransportHttpHostWarning,
22
+ context: this.logContext,
23
+ message: `Binding to ${this.httpOptions.host} can expose the MCP Server to the entire local network, which allows other devices on the same network to potentially access the MCP Server. This is a security risk and could allow unauthorized access to your database context.`,
24
+ noRedaction: true,
25
+ });
26
+ }
27
+ }
28
+ get serverAddress() {
29
+ const result = this.httpServer?.address();
30
+ if (typeof result === "string") {
31
+ return result;
32
+ }
33
+ if (typeof result === "object" && result) {
34
+ return `http://${result.address}:${result.port}`;
35
+ }
36
+ throw new Error("Server is not started yet");
37
+ }
38
+ async start() {
39
+ this.checkHttpHost();
40
+ await this.setupRoutes();
41
+ const { port, host } = this.httpOptions;
42
+ this.httpServer = await new Promise((resolve, reject) => {
43
+ const result = this.app.listen(port, host, (err) => {
44
+ if (err) {
45
+ reject(err);
46
+ }
47
+ else {
48
+ resolve(result);
49
+ }
50
+ });
51
+ });
52
+ this.logger.info({
53
+ message: `Http server started on address: ${this.serverAddress}`,
54
+ context: this.logContext,
55
+ noRedaction: true,
56
+ id: LogId.httpServerStarted,
57
+ });
58
+ }
59
+ async stop() {
60
+ if (this.httpServer) {
61
+ this.logger.info({
62
+ message: "Stopping server...",
63
+ context: this.logContext,
64
+ id: LogId.httpServerStopping,
65
+ });
66
+ const server = this.httpServer;
67
+ await new Promise((resolve, reject) => {
68
+ server.close((err) => {
69
+ if (err) {
70
+ reject(err);
71
+ }
72
+ else {
73
+ resolve(undefined);
74
+ }
75
+ });
76
+ });
77
+ this.logger.info({
78
+ message: "Server stopped",
79
+ context: this.logContext,
80
+ id: LogId.httpServerStopped,
81
+ });
82
+ }
83
+ else {
84
+ this.logger.info({
85
+ message: "Server is not running",
86
+ context: this.logContext,
87
+ id: LogId.httpServerStopped,
88
+ });
89
+ }
90
+ }
91
+ }
92
+ //# sourceMappingURL=expressBasedHttpServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressBasedHttpServer.js","sourceRoot":"","sources":["../src/expressBasedHttpServer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export type { StreamableHttpRunnerOptions } from "./streamableHttpRunner.js";
2
+ export { StreamableHttpRunner } from "./streamableHttpRunner.js";
3
+ export { MCPHttpServer, type MCPHttpServerOptions } from "./mcpHttpServer.js";
4
+ export { MonitoringServer, type MonitoringServerOptions } from "./monitoringServer.js";
5
+ export { ExpressBasedHttpServer, type ExpressBasedHttpServerOptions } from "./expressBasedHttpServer.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAG7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGjE,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAGvF,OAAO,EAAE,sBAAsB,EAAE,KAAK,6BAA6B,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Node.js-specific transport runners
2
+ export { StreamableHttpRunner } from "./streamableHttpRunner.js";
3
+ // HTTP Servers
4
+ export { MCPHttpServer } from "./mcpHttpServer.js";
5
+ export { MonitoringServer } from "./monitoringServer.js";
6
+ // Express HTTP Server base
7
+ export { ExpressBasedHttpServer } from "./expressBasedHttpServer.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,qCAAqC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,eAAe;AACf,OAAO,EAAE,aAAa,EAA6B,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAgC,MAAM,uBAAuB,CAAC;AAEvF,2BAA2B;AAC3B,OAAO,EAAE,sBAAsB,EAAsC,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2
+ import type { ICompositeLogger, IMetrics, DefaultMetricDefinitions, TransportRequestContext, ISessionStore, HttpServerOptions, SessionManagementOptions, SessionServer } from "@mongodb-js/mcp-types";
3
+ import { ExpressBasedHttpServer } from "./expressBasedHttpServer.js";
4
+ /**
5
+ * Options for creating an MCPHttpServer instance.
6
+ */
7
+ export type MCPHttpServerOptions<TMetrics extends DefaultMetricDefinitions = DefaultMetricDefinitions> = {
8
+ options: {
9
+ /** HTTP server options */
10
+ http: HttpServerOptions;
11
+ /** Session management options */
12
+ session: SessionManagementOptions;
13
+ };
14
+ /** Logger for the server */
15
+ logger: ICompositeLogger;
16
+ /** Metrics instance */
17
+ metrics: IMetrics<TMetrics>;
18
+ /** Session store for managing transports */
19
+ sessionStore: ISessionStore<StreamableHTTPServerTransport>;
20
+ };
21
+ /**
22
+ * HTTP server that handles MCP requests over HTTP using the Streamable HTTP transport.
23
+ *
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * class MyMCPHttpServer extends MCPHttpServer {
28
+ * protected override async createServerForRequest(request: TransportRequestContext): Promise<MyServer> {
29
+ * return new MyServer({ ... });
30
+ * }
31
+ * }
32
+ * ```
33
+ */
34
+ export declare abstract class MCPHttpServer<TServer extends SessionServer = SessionServer, TMetrics extends DefaultMetricDefinitions = DefaultMetricDefinitions> extends ExpressBasedHttpServer {
35
+ private readonly sessionStore;
36
+ readonly sessionOptions: SessionManagementOptions;
37
+ protected readonly metrics: IMetrics<TMetrics>;
38
+ private readonly pendingInitializations;
39
+ constructor({ options, logger, metrics, sessionStore }: MCPHttpServerOptions<TMetrics>);
40
+ stop(): Promise<void>;
41
+ /**
42
+ * Creates a server instance for a specific request. Override this method
43
+ * in subclasses to customize per-request server creation.
44
+ */
45
+ protected abstract createServerForRequest(request: TransportRequestContext): Promise<TServer>;
46
+ private reportSessionError;
47
+ private startKeepAliveLoop;
48
+ /**
49
+ * Ensures the session for the given sessionId is initialized, serializing
50
+ * concurrent initialization attempts so only one runs at a time.
51
+ *
52
+ * If a session already exists in the store, this is a no-op.
53
+ * If another request is already initializing this session, this call waits
54
+ * for that initialization to complete.
55
+ * Otherwise, this call performs the initialization.
56
+ *
57
+ * After this method resolves, the caller should look up the transport from
58
+ * the session store via `sessionStore.getSession()`.
59
+ *
60
+ * When `isImplicitInitialization` is true, the transport is pre-configured as
61
+ * initialized (bypassing the MCP initialize handshake) so that it can handle
62
+ * non-initialize requests immediately. When false, the transport is left in
63
+ * its default state so it can process the initialize request normally.
64
+ */
65
+ private ensureSessionInitialized;
66
+ /**
67
+ * Restores the client state negotiated during the session's original
68
+ * initialization onto an implicitly re-initialized server. The server on
69
+ * this pod never saw the client's `initialize` request, so without this
70
+ * it would treat the client as capability-less — e.g. skipping
71
+ * confirmation elicitation for destructive tools. No-op when the session
72
+ * store does not persist negotiated client state.
73
+ */
74
+ private restoreNegotiatedClientState;
75
+ /**
76
+ * Arranges for the client state negotiated by a real `initialize`
77
+ * exchange to be persisted through the session store, so implicit
78
+ * re-initializations of this session can restore it. Wraps the
79
+ * `oninitialized` callback installed by `server.connect`, hence must run
80
+ * after it.
81
+ */
82
+ private captureNegotiatedClientStateOnInitialize;
83
+ protected setupMiddlewares(): void;
84
+ protected setupRoutes(): Promise<void>;
85
+ private withErrorHandling;
86
+ }
87
+ //# sourceMappingURL=mcpHttpServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpHttpServer.d.ts","sourceRoot":"","sources":["../src/mcpHttpServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAGnG,OAAO,KAAK,EACR,gBAAgB,EAEhB,QAAQ,EACR,wBAAwB,EACxB,uBAAuB,EACvB,aAAa,EACb,iBAAiB,EACjB,wBAAwB,EACxB,aAAa,EAEhB,MAAM,uBAAuB,CAAC;AAgB/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE;;GAEG;AACH,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,wBAAwB,GAAG,wBAAwB,IAAI;IACrG,OAAO,EAAE;QACL,0BAA0B;QAC1B,IAAI,EAAE,iBAAiB,CAAC;QACxB,iCAAiC;QACjC,OAAO,EAAE,wBAAwB,CAAC;KACrC,CAAC;IACF,4BAA4B;IAC5B,MAAM,EAAE,gBAAgB,CAAC;IACzB,uBAAuB;IACvB,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5B,4CAA4C;IAC5C,YAAY,EAAE,aAAa,CAAC,6BAA6B,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,8BAAsB,aAAa,CAC/B,OAAO,SAAS,aAAa,GAAG,aAAa,EAC7C,QAAQ,SAAS,wBAAwB,GAAG,wBAAwB,CACtE,SAAQ,sBAAsB;IAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+C;IAC5E,SAAgB,cAAc,EAAE,wBAAwB,CAAC;IACzD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoC;gBAE/D,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,oBAAoB,CAAC,QAAQ,CAAC;IAazE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlC;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;IAE7F,OAAO,CAAC,kBAAkB;YAsCZ,kBAAkB;IAmDhC;;;;;;;;;;;;;;;;OAgBG;YACW,wBAAwB;IA+ItC;;;;;;;OAOG;YACW,4BAA4B;IAqC1C;;;;;;OAMG;IACH,OAAO,CAAC,wCAAwC;IAyBhD,SAAS,CAAC,gBAAgB,IAAI,IAAI;cAmBT,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA+FrD,OAAO,CAAC,iBAAiB;CAqC5B"}
@@ -0,0 +1,440 @@
1
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
3
+ import express from "express";
4
+ import { LogId, JSON_RPC_ERROR_CODE_SESSION_ID_REQUIRED, JSON_RPC_ERROR_CODE_SESSION_ID_INVALID, JSON_RPC_ERROR_CODE_INVALID_REQUEST, JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND, JSON_RPC_ERROR_CODE_DISALLOWED_EXTERNAL_SESSION, JSON_RPC_ERROR_CODE_PROCESSING_REQUEST_FAILED, JSON_RPC_ERROR_CODE_SESSION_LIMIT_EXCEEDED, UserFacingError, getRandomUUID, SessionRejectedError, SessionLimitExceededError, requestIdAttr, } from "@mongodb-js/mcp-core";
5
+ import { ExpressBasedHttpServer } from "./expressBasedHttpServer.js";
6
+ import { sleep } from "./utils.js";
7
+ /**
8
+ * HTTP server that handles MCP requests over HTTP using the Streamable HTTP transport.
9
+ *
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * class MyMCPHttpServer extends MCPHttpServer {
14
+ * protected override async createServerForRequest(request: TransportRequestContext): Promise<MyServer> {
15
+ * return new MyServer({ ... });
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export class MCPHttpServer extends ExpressBasedHttpServer {
21
+ constructor({ options, logger, metrics, sessionStore }) {
22
+ super({
23
+ options: {
24
+ logContext: "mcpHttpServer",
25
+ http: options.http,
26
+ },
27
+ logger,
28
+ });
29
+ this.pendingInitializations = new Map();
30
+ this.sessionOptions = options.session;
31
+ this.metrics = metrics;
32
+ this.sessionStore = sessionStore;
33
+ }
34
+ async stop() {
35
+ await Promise.all([this.sessionStore.closeAllSessions(), super.stop()]);
36
+ }
37
+ reportSessionError(res, errorCode) {
38
+ let message;
39
+ let statusCode = 400;
40
+ switch (errorCode) {
41
+ case JSON_RPC_ERROR_CODE_SESSION_ID_REQUIRED:
42
+ message = "session id is required";
43
+ break;
44
+ case JSON_RPC_ERROR_CODE_SESSION_ID_INVALID:
45
+ message = "session id is invalid";
46
+ break;
47
+ case JSON_RPC_ERROR_CODE_INVALID_REQUEST:
48
+ message = "invalid request";
49
+ break;
50
+ case JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND:
51
+ message = "session not found";
52
+ statusCode = 404;
53
+ break;
54
+ case JSON_RPC_ERROR_CODE_DISALLOWED_EXTERNAL_SESSION:
55
+ message = "cannot provide sessionId when externally managed sessions are disabled";
56
+ break;
57
+ case JSON_RPC_ERROR_CODE_SESSION_LIMIT_EXCEEDED:
58
+ message = "server has reached the maximum number of concurrent sessions, try again later";
59
+ statusCode = 503;
60
+ break;
61
+ default:
62
+ message = "unknown error";
63
+ statusCode = 500;
64
+ }
65
+ res.status(statusCode).json({
66
+ jsonrpc: "2.0",
67
+ error: {
68
+ code: errorCode,
69
+ message,
70
+ },
71
+ });
72
+ }
73
+ async startKeepAliveLoop({ transport, logger, signal, }) {
74
+ if (this.httpOptions.responseType === "json") {
75
+ // Don't start the ping loop for JSON response type since the connection is short-lived and pings aren't needed
76
+ return;
77
+ }
78
+ let failedPings = 0;
79
+ while (!signal.aborted) {
80
+ try {
81
+ logger.debug({
82
+ id: LogId.streamableHttpTransportKeepAlive,
83
+ context: "streamableHttpTransport",
84
+ message: "Sending ping",
85
+ });
86
+ await transport.send({
87
+ jsonrpc: "2.0",
88
+ method: "ping",
89
+ });
90
+ failedPings = 0;
91
+ }
92
+ catch (err) {
93
+ try {
94
+ failedPings++;
95
+ logger.warning({
96
+ id: LogId.streamableHttpTransportKeepAliveFailure,
97
+ context: "streamableHttpTransport",
98
+ message: `Error sending ping (attempt #${failedPings}): ${err instanceof Error ? err.message : String(err)}`,
99
+ });
100
+ if (failedPings > 3) {
101
+ await transport.close();
102
+ return;
103
+ }
104
+ }
105
+ catch {
106
+ // Ignore the error of the transport close
107
+ }
108
+ }
109
+ await sleep(30000, { signal });
110
+ }
111
+ }
112
+ /**
113
+ * Ensures the session for the given sessionId is initialized, serializing
114
+ * concurrent initialization attempts so only one runs at a time.
115
+ *
116
+ * If a session already exists in the store, this is a no-op.
117
+ * If another request is already initializing this session, this call waits
118
+ * for that initialization to complete.
119
+ * Otherwise, this call performs the initialization.
120
+ *
121
+ * After this method resolves, the caller should look up the transport from
122
+ * the session store via `sessionStore.getSession()`.
123
+ *
124
+ * When `isImplicitInitialization` is true, the transport is pre-configured as
125
+ * initialized (bypassing the MCP initialize handshake) so that it can handle
126
+ * non-initialize requests immediately. When false, the transport is left in
127
+ * its default state so it can process the initialize request normally.
128
+ */
129
+ async ensureSessionInitialized({ req, sessionId: providedSessionId, isImplicitInitialization, }) {
130
+ const sessionId = providedSessionId ?? getRandomUUID();
131
+ const requestIdAttrs = requestIdAttr(req.headers);
132
+ // Check if session already exists
133
+ if (await this.sessionStore.getSession(sessionId, req.headers)) {
134
+ return sessionId;
135
+ }
136
+ // Serialize initializations: if another request is initializing, wait for it
137
+ const pendingInit = this.pendingInitializations.get(sessionId);
138
+ if (pendingInit) {
139
+ this.logger.debug({
140
+ id: LogId.streamableHttpTransportSessionNotFound,
141
+ context: "streamableHttpTransport",
142
+ message: `Session with ID ${sessionId} is already being initialized, waiting`,
143
+ attributes: { ...requestIdAttrs },
144
+ });
145
+ try {
146
+ await pendingInit;
147
+ }
148
+ catch {
149
+ // The initializer handles its own error
150
+ }
151
+ return sessionId;
152
+ }
153
+ this.logger.debug({
154
+ id: LogId.streamableHttpTransportSessionNotFound,
155
+ context: "streamableHttpTransport",
156
+ message: `Session with ID ${sessionId} not found, initializing new session`,
157
+ attributes: {
158
+ ...requestIdAttrs,
159
+ },
160
+ });
161
+ const initPromise = (async () => {
162
+ const request = {
163
+ headers: req.headers,
164
+ query: req.query,
165
+ };
166
+ const server = await this.createServerForRequest(request);
167
+ const transport = new StreamableHTTPServerTransport({
168
+ sessionIdGenerator: () => sessionId,
169
+ enableJsonResponse: this.httpOptions.responseType === "json",
170
+ onsessionclosed: async (sessionId) => {
171
+ try {
172
+ await this.sessionStore.closeSession({ sessionId, reason: "transport_closed" });
173
+ }
174
+ catch (error) {
175
+ this.logger.error({
176
+ id: LogId.sessionCloseFailure,
177
+ context: "streamableHttpTransport",
178
+ message: `Error closing session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
179
+ });
180
+ }
181
+ },
182
+ });
183
+ // HACK: When we're implicitly initializing the session, we want to configure the session id and _initialized flag on the transport
184
+ // so that it believes it actually went through the initialization flow. Without it, we'd get errors like "transport not initialized"
185
+ // when we try to use it without initialize request
186
+ if (isImplicitInitialization) {
187
+ const internalTransport = transport["_webStandardTransport"];
188
+ internalTransport._initialized = true;
189
+ internalTransport.sessionId = sessionId;
190
+ }
191
+ server.session?.logger.setAttribute("sessionId", sessionId);
192
+ const keepAliveController = new AbortController();
193
+ void this.startKeepAliveLoop({
194
+ transport,
195
+ logger: server.session?.logger ?? this.logger,
196
+ signal: keepAliveController.signal,
197
+ });
198
+ transport.onclose = () => {
199
+ keepAliveController.abort();
200
+ server.close().catch((error) => {
201
+ this.logger.error({
202
+ id: LogId.streamableHttpTransportCloseFailure,
203
+ context: "streamableHttpTransport",
204
+ message: `Error closing server: ${error instanceof Error ? error.message : String(error)}`,
205
+ });
206
+ });
207
+ };
208
+ await server.connect(transport);
209
+ if (isImplicitInitialization) {
210
+ await this.restoreNegotiatedClientState(server, sessionId, req.headers);
211
+ }
212
+ else {
213
+ this.captureNegotiatedClientStateOnInitialize(server, sessionId, req.headers);
214
+ }
215
+ await this.sessionStore.addSession({
216
+ sessionId,
217
+ transport,
218
+ logger: server.session?.logger ?? this.logger,
219
+ session: server.session,
220
+ // Pass the incoming request headers to the session store so
221
+ // that we can trace the x-request-id and other headers in the
222
+ // resulting logs and downstream requests.
223
+ headers: req.headers,
224
+ });
225
+ })();
226
+ this.pendingInitializations.set(sessionId, initPromise);
227
+ try {
228
+ await initPromise;
229
+ }
230
+ catch (error) {
231
+ this.logger.error({
232
+ id: LogId.streamableHttpTransportRequestFailure,
233
+ context: "streamableHttpTransport",
234
+ message: `Failed to initialize session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
235
+ attributes: { ...requestIdAttrs },
236
+ });
237
+ // Remove the partially initialized session on failure so that
238
+ // subsequent requests don't see a broken session and can retry
239
+ try {
240
+ await this.sessionStore.closeSession({ sessionId, reason: "unknown" });
241
+ }
242
+ catch {
243
+ // Session might not be in the store, that's fine
244
+ }
245
+ throw error;
246
+ }
247
+ finally {
248
+ this.pendingInitializations.delete(sessionId);
249
+ }
250
+ return sessionId;
251
+ }
252
+ /**
253
+ * Restores the client state negotiated during the session's original
254
+ * initialization onto an implicitly re-initialized server. The server on
255
+ * this pod never saw the client's `initialize` request, so without this
256
+ * it would treat the client as capability-less — e.g. skipping
257
+ * confirmation elicitation for destructive tools. No-op when the session
258
+ * store does not persist negotiated client state.
259
+ */
260
+ async restoreNegotiatedClientState(server, sessionId, headers) {
261
+ let state;
262
+ try {
263
+ state = await this.sessionStore.loadNegotiatedClientState(sessionId, headers);
264
+ }
265
+ catch (error) {
266
+ // The restored session stays usable; it just behaves as if the
267
+ // client had no optional capabilities.
268
+ this.logger.warning({
269
+ id: LogId.streamableHttpTransportClientStateRestoreFailure,
270
+ context: "streamableHttpTransport",
271
+ message: `Failed to restore negotiated client state for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
272
+ attributes: { ...requestIdAttr(headers) },
273
+ });
274
+ return;
275
+ }
276
+ if (!state) {
277
+ return;
278
+ }
279
+ // HACK: like the transport `_initialized` flag above, the SDK offers
280
+ // no supported way to seed a Server with a previously negotiated
281
+ // initialization, so we write the private fields the initialize
282
+ // handler would have populated.
283
+ const mcpServer = server.mcpServer;
284
+ const protocolServer = mcpServer.server;
285
+ protocolServer._clientCapabilities = state.clientCapabilities;
286
+ protocolServer._clientVersion = state.clientInfo;
287
+ server.session.setMcpClient(state.clientInfo);
288
+ }
289
+ /**
290
+ * Arranges for the client state negotiated by a real `initialize`
291
+ * exchange to be persisted through the session store, so implicit
292
+ * re-initializations of this session can restore it. Wraps the
293
+ * `oninitialized` callback installed by `server.connect`, hence must run
294
+ * after it.
295
+ */
296
+ captureNegotiatedClientStateOnInitialize(server, sessionId, headers) {
297
+ const protocolServer = server.mcpServer.server;
298
+ const originalOnInitialized = protocolServer.oninitialized;
299
+ protocolServer.oninitialized = () => {
300
+ originalOnInitialized?.();
301
+ const state = {
302
+ clientCapabilities: protocolServer.getClientCapabilities(),
303
+ clientInfo: protocolServer.getClientVersion(),
304
+ };
305
+ this.sessionStore.saveNegotiatedClientState(sessionId, state, headers).catch((error) => {
306
+ this.logger.warning({
307
+ id: LogId.streamableHttpTransportClientStateSaveFailure,
308
+ context: "streamableHttpTransport",
309
+ message: `Failed to save negotiated client state for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
310
+ attributes: { ...requestIdAttr(headers) },
311
+ });
312
+ });
313
+ };
314
+ }
315
+ setupMiddlewares() {
316
+ this.app.use(express.json({ limit: this.httpOptions.bodyLimit ?? 1024 * 1024 }));
317
+ const headers = this.httpOptions.headers;
318
+ if (headers && Object.keys(headers).length > 0) {
319
+ this.app.use((req, res, next) => {
320
+ for (const [key, value] of Object.entries(headers)) {
321
+ const header = req.headers[key.toLowerCase()];
322
+ if (!header || header !== value) {
323
+ res.status(403).json({ error: `Invalid value for header "${key}"` });
324
+ return;
325
+ }
326
+ }
327
+ next();
328
+ });
329
+ }
330
+ }
331
+ // eslint-disable-next-line @typescript-eslint/require-await -- Required for override signature
332
+ async setupRoutes() {
333
+ this.setupMiddlewares();
334
+ const handleSessionRequest = async (req, res) => {
335
+ const sessionId = req.headers["mcp-session-id"];
336
+ if (!sessionId) {
337
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_ID_REQUIRED);
338
+ }
339
+ if (typeof sessionId !== "string") {
340
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_ID_INVALID);
341
+ }
342
+ const requestIdAttrs = requestIdAttr(req.headers);
343
+ let transport = await this.sessionStore.getSession(sessionId, req.headers);
344
+ if (!transport) {
345
+ if (!this.sessionOptions.externallyManagedSessions) {
346
+ this.logger.debug({
347
+ id: LogId.streamableHttpTransportSessionNotFound,
348
+ context: "streamableHttpTransport",
349
+ message: `Session with ID ${sessionId} not found`,
350
+ attributes: { ...requestIdAttrs },
351
+ });
352
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND);
353
+ }
354
+ const resolvedSessionId = await this.ensureSessionInitialized({
355
+ req,
356
+ sessionId,
357
+ isImplicitInitialization: true,
358
+ });
359
+ transport = await this.sessionStore.getSession(resolvedSessionId, req.headers);
360
+ if (!transport) {
361
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND);
362
+ }
363
+ }
364
+ await transport.handleRequest(req, res, req.body);
365
+ };
366
+ this.app.post("/mcp", this.withErrorHandling(async (req, res) => {
367
+ const sessionId = req.headers["mcp-session-id"];
368
+ if (sessionId && typeof sessionId !== "string") {
369
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_ID_INVALID);
370
+ }
371
+ if (isInitializeRequest(req.body)) {
372
+ if (sessionId && !this.sessionOptions.externallyManagedSessions) {
373
+ this.logger.debug({
374
+ id: LogId.streamableHttpTransportDisallowedExternalSessionError,
375
+ context: "streamableHttpTransport",
376
+ message: `Client provided session ID ${sessionId}, but externallyManagedSessions is disabled`,
377
+ attributes: requestIdAttr(req.headers),
378
+ });
379
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_DISALLOWED_EXTERNAL_SESSION);
380
+ }
381
+ const resolvedSessionId = await this.ensureSessionInitialized({
382
+ req,
383
+ sessionId,
384
+ isImplicitInitialization: false,
385
+ });
386
+ const transport = await this.sessionStore.getSession(resolvedSessionId, req.headers);
387
+ if (!transport) {
388
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND);
389
+ }
390
+ await transport.handleRequest(req, res, req.body);
391
+ return;
392
+ }
393
+ if (sessionId) {
394
+ return await handleSessionRequest(req, res);
395
+ }
396
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_INVALID_REQUEST);
397
+ }));
398
+ this.app.get("/mcp", this.withErrorHandling(async (req, res) => {
399
+ if (this.httpOptions.responseType === "sse") {
400
+ await handleSessionRequest(req, res);
401
+ }
402
+ else {
403
+ res.status(405).set("Allow", ["POST", "DELETE"]).send("Method Not Allowed");
404
+ }
405
+ }));
406
+ this.app.delete("/mcp", this.withErrorHandling(handleSessionRequest));
407
+ }
408
+ withErrorHandling(fn) {
409
+ return (req, res, next) => {
410
+ fn(req, res, next).catch((error) => {
411
+ const errorMessage = error instanceof Error ? error.message : String(error);
412
+ this.logger.error({
413
+ id: LogId.streamableHttpTransportRequestFailure,
414
+ context: "streamableHttpTransport",
415
+ message: `Error handling request: ${errorMessage}`,
416
+ attributes: requestIdAttr(req.headers),
417
+ });
418
+ if (error instanceof SessionRejectedError) {
419
+ // Respond exactly as if the session doesn't exist so that
420
+ // callers can't probe whether a session id is valid; the
421
+ // rejection reason is only visible in the log above.
422
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND);
423
+ }
424
+ if (error instanceof SessionLimitExceededError) {
425
+ return this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_LIMIT_EXCEEDED);
426
+ }
427
+ // Only propagate error messages for user-facing errors
428
+ const message = error instanceof UserFacingError ? error.message : `failed to handle request`;
429
+ res.status(400).json({
430
+ jsonrpc: "2.0",
431
+ error: {
432
+ code: JSON_RPC_ERROR_CODE_PROCESSING_REQUEST_FAILED,
433
+ message,
434
+ },
435
+ });
436
+ });
437
+ };
438
+ }
439
+ }
440
+ //# sourceMappingURL=mcpHttpServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpHttpServer.js","sourceRoot":"","sources":["../src/mcpHttpServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAgD,MAAM,oCAAoC,CAAC;AACvH,OAAO,OAAO,MAAM,SAAS,CAAC;AAa9B,OAAO,EACH,KAAK,EACL,uCAAuC,EACvC,sCAAsC,EACtC,mCAAmC,EACnC,qCAAqC,EACrC,+CAA+C,EAC/C,6CAA6C,EAC7C,0CAA0C,EAC1C,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,yBAAyB,EACzB,aAAa,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAoBnC;;;;;;;;;;;;GAYG;AACH,MAAM,OAAgB,aAGpB,SAAQ,sBAAsB;IAM5B,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAkC;QAClF,KAAK,CAAC;YACF,OAAO,EAAE;gBACL,UAAU,EAAE,eAAe;gBAC3B,IAAI,EAAE,OAAO,CAAC,IAAI;aACrB;YACD,MAAM;SACT,CAAC,CAAC;QATU,2BAAsB,GAAG,IAAI,GAAG,EAAyB,CAAC;QAUvE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;IAEM,KAAK,CAAC,IAAI;QACb,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5E,CAAC;IAQO,kBAAkB,CAAC,GAAqB,EAAE,SAAiB;QAC/D,IAAI,OAAe,CAAC;QACpB,IAAI,UAAU,GAAG,GAAG,CAAC;QAErB,QAAQ,SAAS,EAAE,CAAC;YAChB,KAAK,uCAAuC;gBACxC,OAAO,GAAG,wBAAwB,CAAC;gBACnC,MAAM;YACV,KAAK,sCAAsC;gBACvC,OAAO,GAAG,uBAAuB,CAAC;gBAClC,MAAM;YACV,KAAK,mCAAmC;gBACpC,OAAO,GAAG,iBAAiB,CAAC;gBAC5B,MAAM;YACV,KAAK,qCAAqC;gBACtC,OAAO,GAAG,mBAAmB,CAAC;gBAC9B,UAAU,GAAG,GAAG,CAAC;gBACjB,MAAM;YACV,KAAK,+CAA+C;gBAChD,OAAO,GAAG,wEAAwE,CAAC;gBACnF,MAAM;YACV,KAAK,0CAA0C;gBAC3C,OAAO,GAAG,+EAA+E,CAAC;gBAC1F,UAAU,GAAG,GAAG,CAAC;gBACjB,MAAM;YACV;gBACI,OAAO,GAAG,eAAe,CAAC;gBAC1B,UAAU,GAAG,GAAG,CAAC;QACzB,CAAC;QACD,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;YACxB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACH,IAAI,EAAE,SAAS;gBACf,OAAO;aACV;SACJ,CAAC,CAAC;IACP,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,EAC7B,SAAS,EACT,MAAM,EACN,MAAM,GAKT;QACG,IAAI,IAAI,CAAC,WAAW,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;YAC3C,+GAA+G;YAC/G,OAAO;QACX,CAAC;QAED,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC;gBACD,MAAM,CAAC,KAAK,CAAC;oBACT,EAAE,EAAE,KAAK,CAAC,gCAAgC;oBAC1C,OAAO,EAAE,yBAAyB;oBAClC,OAAO,EAAE,cAAc;iBAC1B,CAAC,CAAC;gBAEH,MAAM,SAAS,CAAC,IAAI,CAAC;oBACjB,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,MAAM;iBACjB,CAAC,CAAC;gBACH,WAAW,GAAG,CAAC,CAAC;YACpB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,IAAI,CAAC;oBACD,WAAW,EAAE,CAAC;oBACd,MAAM,CAAC,OAAO,CAAC;wBACX,EAAE,EAAE,KAAK,CAAC,uCAAuC;wBACjD,OAAO,EAAE,yBAAyB;wBAClC,OAAO,EAAE,gCAAgC,WAAW,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;qBAC/G,CAAC,CAAC;oBAEH,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;wBAClB,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;wBACxB,OAAO;oBACX,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACL,0CAA0C;gBAC9C,CAAC;YACL,CAAC;YAED,MAAM,KAAK,CAAC,KAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACpC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACK,KAAK,CAAC,wBAAwB,CAAC,EACnC,GAAG,EACH,SAAS,EAAE,iBAAiB,EAC5B,wBAAwB,GAK3B;QACG,MAAM,SAAS,GAAG,iBAAiB,IAAI,aAAa,EAAE,CAAC;QACvD,MAAM,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAElD,kCAAkC;QAClC,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7D,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,6EAA6E;QAC7E,MAAM,WAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,WAAW,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBACd,EAAE,EAAE,KAAK,CAAC,sCAAsC;gBAChD,OAAO,EAAE,yBAAyB;gBAClC,OAAO,EAAE,mBAAmB,SAAS,wCAAwC;gBAC7E,UAAU,EAAE,EAAE,GAAG,cAAc,EAAE;aACpC,CAAC,CAAC;YACH,IAAI,CAAC;gBACD,MAAM,WAAW,CAAC;YACtB,CAAC;YAAC,MAAM,CAAC;gBACL,wCAAwC;YAC5C,CAAC;YACD,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACd,EAAE,EAAE,KAAK,CAAC,sCAAsC;YAChD,OAAO,EAAE,yBAAyB;YAClC,OAAO,EAAE,mBAAmB,SAAS,sCAAsC;YAC3E,UAAU,EAAE;gBACR,GAAG,cAAc;aACpB;SACJ,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,CAAC,KAAK,IAAmB,EAAE;YAC3C,MAAM,OAAO,GAA4B;gBACrC,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,KAAK,EAAE,GAAG,CAAC,KAAsD;aACpE,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;YAE1D,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;gBAChD,kBAAkB,EAAE,GAAW,EAAE,CAAC,SAAS;gBAC3C,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC,YAAY,KAAK,MAAM;gBAC5D,eAAe,EAAE,KAAK,EAAE,SAAS,EAAiB,EAAE;oBAChD,IAAI,CAAC;wBACD,MAAM,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBACpF,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;4BACd,EAAE,EAAE,KAAK,CAAC,mBAAmB;4BAC7B,OAAO,EAAE,yBAAyB;4BAClC,OAAO,EAAE,yBAAyB,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;yBAC3G,CAAC,CAAC;oBACP,CAAC;gBACL,CAAC;aACJ,CAAC,CAAC;YAEH,mIAAmI;YACnI,qIAAqI;YACrI,mDAAmD;YACnD,IAAI,wBAAwB,EAAE,CAAC;gBAC3B,MAAM,iBAAiB,GAAG,SAAS,CAAC,uBAAuB,CAG1D,CAAC;gBACF,iBAAiB,CAAC,YAAY,GAAG,IAAI,CAAC;gBACtC,iBAAiB,CAAC,SAAS,GAAG,SAAS,CAAC;YAC5C,CAAC;YAED,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE5D,MAAM,mBAAmB,GAAG,IAAI,eAAe,EAAE,CAAC;YAClD,KAAK,IAAI,CAAC,kBAAkB,CAAC;gBACzB,SAAS;gBACT,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,IAAI,CAAC,MAAM;gBAC7C,MAAM,EAAE,mBAAmB,CAAC,MAAM;aACrC,CAAC,CAAC;YACH,SAAS,CAAC,OAAO,GAAG,GAAS,EAAE;gBAC3B,mBAAmB,CAAC,KAAK,EAAE,CAAC;gBAE5B,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;oBACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;wBACd,EAAE,EAAE,KAAK,CAAC,mCAAmC;wBAC7C,OAAO,EAAE,yBAAyB;wBAClC,OAAO,EAAE,yBAAyB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBAC7F,CAAC,CAAC;gBACP,CAAC,CAAC,CAAC;YACP,CAAC,CAAC;YAEF,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAEhC,IAAI,wBAAwB,EAAE,CAAC;gBAC3B,MAAM,IAAI,CAAC,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5E,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,wCAAwC,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAClF,CAAC;YAED,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;gBAC/B,SAAS;gBACT,SAAS;gBACT,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,IAAI,CAAC,MAAM;gBAC7C,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,4DAA4D;gBAC5D,8DAA8D;gBAC9D,0CAA0C;gBAC1C,OAAO,EAAE,GAAG,CAAC,OAAO;aACvB,CAAC,CAAC;QACP,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACxD,IAAI,CAAC;YACD,MAAM,WAAW,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBACd,EAAE,EAAE,KAAK,CAAC,qCAAqC;gBAC/C,OAAO,EAAE,yBAAyB;gBAClC,OAAO,EAAE,gCAAgC,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC/G,UAAU,EAAE,EAAE,GAAG,cAAc,EAAE;aACpC,CAAC,CAAC;YACH,8DAA8D;YAC9D,+DAA+D;YAC/D,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAC3E,CAAC;YAAC,MAAM,CAAC;gBACL,iDAAiD;YACrD,CAAC;YACD,MAAM,KAAK,CAAC;QAChB,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,4BAA4B,CACtC,MAAe,EACf,SAAiB,EACjB,OAAgC;QAEhC,IAAI,KAAwC,CAAC;QAC7C,IAAI,CAAC;YACD,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAClF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,+DAA+D;YAC/D,uCAAuC;YACvC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;gBAChB,EAAE,EAAE,KAAK,CAAC,gDAAgD;gBAC1D,OAAO,EAAE,yBAAyB;gBAClC,OAAO,EAAE,yDAAyD,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBACxI,UAAU,EAAE,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE;aAC5C,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO;QACX,CAAC;QAED,qEAAqE;QACrE,iEAAiE;QACjE,gEAAgE;QAChE,gCAAgC;QAChC,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACnC,MAAM,cAAc,GAAG,SAAS,CAAC,MAGhC,CAAC;QACF,cAAc,CAAC,mBAAmB,GAAG,KAAK,CAAC,kBAAkB,CAAC;QAC9D,cAAc,CAAC,cAAc,GAAG,KAAK,CAAC,UAAU,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;OAMG;IACK,wCAAwC,CAC5C,MAAe,EACf,SAAiB,EACjB,OAAgC;QAEhC,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAC/C,MAAM,qBAAqB,GAAG,cAAc,CAAC,aAAa,CAAC;QAC3D,cAAc,CAAC,aAAa,GAAG,GAAS,EAAE;YACtC,qBAAqB,EAAE,EAAE,CAAC;YAE1B,MAAM,KAAK,GAA0B;gBACjC,kBAAkB,EAAE,cAAc,CAAC,qBAAqB,EAAE;gBAC1D,UAAU,EAAE,cAAc,CAAC,gBAAgB,EAAE;aAChD,CAAC;YACF,IAAI,CAAC,YAAY,CAAC,yBAAyB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBAC5F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;oBAChB,EAAE,EAAE,KAAK,CAAC,6CAA6C;oBACvD,OAAO,EAAE,yBAAyB;oBAClC,OAAO,EAAE,sDAAsD,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBACrI,UAAU,EAAE,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE;iBAC5C,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;IACN,CAAC;IAES,gBAAgB;QACtB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;QAEjF,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;QACzC,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;oBACjD,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;oBAC9C,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;wBAC9B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,6BAA6B,GAAG,GAAG,EAAE,CAAC,CAAC;wBACrE,OAAO;oBACX,CAAC;gBACL,CAAC;gBACD,IAAI,EAAE,CAAC;YACX,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,+FAA+F;IAC5E,KAAK,CAAC,WAAW;QAChC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,MAAM,oBAAoB,GAAG,KAAK,EAAE,GAAoB,EAAE,GAAqB,EAAiB,EAAE;YAC9F,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YAChD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,uCAAuC,CAAC,CAAC;YACjF,CAAC;YAED,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;YAChF,CAAC;YAED,MAAM,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAElD,IAAI,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC3E,IAAI,CAAC,SAAS,EAAE,CAAC;gBACb,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,yBAAyB,EAAE,CAAC;oBACjD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;wBACd,EAAE,EAAE,KAAK,CAAC,sCAAsC;wBAChD,OAAO,EAAE,yBAAyB;wBAClC,OAAO,EAAE,mBAAmB,SAAS,YAAY;wBACjD,UAAU,EAAE,EAAE,GAAG,cAAc,EAAE;qBACpC,CAAC,CAAC;oBACH,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;gBAC/E,CAAC;gBAED,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC;oBAC1D,GAAG;oBACH,SAAS;oBACT,wBAAwB,EAAE,IAAI;iBACjC,CAAC,CAAC;gBACH,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,iBAAiB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC/E,IAAI,CAAC,SAAS,EAAE,CAAC;oBACb,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;gBAC/E,CAAC;YACL,CAAC;YAED,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACtD,CAAC,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,IAAI,CACT,MAAM,EACN,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,GAAoB,EAAE,GAAqB,EAAE,EAAE;YACzE,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YAChD,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAC7C,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;YAChF,CAAC;YAED,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,yBAAyB,EAAE,CAAC;oBAC9D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;wBACd,EAAE,EAAE,KAAK,CAAC,qDAAqD;wBAC/D,OAAO,EAAE,yBAAyB;wBAClC,OAAO,EAAE,8BAA8B,SAAS,6CAA6C;wBAC7F,UAAU,EAAE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC;qBACzC,CAAC,CAAC;oBACH,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,+CAA+C,CAAC,CAAC;gBACzF,CAAC;gBAED,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC;oBAC1D,GAAG;oBACH,SAAS;oBACT,wBAAwB,EAAE,KAAK;iBAClC,CAAC,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,iBAAiB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBACrF,IAAI,CAAC,SAAS,EAAE,CAAC;oBACb,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;gBAC/E,CAAC;gBACD,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClD,OAAO;YACX,CAAC;YAED,IAAI,SAAS,EAAE,CAAC;gBACZ,OAAO,MAAM,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAChD,CAAC;YAED,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,mCAAmC,CAAC,CAAC;QAC7E,CAAC,CAAC,CACL,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,GAAG,CACR,MAAM,EACN,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAiB,EAAE;YACrD,IAAI,IAAI,CAAC,WAAW,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;gBAC1C,MAAM,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACzC,CAAC;iBAAM,CAAC;gBACJ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAChF,CAAC;QACL,CAAC,CAAC,CACL,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC1E,CAAC;IAEO,iBAAiB,CACrB,EAA8F;QAE9F,OAAO,CAAC,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAQ,EAAE;YACrF,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC/B,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;oBACd,EAAE,EAAE,KAAK,CAAC,qCAAqC;oBAC/C,OAAO,EAAE,yBAAyB;oBAClC,OAAO,EAAE,2BAA2B,YAAY,EAAE;oBAClD,UAAU,EAAE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC;iBACzC,CAAC,CAAC;gBAEH,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;oBACxC,0DAA0D;oBAC1D,yDAAyD;oBACzD,qDAAqD;oBACrD,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;gBAC/E,CAAC;gBAED,IAAI,KAAK,YAAY,yBAAyB,EAAE,CAAC;oBAC7C,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,0CAA0C,CAAC,CAAC;gBACpF,CAAC;gBAED,uDAAuD;gBACvD,MAAM,OAAO,GAAG,KAAK,YAAY,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;gBAE9F,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACjB,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE;wBACH,IAAI,EAAE,6CAA6C;wBACnD,OAAO;qBACV;iBACJ,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;IACN,CAAC;CACJ"}
@@ -0,0 +1,53 @@
1
+ import type { DefaultMetricDefinitions, ILogger, IMetrics } from "@mongodb-js/mcp-types";
2
+ import { ExpressBasedHttpServer } from "./expressBasedHttpServer.js";
3
+ /**
4
+ * HTTP server that provides monitoring endpoints like health checks and metrics.
5
+ *
6
+ * To customize behavior, extend this class and override methods:
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * class MyMonitoringServer extends MonitoringServer {
11
+ * protected override async setupRoutes(): Promise<void> {
12
+ * // Add custom routes
13
+ * this.app.get("/custom", (req, res) => res.json({ custom: true }));
14
+ * await super.setupRoutes();
15
+ * }
16
+ * }
17
+ * ```
18
+ */
19
+ export declare class MonitoringServer<TMetrics extends DefaultMetricDefinitions = DefaultMetricDefinitions> extends ExpressBasedHttpServer {
20
+ protected readonly features: MonitoringServerFeature[];
21
+ protected readonly metrics: IMetrics<TMetrics>;
22
+ protected readonly version: string;
23
+ constructor({ options, logger, metrics }: MonitoringServerOptions<TMetrics>);
24
+ protected setupRoutes(): Promise<void>;
25
+ }
26
+ /**
27
+ * Complete constructor params for creating a MonitoringServer instance.
28
+ */
29
+ export type MonitoringServerOptions<TMetrics extends DefaultMetricDefinitions = DefaultMetricDefinitions> = {
30
+ /** Options for configuring the monitoring server */
31
+ options: {
32
+ /** HTTP configuration */
33
+ http: {
34
+ /** Host to bind the monitoring server to */
35
+ host: string;
36
+ /** Port to bind the monitoring server to */
37
+ port: number;
38
+ };
39
+ /** Features to enable on the monitoring server */
40
+ features: MonitoringServerFeature[];
41
+ /**
42
+ * Version to expose in the health endpoint.
43
+ * Defaults to the package version.
44
+ */
45
+ version?: string;
46
+ };
47
+ /** Logger for the server */
48
+ logger: ILogger;
49
+ /** Metrics instance */
50
+ metrics: IMetrics<TMetrics>;
51
+ };
52
+ export type MonitoringServerFeature = "health-check" | "metrics";
53
+ //# sourceMappingURL=monitoringServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoringServer.d.ts","sourceRoot":"","sources":["../src/monitoringServer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAgB,CACzB,QAAQ,SAAS,wBAAwB,GAAG,wBAAwB,CACtE,SAAQ,sBAAsB;IAC5B,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACvD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEvB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,uBAAuB,CAAC,QAAQ,CAAC;cAaxD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAkClD;AAED;;GAEG;AACH,MAAM,MAAM,uBAAuB,CAAC,QAAQ,SAAS,wBAAwB,GAAG,wBAAwB,IAAI;IACxG,oDAAoD;IACpD,OAAO,EAAE;QACL,yBAAyB;QACzB,IAAI,EAAE;YACF,4CAA4C;YAC5C,IAAI,EAAE,MAAM,CAAC;YACb,4CAA4C;YAC5C,IAAI,EAAE,MAAM,CAAC;SAChB,CAAC;QACF,kDAAkD;QAClD,QAAQ,EAAE,uBAAuB,EAAE,CAAC;QACpC;;;WAGG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,4BAA4B;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,uBAAuB;IACvB,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,cAAc,GAAG,SAAS,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { LogId } from "@mongodb-js/mcp-core";
2
+ import { ExpressBasedHttpServer } from "./expressBasedHttpServer.js";
3
+ import packageJson from "../package.json" with { type: "json" };
4
+ /**
5
+ * HTTP server that provides monitoring endpoints like health checks and metrics.
6
+ *
7
+ * To customize behavior, extend this class and override methods:
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * class MyMonitoringServer extends MonitoringServer {
12
+ * protected override async setupRoutes(): Promise<void> {
13
+ * // Add custom routes
14
+ * this.app.get("/custom", (req, res) => res.json({ custom: true }));
15
+ * await super.setupRoutes();
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export class MonitoringServer extends ExpressBasedHttpServer {
21
+ constructor({ options, logger, metrics }) {
22
+ super({
23
+ options: {
24
+ logContext: "monitoringServer",
25
+ http: { port: options.http.port, host: options.http.host },
26
+ },
27
+ logger,
28
+ });
29
+ this.features = options.features;
30
+ this.metrics = metrics;
31
+ this.version = options.version ?? packageJson.version;
32
+ }
33
+ setupRoutes() {
34
+ if (this.features.includes("health-check")) {
35
+ this.app.get("/health", (_req, res) => {
36
+ // Health responses should never be cached by proxies or load balancers.
37
+ res.set("Cache-Control", "no-store");
38
+ res.json({
39
+ status: "ok",
40
+ version: this.version,
41
+ uptimeSeconds: Math.floor(process.uptime()),
42
+ timestamp: new Date().toISOString(),
43
+ });
44
+ });
45
+ }
46
+ if (this.features.includes("metrics") && this.metrics?.getMetrics) {
47
+ const getMetrics = this.metrics.getMetrics.bind(this.metrics);
48
+ this.app.get("/metrics", async (_req, res) => {
49
+ try {
50
+ const output = await getMetrics();
51
+ res.set("Content-Type", "text/plain");
52
+ res.send(output);
53
+ }
54
+ catch (error) {
55
+ this.logger.error({
56
+ id: LogId.monitoringServerMetricsFailure,
57
+ context: "monitoringServer",
58
+ message: `Failed to retrieve metrics: ${String(error)}`,
59
+ });
60
+ res.status(500).json({ error: "Failed to retrieve metrics" });
61
+ }
62
+ });
63
+ }
64
+ return Promise.resolve();
65
+ }
66
+ }
67
+ //# sourceMappingURL=monitoringServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoringServer.js","sourceRoot":"","sources":["../src/monitoringServer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEhE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,gBAEX,SAAQ,sBAAsB;IAK5B,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAqC;QACvE,KAAK,CAAC;YACF,OAAO,EAAE;gBACL,UAAU,EAAE,kBAAkB;gBAC9B,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE;aAC7D;YACD,MAAM;SACT,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC;IAC1D,CAAC;IAEkB,WAAW;QAC1B,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAqB,EAAE,GAAqB,EAAE,EAAE;gBACrE,wEAAwE;gBACxE,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;gBACrC,GAAG,CAAC,IAAI,CAAC;oBACL,MAAM,EAAE,IAAI;oBACZ,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAC3C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACtC,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;QACP,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;YAChE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC9D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,IAAqB,EAAE,GAAqB,EAAE,EAAE;gBAC5E,IAAI,CAAC;oBACD,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;oBAClC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;oBACtC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;wBACd,EAAE,EAAE,KAAK,CAAC,8BAA8B;wBACxC,OAAO,EAAE,kBAAkB;wBAC3B,OAAO,EAAE,+BAA+B,MAAM,CAAC,KAAK,CAAC,EAAE;qBAC1D,CAAC,CAAC;oBACH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;gBAClE,CAAC;YACL,CAAC,CAAC,CAAC;QACP,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;CACJ"}
@@ -0,0 +1,36 @@
1
+ import type { DefaultMetricDefinitions, ITransportRunner } from "@mongodb-js/mcp-types";
2
+ import type { CompositeLogger } from "@mongodb-js/mcp-core";
3
+ import type { MCPHttpServer } from "./mcpHttpServer.js";
4
+ import type { SessionServer } from "@mongodb-js/mcp-types";
5
+ import type { MonitoringServer } from "./monitoringServer.js";
6
+ /**
7
+ * Options for StreamableHttpRunner.
8
+ */
9
+ export type StreamableHttpRunnerOptions<TServer extends SessionServer = SessionServer, TMetrics extends DefaultMetricDefinitions = DefaultMetricDefinitions> = {
10
+ /** Logger instance */
11
+ logger: CompositeLogger;
12
+ mcpHttpServer: MCPHttpServer<TServer, TMetrics>;
13
+ monitoringServer?: MonitoringServer<TMetrics>;
14
+ };
15
+ /**
16
+ * Transport runner for HTTP transport with streamable responses.
17
+ * Supports both SSE and JSON response types.
18
+ *
19
+ * Server creation is handled by the `MCPHttpServer` instance passed to the constructor.
20
+ * To customize per-request server creation, extend `MCPHttpServer` and override
21
+ * the `createServerForRequest()` method.
22
+ */
23
+ export declare class StreamableHttpRunner<TServer extends SessionServer = SessionServer, TMetrics extends DefaultMetricDefinitions = DefaultMetricDefinitions> implements ITransportRunner {
24
+ protected readonly mcpHttpServer: MCPHttpServer<TServer, TMetrics>;
25
+ protected readonly monitoringServer: MonitoringServer<TMetrics> | undefined;
26
+ protected readonly logger: CompositeLogger;
27
+ constructor({ logger, mcpHttpServer, monitoringServer }: StreamableHttpRunnerOptions<TServer, TMetrics>);
28
+ /** Starts the transport runner. */
29
+ start(): Promise<void>;
30
+ /**
31
+ * Stops the HTTP transport runner.
32
+ * This stops the MCP HTTP server and monitoring server.
33
+ */
34
+ close(): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=streamableHttpRunner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamableHttpRunner.d.ts","sourceRoot":"","sources":["../src/streamableHttpRunner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,2BAA2B,CACnC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC7C,QAAQ,SAAS,wBAAwB,GAAG,wBAAwB,IACpE;IACA,sBAAsB;IACtB,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAChD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;CACjD,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,oBAAoB,CAC7B,OAAO,SAAS,aAAa,GAAG,aAAa,EAC7C,QAAQ,SAAS,wBAAwB,GAAG,wBAAwB,CACtE,YAAW,gBAAgB;IACzB,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACnE,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;IAC5E,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;gBAE/B,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,EAAE,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC;IAMvG,mCAAmC;IAC7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
@@ -0,0 +1,35 @@
1
+ import { LogId } from "@mongodb-js/mcp-core";
2
+ /**
3
+ * Transport runner for HTTP transport with streamable responses.
4
+ * Supports both SSE and JSON response types.
5
+ *
6
+ * Server creation is handled by the `MCPHttpServer` instance passed to the constructor.
7
+ * To customize per-request server creation, extend `MCPHttpServer` and override
8
+ * the `createServerForRequest()` method.
9
+ */
10
+ export class StreamableHttpRunner {
11
+ constructor({ logger, mcpHttpServer, monitoringServer }) {
12
+ this.logger = logger;
13
+ this.mcpHttpServer = mcpHttpServer;
14
+ this.monitoringServer = monitoringServer;
15
+ }
16
+ /** Starts the transport runner. */
17
+ async start() {
18
+ await this.mcpHttpServer.start();
19
+ // Start the monitoring server if one exists
20
+ await this.monitoringServer?.start();
21
+ this.logger.info({
22
+ message: "Streamable HTTP Transport started",
23
+ context: "streamableHttpTransport",
24
+ id: LogId.streamableHttpTransportStarted,
25
+ });
26
+ }
27
+ /**
28
+ * Stops the HTTP transport runner.
29
+ * This stops the MCP HTTP server and monitoring server.
30
+ */
31
+ async close() {
32
+ await Promise.allSettled([this.mcpHttpServer.stop(), this.monitoringServer?.stop()]);
33
+ }
34
+ }
35
+ //# sourceMappingURL=streamableHttpRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamableHttpRunner.js","sourceRoot":"","sources":["../src/streamableHttpRunner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAkB7C;;;;;;;GAOG;AACH,MAAM,OAAO,oBAAoB;IAQ7B,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAkD;QACnG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC7C,CAAC;IAED,mCAAmC;IACnC,KAAK,CAAC,KAAK;QACP,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAEjC,4CAA4C;QAC5C,MAAM,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAC;QAErC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACb,OAAO,EAAE,mCAAmC;YAC5C,OAAO,EAAE,yBAAyB;YAClC,EAAE,EAAE,KAAK,CAAC,8BAA8B;SAC3C,CAAC,CAAC;IACP,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACP,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzF,CAAC;CACJ"}
@@ -0,0 +1,4 @@
1
+ export declare function sleep(ms: number, { signal }: {
2
+ signal: AbortSignal;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB1F"}
package/dist/utils.js ADDED
@@ -0,0 +1,17 @@
1
+ export async function sleep(ms, { signal }) {
2
+ return new Promise((resolve) => {
3
+ let listener = undefined;
4
+ const timeout = setTimeout(() => {
5
+ if (listener) {
6
+ signal.removeEventListener("abort", listener);
7
+ }
8
+ resolve();
9
+ }, ms);
10
+ listener = () => {
11
+ clearTimeout(timeout);
12
+ resolve();
13
+ };
14
+ signal.addEventListener("abort", listener, { once: true });
15
+ });
16
+ }
17
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,EAAU,EAAE,EAAE,MAAM,EAA2B;IACvE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,IAAI,QAAQ,GAA6B,SAAS,CAAC;QACnD,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,QAAQ,EAAE,CAAC;gBACX,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAClD,CAAC;YACD,OAAO,EAAE,CAAC;QACd,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,QAAQ,GAAG,GAAS,EAAE;YAClB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,EAAE,CAAC;QACd,CAAC,CAAC;QAEF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACP,CAAC"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@mongodb-js/mcp-http-runners",
3
+ "description": "HTTP transport runners and servers for MongoDB MCP servers",
4
+ "version": "3.0.0-prerelease.1",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "author": "MongoDB <info@mongodb.com>",
8
+ "repository": {
9
+ "url": "https://github.com/mongodb-js/mongodb-mcp-server.git",
10
+ "directory": "packages/http-runners"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc --build tsconfig.build.json",
25
+ "test": "vitest --run",
26
+ "check": "tsc --build tsconfig.build.json --noEmit"
27
+ },
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.29.0",
30
+ "@mongodb-js/mcp-core": "workspace:*",
31
+ "express": "^5.2.1"
32
+ },
33
+ "devDependencies": {
34
+ "@mongodb-js/mcp-types": "workspace:*",
35
+ "@mongodb-js/mcp-metrics": "workspace:*",
36
+ "@types/express": "^5.0.3",
37
+ "@types/node": "^25.6.0",
38
+ "typescript": "^5.9.2",
39
+ "vitest": "^4.1.4"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }