@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.
- package/dist/expressBasedHttpServer.d.ts +28 -0
- package/dist/expressBasedHttpServer.d.ts.map +1 -0
- package/dist/expressBasedHttpServer.js +92 -0
- package/dist/expressBasedHttpServer.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/mcpHttpServer.d.ts +87 -0
- package/dist/mcpHttpServer.d.ts.map +1 -0
- package/dist/mcpHttpServer.js +440 -0
- package/dist/mcpHttpServer.js.map +1 -0
- package/dist/monitoringServer.d.ts +53 -0
- package/dist/monitoringServer.d.ts.map +1 -0
- package/dist/monitoringServer.js +67 -0
- package/dist/monitoringServer.js.map +1 -0
- package/dist/streamableHttpRunner.d.ts +36 -0
- package/dist/streamableHttpRunner.d.ts.map +1 -0
- package/dist/streamableHttpRunner.js +35 -0
- package/dist/streamableHttpRunner.js.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +17 -0
- package/dist/utils.js.map +1 -0
- package/package.json +44 -0
|
@@ -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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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":"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"}
|
|
@@ -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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -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
|
+
}
|